--- title: "ADR 0027 — Verification is tracking, not a gate" type: decision tags: [signpost, adr, decision] proposed_by: tok_bc09b340db294c709763789154d8113d model: kmem-docs-publisher extracted_at: "2026-10-08T22:42:41.907Z" --- # ADR 0027 — Verification is tracking, not a gate **Status** Accepted · **Date** 2026-09-17, decision 7 changed 2026-10-07 · **Supersedes** nothing **Amends** - ADR 0016 control 3: this ADR removes the retrieval quarantine. - ADR 0017's consequence about `recall`. - ADR 0018: the `on` mode holds no text back, and the setting reports no held-back count. The status panel offers the same four states in both modes. An org with verification off draws no claim bar, so the reader opens no status panel there (decision 7). **Relates to** ADR 0022 (agent writes), `docs/RETRIEVAL.md`, `docs/SECURITY.md`, `docs/MCP.md`, `docs/ASK.md`, `packages/core/src/retrieval/types.ts`, `packages/core/src/retrieval/pipeline.ts`, `packages/core/src/lifecycle/acceptance.ts` ## Context With verification on, kmem held an assistant's text out of every answer until a person accepted the doc or verified the claim. This was the retrieval quarantine: ADR 0016 control 3, which ADR 0018 made an org choice. Settings > Org > Verification said the same thing: "Choose whether a person checks what assistants write before it appears in answers." The owner rejected that sentence and the behaviour on 2026-09-17. The owner's words, verbatim: > this just seems plain wrong - this is not the main purpose of verification - it is to enable > tracking of what facts are written by humans and which by ai and what is their current state - > whether someone has read and found it stale or superseded or verify it if not verified so that > the answer engine can use that to determine what to give value to. whatever is in the propose > queue is never shown in answers/fetches, but once its accepted it will be shown in answers even > though it is not verified, unlike what the section here says so please fix that behaviour to > match what I said here The quarantine also had a measured cost. On 2026-09-03 a person accepted eleven of the thirty-six notes an agent had written. The quarantine served the accepted half and held the rest. A person then asked ten questions. kmem answered all ten, and eight answers cited the wrong note. The product then grew five mechanisms, and each one existed only because of the quarantine: - a gap sentence about withheld notes; - a count of withheld notes inside a conflict; - a second `recall` in the in-app assistant, to count held-back claims; - a second candidate set and a score floor in the verify queue; - a confirmation dialog with a count in Settings. ## Decision **Verification tracks. It does not gate.** 1. **Every text that is in a doc is answerable, in both modes.** `recall`, Ask, MCP and Quick Connect return an assistant's unverified text and an accepted text. No trust rung and no author kind removes a candidate from a pack. The pipeline removes a candidate for three reasons only: the caller's `types`, the caller's `since`, and `superseded` content. 2. **With verification on, the state decides the rank.** Step 7 of the pipeline multiplies each claim by its rung weight and by its author-kind weight. The rung weights are verified 1, accepted 0.95, unverified 0.85, stale 0.5 and superseded 0.2. The author weights are person 1 and assistant 0.9. So verified text ranks above accepted text, which ranks above unverified text. The app shows who wrote each fact and its state. 3. **With verification off, nothing changes from ADR 0018.** Every rung and author weight is 1, and the app shows no fact states. Stale and superseded handling stays in both modes. 4. **A change that waits in Review never appears in an answer.** A proposal is not doc text. Its Markdown is in a `proposals` row, and kmem writes no chunk for it until a person accepts it. No read door returns it: not `recall`, `read`, `browse`, the briefing, Ask, search, the tree or the public docs. `apps/worker/test/mcp/proposals-never-answer.test.ts` proves it for `recall`, the MCP `read`, `browse` and Ask, on every call. 5. **`include_unverified` stays on the wire and does nothing.** The MCP tool, `POST /api/recall` and Quick Connect still accept it, so kmem does not refuse an older client. Its description says that kmem ignores the field, that recall can answer from any doc text, and that unverified and accepted text ranks lower when the org uses verification. One constant, `INCLUDE_UNVERIFIED_DESCRIPTION` in `@signpost/core/retrieval`, holds that text for every door. `RetrievalQuery` has no such field. 6. **The setting stays.** `org_routing.verification` keeps its storage, its default (`off` for a new org) and its reading rules (a missing or unknown value reads as `on`). `GET` and `PATCH /api/orgs/{orgId}/verification` no longer answer `held_back_when_on`, because kmem holds nothing back. A change applies at once, with no confirmation. It moves ranks and marks, and it removes no doc from an answer. 7. **The setting decides what the reader draws and how a state changes the rank.** The server records a state in both modes. `POST /api/verify` and the agent's `verify` tool refuse no change because of the setting. Stale and Superseded keep their effect on the rank in both modes (decision 3). - With verification on, the claim status panel opens from every claim that has a record, an observation line included. It offers Verified, Stale, Superseded and Not verified, and it shows the claim's state and the Accepted and Verified facts. Until 2026-09-17 the panel offered Stale, Superseded and "No mark" in an org with verification off. So nobody could mark an agent's claim Verified there (`apps/web/src/screens/note/status/transitions.ts`). - With verification off, the reader draws no claim bar: not at rest, not on hover and not on focus. This applies to a block row, an observation line and the gutter of the review preview, for every state, Stale and Superseded included. So the reader has no door to the status panel in that org, and nobody sets a state from a doc there. The doc's History still names the author of each version. `markDoorFor` in `apps/web/src/editor/NoteBody.tsx` holds the rule. - With verification off, no surface draws a state at all: not the doc line, a list row, the recall glyph or a block's sentence. A Stale or Superseded mark from the time the org verified stays stored, still changes the rank, and shows again when the org turns verification back on. `displayStatusFor` in `packages/core/src/provenance/status.ts` holds the rule, and every surface calls it. From 2026-09-17 to 2026-10-07, in an org with verification off, the reader hid the bar at rest. It showed the bar on hover or focus, as the door to the panel. Every surface still drew a stored Stale or Superseded. The owner changed both on 2026-10-07: "i setupo an org velniq and kept verification off but I am still seeing the verification options on the side of the pages", and "make sure that if an org had verification on, and marked something stale etc. then when they turn verification off the labels also go away". An org that turns verification off has decided not to track claims, so the doc offers no control and no label that tracks them. The panel keeps the line under each state for an org with verification off, and the reader gives that line no door today. **What this ADR removes.** - `unreviewedStates` and `unreviewedAuthorKinds` on `RetrievalConfig`, and the two matching fields on `RetrievalFilter`. - `includeUnverified` on `RetrievalQuery`. - The `withheld` pile in the pipeline and in `ConflictInput`. - The `withheld_unreviewed` gap reason and its sentence. - The post-query author-kind filter in `apps/worker/src/retrieval/dense.ts`, and its over-fetch. - The held-back claim count and candidate set in `apps/worker/src/services/usage.ts`, and `QUARANTINE_FLOOR` in `packages/core/src/lifecycle/staleness.ts`. - The second `recall` of the in-app assistant. - The Ask `withheld` gap reason and its "waiting to be checked" notice. - The Settings confirmation and its count. **What stays. These are now the whole control on a direct agent write.** - The org's **agent writes** setting (ADR 0022), off by default. With it off, every agent change waits in Review. - The **connection mode**. A connection made to read, or to read and propose, writes nothing directly. - The **session write budget**. After 25 applied writes, every write of that connection waits in Review. - **History and undo**. The doc history keeps every version, and a person can undo the writes of a connection. - **The marks**. With verification on, a person sees which facts an agent wrote and which facts nobody checked, and answers rank those facts lower. An assistant still may not mark its own text `verified` or `accepted` (`assertVerificationPermitted`, `refuseAssistantStateChange`). - **Review for a change to an existing doc** (ADR 0017), in both modes. ## Consequences - The text of an agent that writes directly reaches answers at once. An org that wants a person to see agent text first turns agent writes off (ADR 0022). Then every agent change is a proposal, and rule 4 keeps it out of every answer. - A partly reviewed corpus answers from all of its text. The conflict detector sees both halves of a disagreement and names both, and the unreviewed half ranks lower. - The verify queue lists a doc by its traffic and its age, whoever wrote it. It does not list an unchecked agent doc that nobody asks about. - The eval gate does not move, because people wrote the whole eval corpus. On 2026-09-17 `node eval/runner.mjs --gate` gave `precision@1` 0.917, with and without `--verification=off`. - The Vectorize metadata index on `author_kind` stays, and no query reads it now. A removal needs a reindex of every vector, and a spare index costs nothing. ## The alternatives that were rejected **Keep the quarantine for `on`, and fix only the Settings sentence.** The owner's words rule it out. The purpose of verification is tracking and ranking, and accepted text answers although nobody verified it. The sentence was not the defect. The behaviour it described was. **Keep `include_unverified: false` as a way to ask for checked text only.** A filter to verified text is a different feature from the quarantine. The quarantine dropped only unreviewed *agent* text, so the flag never meant "checked text only". No client uses the flag that way. A filter that nobody asked for is a second rule to keep in step with the rank. A future "verified only" filter needs its own name and its own decision.