<!-- Exploration-leg final report, read-only sweep of ~/lane-worktrees/hands-flagship pinned at trunk f669b097f, 2026-08-14, engine: sonnet (Explore). Banked verbatim as census receipt. -->

I now have the full picture at pin `f669b097f`. Here is the authoritative census.

## 1. VERB TABLE — every teammate-reachable edit verb

All are dispatched from the single switch in `scripts/edit-cli.ts:4191-4321`. Seven of the sixteen `edit:*` verbs (`fact`, `preview`, `promote`, `rollback`, `confirm-world-consequence`, `headline`, `gate-plan`) have **no dedicated `scripts/il-edit/ops/*.ts` file** — they are implemented directly inside `scripts/edit-cli.ts` itself.

| # | Verb | Implementer (file:line) | Inputs | `--dry-run`? | Undo path stated in code |
|---|------|--------------------------|--------|:---:|---------------------------|
| 1 | `edit:fact` (pkg.json:11) | `cmdFactEdit`/`runFactEditCommand` — **edit-cli.ts:1383, 2352** (no ops file — "the ONE generic fact-edit door", edit-cli.ts:4192) | `<factId> <newValue> [--dry-run] [--sweep]` (edit-cli.ts:1392-1400) | **Yes** (edit-cli.ts:1392) | None in-file; cross-session undo is generic `scripts/edit-undo.ts`, but only for value changes, never creations |
| 2 | `edit:bind` (pkg.json:12) | `runBindCommand`/`runBind` — **bind.ts:804** | `--page`, one of (`--text [--occurrence]`) or (`--byte-start N --byte-end N`), `--type`, `--fact-id`\|`--fact` (+`--format` w/ `--attach-existing`), `--actor-id`, `--actor-kind`, `--reason`, `[--dry-run]` (bind.ts:169-274) | **Yes** (bind.ts:190-192; result status `"VALIDATED_DRY_RUN"`\|`"BOUND"`, bind.ts:80-81) | **None stated.** bind.ts has zero "undo" mentions. `edit-undo.ts:142-151` explicitly refuses to restore a fact bind CREATED ("that change created ${factId}... use the change's recorded reverse wish") |
| 3 | `edit:join` (pkg.json:13) | `runJoinCommand` — **join.ts:382** ("gather a value's occurrences, stage one human ruling", join.ts:1) — stages only, writes nothing but a pending proposal | `<value> [--fact <factId>] [--dry-run]` (join.ts:330) | **Yes** (join.ts:316-318, 387-389: `"Dry run: nothing was written..."`) | None stated |
| 4 | `edit:confirm-join` (pkg.json:14) | `runConfirmJoinCommand` — **join.ts:520** — applies the staged proposal in one batch | `<proposalId> [--include <id>]... [--exclude <id>]...` (join.ts:416-436) | **No** (`parseConfirmArgs` recognizes only `--include`/`--exclude`, join.ts:416-437) | None stated |
| 5 | `edit:insert-text` (pkg.json:15) | `runInsertTextCommand` — **insert-text.ts** ("adds new content adjacent to an existing carrier and DOES change the served bytes", insert-text.ts:1-9) | `--page <p> (--after-fact <id>\|--before-fact <id>) --text "<t>" [--adjacent-at <n>] [--fact-id <id>] --actor-id <id> --actor-kind human\|agent\|system\|capture --reason "<t>" [--confirmed] [--dry-run]` (insert-text.ts:18-20) | **Yes** (insert-text.ts:20) | `scripts/il-edit/ops/remove-inserted-text.ts` — "THE UN-WISH. The inverse of insert-text" (remove-inserted-text.ts:1), fenced to zero-width operator-added inserts only (remove-inserted-text.ts:14-19). **Not its own npm script** — reached only through `scripts/remote-edit-request.ts:886`'s call to `runRemoveInsertedText` when the plain-English wish is "remove" |
| 6 | `edit:mint-page` (pkg.json:16) | `runMintPageCommand`/`runMintPage` — **mint-page.ts:1102-1145** ("MAKE ME A NEW PAGE. The creation verb", mint-page.ts:1) | `--principal <path> --composition <path> --actor-id <id> --actor-kind ... [--ask <t>] [--slug <s>] [--minted-at <ts>] [--dry-run]` (mint-page.ts:1102-1136) | **Yes** (mint-page.ts:1108-1109) | **Explicit, machine-emitted**: result carries `undo: "npm run edit:unmint-page -- --slug ${slug}"` (mint-page.ts:944). Header: "one-word undo (`edit:unmint-page`)" (mint-page.ts:28) |
| 7 | `edit:unmint-page` (pkg.json:17) | `runUnmintPageCommand`/`runUnmintPage` — **unmint-page.ts** ("ONE WORD TAKES BACK A WHOLE MINTED PAGE", unmint-page.ts:1); mechanically unreachable on captured pages (no `mint/<slug>.json`) (unmint-page.ts:8-13) | `--slug <s> --actor-id <id> --actor-kind ... [--ask <t>] [--dry-run]` (unmint-page.ts:265-288) | **Yes** (unmint-page.ts:271-272) | Its result carries `redo: "npm run edit:mint-page -- --principal <p> --composition <p> --actor-id ${id} --actor-kind ${kind}"` (unmint-page.ts:258) — since unmint-page *is itself* mint-page's undo, its own reversal is named `redo` |
| 8 | `edit:image` (pkg.json:18) | `handle()` (exported as `runImageOperationCommand`) — **image.ts:281-309**, called with `["--genome-dir", REPO_ROOT, ...rest]` (edit-cli.ts:4304-4306) | `--genome-dir <d> --fact <factId> --file <path> [--dry-run]` (image.ts:255-279) | **Yes** (image.ts:259-261; `code: "DRY_RUN"`\|`"APPLIED"`) | No separate undo command. On a mid-write failure it self-reverts the staged resource via `revertAttributedImageResource(ingest.staging)` (image.ts:244-245, `guard/genome-fact-writer.ts:285`). A *successful* swap is an ordinary fact-value change, so it inherits generic `edit-undo.ts` coverage |
| 9 | `edit:logo` (pkg.json:19) | `runLogoEditCommand` — **logo.ts:297-319** | `--file <path> [--choose <claimKey>]` (logo.ts:304) | **No CLI flag** (usage names only `--file`/`--choose`, logo.ts:304). Internally computes a no-write `dryRunIngest` first only to learn the served path (logo.ts:213-219), never exposed as an option | None stated in logo.ts. (The `claim-edit-complete` exit shape carries `isUndo`/`undoSubjects`/`undoCoHoldersUntouched` slots — census row 27 — so the underlying claim-edit-transaction machinery is undo-aware, but logo.ts names no undo command) |
| 10 | `edit:headline` (pkg.json:20, alias of `carrier-group`) | `cmdCarrierGroup`/`runCarrierGroupEditCommand` — **edit-cli.ts:2417-2424, 3851** (no ops file) | `(--seed <factId> \| --page <page> --intent "<t>") <newHeadline> [--dry-run]` (edit-cli.ts:3802-3803, 3841-3844) | **Yes** (edit-cli.ts:3824-3826, 3942-3961) | None of its own; every replacement is an ordinary `cmdFactEdit` write to a **pre-existing** fact (edit-cli.ts:3985-3994, preflighted via `runFactEditCommand(...,"--dry-run")` at 3930-3935) — unlike bind/join it never creates a fact, so it IS covered by generic `edit-undo.ts` |
| 11 | `edit:preview` (pkg.json:27) | `runEditPreviewCommand`/`cmdPreview` — **edit-cli.ts:2436, 2792** | none | N/A — build/verify step, no mutation of book state | N/A; a RED result clears the preview chain records itself (edit-cli.ts:4203-4216) |
| 12 | `edit:promote` (pkg.json:29) | `runPromoteCommand`/`cmdPromote` — **edit-cli.ts:2823, 2944** | `--yes [--force-red-preview "<reason>"]` (edit-cli.ts:2816-2820) | **No** | **Explicit**: `"edit:promote: undo point = ${sha} — \"npm run edit:rollback\" returns the whole book there."` (edit-cli.ts:2937-2938) |
| 13 | `edit:rollback` (pkg.json:30) | `runRollbackCommand`/`cmdRollback` — **edit-cli.ts:2948, 2986**; wraps `git revert` as one attributed commit via `rollbackTo()` (edit-cli.ts:2807-2810) | none | **No** | None stated; refuses cleanly (`NO_ROLLBACK_ANCHOR`) with no earlier state to return to (edit-cli.ts:2952-2961) |
| 14 | `edit:confirm-world-consequence` (pkg.json:31) | `runConfirmWorldConsequenceCommand`/`cmdConfirmWorldConsequence` — **edit-cli.ts:2426, 2180** | `<factId> [--proposal <proposalId>]` (edit-cli.ts:2196) | **No** | None stated in-body; writes via the same `writeFactValue` path as `edit:fact` (edit-cli.ts:2288-2296) against a pre-existing before/after pair, so it inherits generic `edit-undo.ts` coverage |
| 15 | `edit:request` (pkg.json:22) = `scripts/remote-edit-request.ts` | itself — "the door" — backed by `lib/remote-edit-request.ts`, `lib/edit-resolution.ts`, `lib/edit-transaction.ts`, and re-invoking `runBind`, `runInsert`, `runRemoveInsertedText`, `runFactEditCommand`, `runConfirmWorldConsequenceCommand`, `runEditPreviewCommand` (remote-edit-request.ts:20-56) | `--instruction "<text>" [--filing-id <id>] [--grounding <legacy.json>] [--requester <name>] [--result <result.json>] [--choose <answer>]` (remote-edit-request.ts:385-408) | **No CLI flag**; internal call sites pass `dryRun:false` for the live apply (e.g. remote-edit-request.ts:787,816,893,1047) though sweeps rehearse internally (remote-edit-request.ts:1191) | It **is** the undo mechanism others ride: `edit-undo.ts` files its reverse wish through this door (edit-undo.ts:257-261), and it is the only path to `remove-inserted-text.ts` (remote-edit-request.ts:886) |
| 16 | `edit:gate-plan` (pkg.json:28) | `cmdGatePlan`/`planEditGate` — **edit-cli.ts:2642, 2581**; explicitly "OUTSIDE the git spine" (edit-cli.ts:4241) | none | N/A — read-only measurement, prints `{mode,basis,verbs}` JSON (edit-cli.ts:29-31) | N/A |
| 17 | `media:mint` (pkg.json:32) | `main()`/`mintMediaAnchors()` — **generate-media-anchors.ts:336, 434-449** | none required; `--check` switches to a read-only verify-only mode instead of minting (generate-media-anchors.ts:436-439) | **No** `--dry-run`; `--check` verifies existing records against the recognizer, it does not preview pending writes | None stated |
| 18 | `scripts/edit-undo.ts` (not an npm script) | itself — files its reverse "through the door" (`remote-edit-request.ts`), holds no write powers of its own (edit-undo.ts:6-8) | `--change <sha> [--requester NAME] [--dry-run]` (edit-undo.ts:18) | **Yes** (edit-undo.ts:36, 185-189) | Its own reverse is an ordinary commit and so could itself be found/re-reversed ("the reverse wish brings it forward again if you change your mind", edit-undo.ts:292); but it explicitly **cannot** restore CREATED facts or deleted content (edit-undo.ts:136-151, `"not-undoable"`) |
| 19 | `scripts/edit-history.ts` (not an npm script) | itself — reads `Genome-Claim: edit` commits + each one's `facts/` diff (edit-history.ts:85-106, 186-205) | `[--limit N] [--json\|--plain]` (edit-history.ts:10) | N/A — pure reader | N/A — it's the read half `edit-undo.ts` consumes (`editHandlesMatching`/`readChange`, edit-history.ts:27,172-184) |

