--- title: "ADR 0018 — Verification is an org's choice, and a new org starts without it" type: decision tags: [signpost, adr, decision] proposed_by: tok_bc09b340db294c709763789154d8113d model: kmem-docs-publisher extracted_at: "2026-10-08T22:40:47.271Z" --- # ADR 0018 — Verification is an org's choice, and a new org starts without it **Status** Accepted; amended by [ADR 0027](./0027-verification-is-tracking.md), which removes the retrieval quarantine from the `on` mode and the held-back count from Settings · **Date** 2026-09-16 · **Supersedes** nothing · **Amends** ADR 0016 control 3 (the retrieval quarantine), ADR 0017's consequence about `recall` · **Relates to** ADR 0008 (the Quick Connect briefing cache), ADR 0009 (the permission ladder), `docs/TAXONOMY.md` §7, `docs/SECURITY.md`, `docs/RETRIEVAL.md`, `packages/core/src/provenance/verification-mode.ts`, `packages/core/src/provenance/status.ts`, `packages/core/src/retrieval/types.ts` > **Amended 2026-09-17 by [ADR 0027](./0027-verification-is-tracking.md).** Verification tracks and > ranks. It holds no text back in either mode. The setting, its default, its storage, the flat > weights of the `off` mode and the rules for the marks stand as this ADR states them. Every > statement below about text held out of answers is historical. That includes `unreviewedStates`, > `unreviewedAuthorKinds`, the withheld gap, the held-back count in Settings and the meaning of > `include_unverified`. Each such passage carries the mark *(historical, ADR 0027)*. ## Context kmem had one verification process, and it applied to every org: - `recall`, Quick Connect and Ask held an assistant's direct write out of answers until a person accepted the doc or verified the claim. This is ADR 0016 control 3. - Ranking multiplied each claim by a weight for its trust rung (verified 1, accepted 0.95, unverified 0.85) and by a weight for its author kind (person 1, assistant 0.9). - Every doc surface drew Unverified, Accepted and Verified marks. - An agent that got a withheld result read "Verify it on the trust screen", and the `trust` launch flag hides that screen. The owner asked for a different default on 2026-09-16: an org decides whether it uses verification, and a team that has not asked for the ritual does not get it. The owner's words for the off state are a quotation: "All KB facts are treated equally". Nothing in the product let an org make that decision. The `trust` launch flag is a build-time constant for the whole app, and it hides one report page; it is not a setting and it does not change retrieval. A second fact made the change necessary. The write path now stores a person's own write as `verified` by that person (B-HUMAN-VERIFIED). In an org that verifies nothing, the rung of a claim is then an accident of the door that wrote the row. A ranking by rung is a ranking by that accident. ## Decision **Each org has a setting, `org_routing.verification`, with two values, `on` and `off`.** With `off`: 1. **Retrieval holds nothing back.** *(Historical, ADR 0027: no mode holds text back, and the two fields are gone.)* The org's retrieval config has empty `unreviewedStates` and `unreviewedAuthorKinds`, so an assistant's direct write answers at once. That is the list shape `include_unverified: true` already produced per request, so the pipeline gains no new path. 2. **Every fact weighs the same.** The `verified`, `accepted` and `unverified` weights are 1, and both author-kind weights are 1. 3. **No surface draws a trust mark.** *(Historical, 2026-09-17: the status panel is now the exception, see ADR 0027. When a person opens it, it shows the claim's state and the Accepted and Verified facts in both modes. At rest, no surface draws a trust mark. Since 2026-10-07 the reader draws no claim bar at all in an `off` org, so it opens no status panel there: ADR 0027 decision 7.)* `displayProvenance` answers `status: null` for a Verified, Accepted or Unverified doc and names no verifier and no accepter. The web draws no such mark on a block, an observation line, the documentation site, the reader's strip, the status panel, the properties panel or the `/recall` screen (`blockStatusFor`). A block's sentence (`waymark.description`) says nothing about a check. 4. **Stale and superseded content keep their handling.** *(Historical, 2026-09-17: the panel no longer offers "No mark". It offers Verified, Stale, Superseded and Not verified in both modes, see ADR 0027. Historical, 2026-10-07: no surface of an `off` org draws a Stale or Superseded chip, and its gutter draws no author glyph. A person sets no state from the doc there. The weights below stay. ADR 0027 decision 7.)* Their weights stay, `superseded` stays excluded from default answers, and both chips still draw. Each is a ruling about age or replacement, not about trust. A person still marks a claim stale or superseded from the doc: a block with no trust mark draws its author glyph, which opens the status panel. The panel then offers Stale, Superseded and "No mark". "No mark" stores `unverified`, which ranks like every other claim in that org. 5. **Agents still see the `verify` tool and `include_unverified`.** *(Historical for `include_unverified`, ADR 0027: kmem ignores the field in every org, and its description says so.)* Their descriptions say what changes when the org does not use verification. The tool list and the briefing are the same for every org, so the Quick Connect briefing cache (ADR 0008) needs no new key. With `on`, everything stays as it shipped, including the three things above that `off` removes. *(Historical, ADR 0027: `on` no longer holds text back.)* **Review for an agent's change to an existing doc stays in both modes.** ADR 0017's queue prevents the silent loss of a doc a person wrote. That is a data-loss control, not a trust feature, and this setting does not touch it. **The values.** - A new org gets `off`. `orgRoutingInsertStatement` writes it, not a column default. The operator script `scripts/provision-tenant.mjs` writes the same value from its own copy of the constant. `apps/worker/test/invariants/org-routing-verification.test.ts` refuses an `org_routing` insert in either place that does not name the column. - Migration `0022_org_verification.sql` wrote `off` for every org that existed, because the owner asked for that default. - A NULL value, a value that is not a mode, and a missing row read as `on`, the stricter mode. Every self-hosted deployment has no row and reads `on`. The rule is ADR 0016's: a value nobody chose is never the permissive one. **Who changes it.** An owner or an admin of the org, in Settings > Org > Verification, through `PATCH /api/orgs/{orgId}/verification`. Every member reads it at `GET` on the same address. The route logs a change as `org_verification_changed` at `warn`, with the person's id and both values. It logs a refused change as `org_verification_refused`. **Turning it on asks first, and accepts nothing.** *(Historical, ADR 0027: the change applies at once, with no count, because no text leaves answers.)* Settings shows a count: the docs the reader may open that hold assistant text nobody accepted or verified. That text leaves answers the moment the mode changes. The switch does not accept those docs. An acceptance is a person's decision about a doc, and a bulk acceptance is the ritual an org can now leave out. The route counts under the reader's own read scope (invariant I7). **Where the rules live.** One place each (`AGENTS.md` rule 5): - `verificationModeFromStored` reads a stored value. `packages/core/src/provenance/verification-mode.ts`. - `retrievalConfigFor(mode)` turns a mode into a config. `packages/core/src/retrieval/types.ts`. - `verificationModeOf(ctx)` resolves a request's mode from the same per-request memo as the capability chain. `apps/worker/src/services/permissions.ts`. It is not on the tenant memo, which lives for 30 s across requests. - `orgRetrievalConfig(ctx)` is the one call every `recall` door and the trust report make. Ask resolves the same mode and calls `retrievalConfigFor`. *(The usage queue made the call until ADR 0027 removed its held-back count.)* `apps/worker/test/invariants/org-retrieval-config.test.ts` refuses a door that passes `DEFAULT_RETRIEVAL_CONFIG` to the pipeline, or that passes a mode written as a literal. - `displayStatusFor` in `packages/core/src/provenance/status.ts` decides a mark. `displayProvenance` calls it for a doc, Ask's source chip for a claim, and `blockStatusFor` in the web app for a block. - `GET /api/notes/{permalink}` and `POST /api/recall` carry the mode as a top-level `verification` field, so the web draws in the org's mode. The MCP `recall` pack does not carry it. **The mode changes reads only.** A change of mode writes one `org_routing` column. It rewrites no provenance row, and it accepts or verifies nothing. ## The alternative that was rejected **Back-fill existing orgs to `on`, and let an owner turn verification off.** `docs/TAXONOMY.md` §7 says a permission migration never widens access, and migration `0011` followed that rule. The reconciliation recommended the same here. This ADR rejects it for two reasons. First, the setting is not a permission. It changes no read scope: a sealed or private note stays absent for every reader in both modes. No row outside a person's scope becomes readable. The setting changes which of the docs a reader may already open take part in an answer *(historical, ADR 0027: it changes only their rank)*. Second, the owner decided the default for the orgs that exist and for new ones. A back-fill to `on` keeps the ritual the owner asked to remove on every org that exists today. Two smaller alternatives, and why not: - **Hide the `verify` tool and `include_unverified` from an `off` org.** A tool list per org needs the mode in the MCP session. A briefing per org needs the mode in the ADR 0008 cache key. A sentence that states both cases costs less and cannot go stale. - **Keep the ranking weights and remove only the quarantine.** Then a person's newer text, which the write path stores as `verified`, outranks the same person's older text. The owner's rule of equal facts is then false. ## Consequences - **ADR 0016 control 3 applies only to an org that uses verification.** *(Historical, ADR 0027: the control applies to no org, and ADR 0027 lists what contains a direct write now.)* In an `off` org, a leaked write credential can create a doc that other agents' `recall` returns at once. That is a knowledge-poisoning path. These controls remain: - the ladder (nothing above `open`); - the link's own scope; - the session write budget of 25 applied writes; - Review for any change to an existing doc (ADR 0017); - undo by session, and revocation; - the provenance row that names the link. `docs/SECURITY.md` records the weakness. - **Turning verification on can look like data loss.** *(Historical, ADR 0027: no doc leaves answers.)* Every doc with unchecked assistant text leaves answers at once. The count in Settings and the sentence "Nothing is deleted" (a quotation of the dialog) tell the person before they act. Turning it off puts the docs back at once. - **The withheld gap no longer names the trust screen.** *(Historical, ADR 0027: the withheld gap is gone.)* It says "Ask a person to open it in the web app and verify it", which is a control every doc has. An `off` org never reaches that sentence. - **The in-process eval runs twice.** `node eval/runner.mjs --gate` scores the default config, and `node eval/runner.mjs --gate --verification=off` scores `retrievalConfigFor("off")`. Both configs ship, so both runs can pass the gate. Every eval chunk is a human claim at `unverified`, so the flat weights change no ranking there. On 2026-09-16 both runs scored `precision@1` 0.917 against the 0.85 gate. - **One surface still draws without the mode.** The trust screen draws glyphs from `waymarkStateOf`. It is the verification queue itself, and the `trust` launch flag hides it. ## What would make me revisit this - **A poisoned answer in an `off` org.** An agent's direct write can reach another agent's answer, and a person can act on it before anybody notices. If that happens, the default is wrong for that class of org. The setting then needs a value per space, not only a value per org. - **Orgs that turn verification on and then off again within a day.** That is the count failing to prepare them for what leaves answers. The measure is the pair of `org_verification_changed` lines.