---

## 2. EXIT/REFUSAL SURFACE

**`scripts/lib/teammate-exit-register.ts`.** `TEAMMATE_EXIT_IDS` (lines 22-58) is a flat array of **35** ids — verified programmatically, and it exactly matches the row count of `docs/specs/one-reader-exit-census.json` (also 35). The array itself carries no grouping comments; all classification lives in the JSON census. `DOOR_NOTICE_CODES` (lines 79-158) is a **separate, larger vocabulary of 73 codes**, grouped in-source by comment headers (consent seam 12, route/redirect 9, resolution boundaries 19, receipts-that-ride-an-applied-edit 23, choice labels 10) — these are sub-messages (`doorNotice()`, lines 165-176) that ride *inside* the 35 top-level exits, not exits themselves. Runtime enforcement is `assertTeammateExitSlots` (lines 242-258): every emitted exit must carry **exactly** its declared slot set — missing or extra slots both throw `AT-4`. The census loader (`readerRows`, lines 191-229) fail-closes on malformed rows and explicitly **bans** any row from ever again carrying `template`/`sentenceOwner`/`sentence`/`prose`/`say` (lines 217-223: "THE MOUTH IS GONE AND STAYS GONE") — the whole point of this file (per its own header, lines 4-20) is that it is a *census*, never a *voice*.

**`docs/specs/one-reader-exit-census.json`** — **35 rows**. Distinct `audience` values (2): `teammate` (27), `cockpit` (8). Distinct `classification` values (14): `physics-refusal` (9), `facts-shaped` (6), `edited` (6), `rails-refusal` (3), `ask` (2), and one each of `labeled-absence`, `replay`, `route-refusal`, `consent-state`, `world-consequence-ask`, `labeled-absence-and-ask`, `physics-or-structure-refusal`, `facts-shaped-or-physics-refusal`, `route-inform`. Five representative rows:
- `missing-instruction` — cockpit / physics-refusal / `slots: []` (the minimal case, line 3-9)
- `consent-required` — cockpit / consent-state / `slots: [act, awaiting, consequenceClass]` (line 68-78)
- `classification-place-ask` — teammate / ask / `slots: [facts, notice]` (line 123-132)
- `redirect-complete` — teammate / edited / 12 slots incl. `undoWith` (line 145-164)
- `edit-complete` — teammate / edited / 15 slots (line 419-442) — the generic fact-edit door's terminal success exit

**Flat refusal vs route/ask shape.** The 14 rows classified `*-refusal` (`physics-refusal`/`rails-refusal`/`physics-or-structure-refusal`/`facts-shaped-or-physics-refusal`, e.g. `bind-refusal`, `insert-preview-refusal`, `scoped-transaction-refusal`) are flat stops. Exactly one id that is *named* like a refusal is classified as a **route**: `redirect-refusal` → `"route-refusal"` (census line 56-66) — the census records it as handing off to a named owner rather than merely stopping. Six rows are ask/route/inform-shaped: `classification-place-ask`, `classification-insert-ask` (`ask`), `redirect-confirmation-ask` (`world-consequence-ask`), `insert-list-shape-ask` (`labeled-absence-and-ask`), `redirect-refusal` (`route-refusal`), `sweep-partial-inform` (`route-inform`). This distinction is structural in `scripts/il-edit/refusal-envelope.ts` too: every `RefusalEnvelope` optionally carries `cure?: string` **and/or** `route?: {owner:"human"|"factory", say}` (lines 6-11), and `refusalEnvelope()` throws if **both are absent** (lines 39-43: `"refusal ${code} requires a cure or route"`) — i.e. every refusal in this codebase is either self-curable (flat) or routed to a named owner, never neither.

**`docs/specs/census-baseline.json`** — `schemaVersion: 1`, `basis`: `"assembly-line-build@f402fe739 + structure verbs (mint/unmint, 2026-08-11) + landing 2026-08-12: union with trunk's walls rows (47/55/56a fold), counts recomputed from rows"` (line 3). Declared totals: **INFORM 2 / RAILS 29 / ROUTE 18 = 49 rows**, spanning **four** modules, not one — verified per-module breakdown:
- `scripts/il-edit/ops/join.ts` — 22 codes (2 INFORM, 7 RAILS, 13 ROUTE)
- `scripts/il-edit/ops/mint-page.ts` — 19 codes (15 RAILS, 4 ROUTE)
- `scripts/il-edit/ops/unmint-page.ts` — 6 codes (5 RAILS, 1 ROUTE)
- `scripts/lib/remote-edit-request.ts` — 2 codes (2 RAILS: `RAILS_REMOTE_REQUESTER_ACTOR_DIVERGENCE`, `RAILS_REMOTE_UNCLOSED_SPAN`)

This is the STOP-code census for exactly the join/mint/unmint/remote-edit verbs (each row's `code` corresponds to a `STOP.*` constant consumed via `answer()`/`refuse()` in those files, e.g. `STOP.ROUTE_JOIN_DRY_RUN_DUPLICATE` at join.ts:317) — `bind`, `fact`, `insert-text`, `image`, `logo` etc. are outside this particular census's jurisdiction.

---

## 3. M/J FORK

`jurisdiction-router.ts`'s `routeClaim()` (**jurisdiction-router.ts:136-160**) is a pure closed-set lookup, never a per-edit judgment call: it reads a claim's on-disk `type:` string (`readFactType`, lines 265-287) and tests it for **exact** membership in `M_TYPES` — 11 real genome type strings covering telephone/fax/email plus 5 `postal-address-*` and 3 `opening-hours*` subtypes (lines 75-87, ratified list "phone, email, address, hours, price" with price "DELIBERATELY OMITTED" since no such type exists in the fixed taxonomy, lines 61-68). Only exact `M_TYPES` membership can return court `"M"`; a recognized-but-judgment type in `J_TYPES` (richtext, design-color, business-name, page-title, etc., lines 96-128) or any missing/unrecognized type fails closed to `"J"` (lines 136-160) — "there is no 'default to M' branch anywhere in this file" (line 13). Above that type lookup sits a second, structural "carrier rail": `routeLinkDestination()`/`isLinkDestinationCarrier()` (lines 225-247) reads only *where* the edited bytes sit — an attribute-value layer whose attribute name is `href`/`action`/`formaction` (lines 193-211) — and can overrule an "M" verdict into "J" because "nothing recorded says where this practice's links belong" (line 215; motivated by a measured incident where a booking-link retarget slipped through as ordinary text, lines 166-171). `auto-lane.ts`'s `evaluateAutoEligibility()` (**auto-lane.ts:245-372**) then treats reaching "M" as necessary but not sufficient: it re-checks, in a fixed load-bearing order — carrier rail, then court, then well-formedness, then source-of-truth presence, then the match itself (lines 240-243) — the specific new value against a per-type shape check (`wellFormednessFailureReason`, lines 157-206) and then against an external authority supplied via a `SourceOfTruthProvider` callback (lines 69-72, 314-333); only a value that is well-formed **and** fold/byte-equal to a recorded authority ships with `disposition: "auto"` (lines 365-371), a missing authority routes to a human as `"needs-person"` — explicitly "NOT a refusal" (lines 26-27, 89-98) — and a malformed value or an authority **mismatch** is `"rejected"` outright, deliberately never downgraded to human review (lines 335-363).