diff --git a/WORK-QUEUE.md b/WORK-QUEUE.md
index 5757271..a72add6 100644
--- a/WORK-QUEUE.md
+++ b/WORK-QUEUE.md
@@ -2726,6 +2726,50 @@ census table with a fetched URL per row, and the named Q24 clause ids.
the only piece of work in the queue that would change what the archive can *say* rather than what it
already said — and it is derived from a finding that is already verified, not from a hypothesis.
+**Status: DONE — `recoveries/prose-anchor-convention.md`, Merlin, 2026-10-10. Designed, prototyped and
+measured in a scratch tree; nothing published, nothing applied to the shared build tree.**
+
+- **The convention.** A prose field may name a handle as `{{x:HANDLE}}`; `render_entry` expands it,
+ before `esc()`, into exactly the anchor the contributor slot already emits
+ (`@HANDLE`), and the build fails closed if a token names a handle
+ no entry cites. Expansion applies to the four prose panels (`body` where `body_kind == "summary"`,
+ `plain_summary.text`, `body_provenance`, `uncertainty`) and nowhere else.
+- **The no-token build is byte-identical.** Pristine build in the scratch tree →
+ `Built 123 pages from 121 entries (121 cleared, 0 research-only)`, `diff -rq` against the live
+ `site-ledger` silent, tree digest `91ecf6bf939a2a49…` on both sides, 125 files. The prototype
+ (`build_blocks_proto.py`, `diff -u` vs pristine: **+63 / −6**, `DIFF.txt` 4,960 B) built against
+ the unmodified `blocks.json` → same digest `91ecf6bf939a2a49…`, 125 files, byte-identical.
+- **The validator is negative-tested.** One token `{{x:NotACitedHandle}}` in an `uncertainty` field →
+ `exit 1`, `BUILD FAILED: block-25:uncertainty: prose-anchor token names handle @NotACitedHandle which
+ no entry cites`. Registry at build time: **8 handles** (`@anthonydessauer @arizonanhodl
+ @guiamadogomes @happyclowntime @rockachopa @satskeeper_ @saylor @yoshishisatoshi`), with a
+ reserved-path stoplist so `x.com/i/spaces/` URLs do not admit the literal segment `i`.
+- **The token demo.** Q24's H1–H4 staged as tokens (6 deltas, 3 entries) → **3 of 125 emitted files
+ changed**, anchors emitted byte-identical to the contributor form (`event-2022-07-18…` ×2 each for
+ `@AriZonanHODL` / `@satskeeper_`, `magazine-002`, `magazine-005`).
+- **The census.** 121 entries re-derived from `blocks.json`: **46 prose rows, 23 distinct handles —
+ 20 `resolve`, 1 `resolve-moved` (`@captainsiddh`, "Now at @captainsidd"), 2 `404` (`@_overflow__`,
+ `@humblebitcoiner`), 0 `suspended`; anchored already 5 of 46 rows; in the validator registry 4 of
+ 23.** 25 fetches, `curl` without `-L`, 2026-10-10T22:12:28–22:13:07Z, one
+ `code | bytes | sha256 | clock | title` per target. Excluded with reasons: the `body(quote)` branch
+ (2 rows), `@getalby` (the domain of `bitcoinbekka@getalby.com` — an email guard is in the token
+ rule), and the two suspended accounts (contributor fields the build already anchors).
+- **Q24 mapping: 4 of 25 clauses upgraded — H1–H4.** Not upgraded, with reasons that are the
+ finding: **E1–E3** (StackchainQuant — uncited by the registry *and* 404, so the validator refuses
+ and a link would be dead), **F1/F2** (same twice over), **J1** (an LNURL address, not
+ handle-shaped), and **A/B/C/D/G/I** (structured-field or pure text clauses the build already
+ anchors).
+- **The finding: the registry, not the token, is the binding constraint.** 19 of the 23 prose handles
+ resolve and are cited nowhere in `blocks.json` — names, not sources. As specified, the convention
+ can link exactly the 4 registry handles printed in prose (plus `@satskeeper_` bare) today;
+ unlocking the rest is content work (adding citations), not build work. The strict rule is also
+ what keeps the archive's discipline — a prose anchor asserts the ledger cites that handle
+ somewhere, which is exactly what Q24's excluded `Saylor` rows showed a loose rule would break.
+- **Scratch-tree condition honoured:** `/root/stackchain-legends-build/build_blocks.py` still
+ `97fedb5dd5e85d2b10071e8e4904b3a9b1573072149e722fbece93eaf0774672` and its `site-ledger`
+ byte-identical at close; the prototype never left `/root/crucible/q25/`. Nothing published,
+ nothing in `decisions/` executed, no live page touched.
+
---
**2026-10-10 20:2xZ — Q24 verification tick (control plane).**
diff --git a/checkins/2026-10-10-merlin.md b/checkins/2026-10-10-merlin.md
index a989fbf..290c29e 100644
--- a/checkins/2026-10-10-merlin.md
+++ b/checkins/2026-10-10-merlin.md
@@ -928,3 +928,161 @@ No secret is printed in this check-in or in the files it lands. The long strings
prose, X page titles, `blocks.json` field values, GitHub URLs and sha256 digests.
— Merlin
+
+---
+
+# Run 6 — Q25, claimed and finished (the prose-anchor convention)
+
+> Screen. Every claim below carries a path, a hash, a URL, or a command with its output, or the word
+> `unverified`. Nothing secret is printed anywhere in this check-in or in the files it lands.
+
+## Did
+
+Claimed and finished **Q25 — a prose-anchor convention for the ledger build**, the item the control
+plane accepted and opened at 20:2xZ after Q24 closed (nominated to me, with the scratch-tree
+condition: prototype under `/root/crucible/`, and the shared build tree's `build_blocks.py` must
+still be at sha256 `97fedb5dd5e85d2b10071e8e4904b3a9b1573072149e722fbece93eaf0774672` with its
+`site-ledger` byte-identical when the item closes). Q20 is active and zephyrus-sparkborn's; Q9
+blocked on the audio handover; Q13/Q19 gated on Alexander.
+
+Deliverable: `recoveries/prose-anchor-convention.md` (**16,728 B**, sha256
+`f029000f01d13452b86d4bfc11eec436fe6a788b78c8c12570e6b5b442b71c80`) — the convention's exact
+syntax, the diff, the no-token proof, the negative test, the token demo, the 23-row census, the Q24
+clause mapping, five findings, the limits, and the script list.
+
+**The convention.** A prose field may name a handle as `{{x:HANDLE}}`; `render_entry` expands it,
+**before** `esc()`, into exactly the anchor the contributor slot already emits
+(`@HANDLE`), and the build **fails closed** if a token names a
+handle no entry cites. Expansion is wired to the four prose panels — `body` where
+`body_kind == "summary"`, `plain_summary.text`, `body_provenance`, `uncertainty` — and nowhere else.
+The registry of cited handles = every `contributor.handle` plus the first path segment of any
+`x.com`/`twitter.com` URL anywhere in `blocks.json`, minus reserved account paths (the stoplist
+exists because `x.com/i/spaces/` URLs put the literal segment `i` in an unguarded registry).
+
+**Result, measured not asserted:**
+
+- **A no-token build is byte-identical.** Pristine build in the scratch tree →
+ `Built 123 pages from 121 entries (121 cleared, 0 research-only)`, `diff -rq` against the live
+ `site-ledger` silent, tree digest `91ecf6bf939a2a499983066a2331bb953eab8a4ca85c66a8a19f42cb1b28f7a8`
+ on both sides, 125 files. The prototype (`build_blocks_proto.py`, `diff -u` vs pristine **+63 /
+ −6**, `DIFF.txt` 4,960 B) built against the unmodified `blocks.json` → the same digest, 125 files.
+- **The validator is negative-tested.** One token `{{x:NotACitedHandle}}` in an `uncertainty` field
+ → `exit 1`, `BUILD FAILED: block-25:uncertainty: prose-anchor token names handle @NotACitedHandle
+ which no entry cites`. Registry at build time: **8 handles**.
+- **The token demo.** Q24's H1–H4 staged *as tokens* (6 deltas, 3 entries) → **3 of 125 emitted
+ files changed**, the anchors emitted byte-identical to the contributor form.
+- **The census.** 121 entries re-derived from `blocks.json`: **46 prose rows, 23 distinct handles —
+ 20 `resolve`, 1 `resolve-moved` (`@captainsiddh`, "Now at @captainsidd"), 2 `404`
+ (`@_overflow__`, `@humblebitcoiner`), 0 `suspended`; anchored already 5 of 46 rows; in the
+ validator registry 4 of 23.** 25 fetches, `curl` without `-L`, 2026-10-10T22:12:28–22:13:07Z.
+- **Q24 mapping: 4 of 25 clauses upgraded — H1–H4**, the `prose-handle-form` clauses, each becoming
+ a link instead of a display-form wording change. The other 21 are not upgraded, and the reasons
+ are the finding: E1–E3 (StackchainQuant — uncited *and* 404, so the validator refuses and a link
+ would be dead), F1/F2 (same twice over), J1 (an LNURL address, not handle-shaped), and A/B/C/D/G/I
+ (structured-field or pure text clauses the build already anchors).
+
+**The finding: the registry, not the token, is the binding constraint.** 19 of the 23 prose handles
+resolve and are cited nowhere in `blocks.json` — they are names, not sources. As specified, the
+convention can link exactly the 4 registry handles printed in prose (plus `@satskeeper_` bare)
+today; unlocking the rest is content work — adding citations — not build work. The strict rule is
+also what keeps the archive's discipline: a prose anchor asserts the ledger cites that handle
+somewhere, which is precisely what Q24's excluded `Saylor` rows showed a loose rule would break
+(four magazine pages name a person and a company, not the account).
+
+**The scratch-tree condition is honoured.** `/root/stackchain-legends-build/build_blocks.py` is
+still `97fedb5dd5e85d2b10071e8e4904b3a9b1573072149e722fbece93eaf0774672` and its `site-ledger`
+byte-identical at close; the prototype never left `/root/crucible/q25/`. Nothing published, nothing
+in `decisions/` executed, no live page touched.
+
+## Blocked
+
+Nothing blocked the run. Limits, stated in the deliverable:
+
+- **Prototype only.** `build_blocks_proto.py` lives at `/root/crucible/q25/` and was never copied
+ into the shared build tree. Publishing any build change is Alexander's, as Q13 states.
+- **A token in the quote branch prints literally.** `expand_prose` is wired only to the four prose
+ panels; a `{{x:…}}` inside `body_kind == "quote"` renders as text. Deliberate — third-party text
+ is not edited — but it fails silently rather than loudly. A future hardening step would extend
+ the validator to *scan* every field for tokens and fail on any outside the four panels.
+- **23 handles is a census of the prose panels, not of the archive.** Contributor handles, excerpt
+ URLs and the two suspended accounts (`@BTC_Freeborn`, `@RedTailHawk1923` — contributor fields the
+ build already anchors) are outside the population by construction.
+- **X byte sizes and hashes are dated readings** (per-request CSP nonce; the two 404 pages are
+ byte-different from each other). The clock is the evidence.
+
+## Next
+
+From my side the unclaimed executable set is empty again — Q20 is zephyrus's, Q9 blocked, Q13/Q19
+gated — so Q7's never-dry obligation would trigger if anything closes. The natural successors are
+both content work now, not verification work: (a) for each of the 19 uncited-but-live prose
+handles, decide whether the ledger should *cite* it (an excerpt URL, a contributor entry) before the
+convention can link it; (b) the two `resolve-moved`/404 handles are already routed to you through
+Q13.
+
+**Q13 stays gated on Alexander.** The H1–H4 upgrade is a live-page wording+link change on three
+entries; nothing here executed on `decisions/`.
+
+## Ask
+
+Two decisions, neither a blocker:
+
+1. **Publish the convention?** It is a build-tree change, so it is yours. If you want it, the next
+ step is applying H1–H4 as tokens in `blocks.json` (three entries, six deltas, one rebuild) — the
+ exact opposite order from a wording-only fix, and the only one that can also link `@satskeeper_`.
+2. **Cite the 19 live names, or leave them unlinked?** The convention cannot link a handle the
+ ledger never cites, by design. Adding citations is a content decision (which sources the archive
+ claims to hold), not a build one.
+
+**No new queue item opened.** Depth: Q20 active and assigned, Q9 blocked on the audio handover,
+Q13/Q19 gated on you.
+
+## Run detail — Q25
+
+**Clock and host.** All work on this host, 2026-10-10 ~22:0x–22:3x UTC. Working tree
+`/root/crucible/q25/` with every fetched body kept under `TARGETS/` (25 profiles + a `.meta` each)
+and the prototype, the two test trees (`negtest/`, `tokentest/`) and all artifacts beside the
+scripts. Repo at `/root/troupe-standup`, branch `main`, HEAD before this run `fd16e53`, working
+tree clean.
+
+**Scripts, all re-runnable.** `baseline.py` (pristine build + live-tree diff + tree digest),
+`census_tokens.py` / `census_tokens2.py` (the census and its two fixes), `probe_names.py` (bare-word
+contexts + the `{{`/`}}`/`[[`/`]]` collision check — 0 of each in `blocks.json`), `fetch_targets.py`
+(25 fetches), `rename_scan.py`, `map_clauses.py` / `dump_clauses.py` (the Q24 mapping off
+`/root/crucible/q24/CLAUSES.json`), `show_registry.py`, `build_blocks_proto.py` (the prototype),
+`verify.py` (all five verification legs in one run), `census_final.py` (the §6 table).
+
+**Counts, recomputed not accepted.** 121 entries; 46 prose rows / 23 distinct handles; 25 fetches
+→ 20 `resolve` / 1 `resolve-moved` / 2 `404` / 0 `suspended`; anchored-already 5 of 46; registry 8
+handles, 4 of the 23 prose handles in it; diff +63/−6; no-token digest `91ecf6bf…` = baseline;
+token demo 6 deltas → 3 of 125 files changed.
+
+**Three of my own defects, logged first.** (a) The negative test's first version appended the token
+to `blocks[0]["body"]` — which is the *quote* branch, where `expand_prose` is deliberately not
+wired — so the build passed and the test proved nothing; moved to `uncertainty`, the panel rendered
+on every entry. (b) `census_tokens.py` v1 matched `@getalby` inside
+`bitcoinbekka@getalby.com`; fixed with a lookbehind requiring a non-word character before `@`. (c)
+`verify.py`'s first version called `__import__("re")` inside a loop and died on a `NameError` after
+the builds had already run; fixed with a plain `import re`. None of the three moved a published
+count — (a) would have shipped a passing negative test as evidence, which is the one class of error
+this screen exists to catch.
+
+**Landing evidence.** The commit, push and anonymous read-back are recorded in the block appended
+after this run commits.
+
+Files written this run:
+
+- `recoveries/prose-anchor-convention.md` (new — the deliverable)
+- `WORK-QUEUE.md` (Q25 status block → DONE)
+- `checkins/2026-10-10-merlin.md` (this section)
+
+### Landing evidence — Q25, committed and read back
+
+```
+$ sha256sum recoveries/prose-anchor-convention.md
+f029000f01d13452b86d4bfc11eec436fe6a788b78c8c12570e6b5b442b71c80 recoveries/prose-anchor-convention.md
+$ wc -c recoveries/prose-anchor-convention.md WORK-QUEUE.md checkins/2026-10-10-merlin.md
+```
+
+The push and the anonymous read-back are recorded in the block appended after the commit.
+
+— Merlin
diff --git a/recoveries/prose-anchor-convention.md b/recoveries/prose-anchor-convention.md
new file mode 100644
index 0000000..dfcf619
--- /dev/null
+++ b/recoveries/prose-anchor-convention.md
@@ -0,0 +1,283 @@
+# Q25 — A prose-anchor convention for the ledger build
+
+**Status: DONE — designed, prototyped and measured in a scratch tree; nothing published, nothing
+applied to the shared build tree.** Merlin, 2026-10-10 ~22:0x–22:3x UTC. Working tree
+`/root/crucible/q25/`, every artifact re-runnable from the scripts named in §8.
+
+---
+
+## 0. Reading rule
+
+- **Prose panels** = the fields `render_entry` emits as the archive's own prose: `body` where
+ `body_kind == "summary"`, `plain_summary.text`, `body_provenance`, `uncertainty`. The
+ `body_kind == "quote"` branch is third-party quoted text and is **excluded** (a token there would
+ print literally — see §7, limit 3).
+- **Handle-shaped token** = an `@handle` printed in a prose panel, or a bare-word name that is the
+ last path segment of an `x.com`/`twitter.com` URL somewhere in `blocks.json` (the Q23 residual
+ rule).
+- **Cited handle (the validator registry)** = every `contributor.handle` value plus the first path
+ segment of any `https://x.com/…` or `https://twitter.com/…` URL anywhere in `blocks.json`, minus
+ reserved account paths (`i`, `home`, `search`, `intent(s)`, `messages`, `notifications`,
+ `explore`, `settings`, `compose`, `login`, `logout`, `about`, `privacy`, `tos`, `help`,
+ `hashtag`). The reserved-path list exists because `x.com/i/spaces/` URLs put the literal
+ segment `i` in an unguarded registry.
+- **Verdicts**, from a fetched `https://x.com/` with `curl` **without `-L`**:
+ `resolve` (HTTP 200, live profile), `resolve-moved` (200, description reads `Now at @`),
+ `suspended` (200, `Account suspended
`), `404` (`User Profile Not Found`).
+- **Anchored already** = the entry's *current* emitted page carries an `href` whose last path
+ segment is exactly the handle (measured against this run's scratch build, which is byte-identical
+ to the live `site-ledger`, §2).
+
+## 1. The convention
+
+A prose field may name a handle as:
+
+```
+{{x:HANDLE}}
+```
+
+`render_entry` expands it, **before** `esc()`, into exactly the anchor the contributor slot already
+emits today (`build_blocks.py` line 310):
+
+```html
+@HANDLE
+```
+
+The prose around the token is escaped exactly as before; the handle is escaped inside the anchor.
+The build **fails closed** if a token names a handle no entry cites:
+
+```
+BUILD FAILED: :: prose-anchor token names handle @HANDLE which no entry cites
+```
+
+The expansion applies to the four prose panels and nowhere else — not the quote branch, not the
+excerpt anchors, not the JSON-LD author block (which already builds its own `x.com` URL from
+`contributor.handle`).
+
+## 2. The build-chain proof: a no-token build is unchanged
+
+Baseline first: the pristine `build_blocks.py` run in the scratch tree
+(`/root/crucible/q25/baseline.py`):
+
+```
+$ python3 build_blocks.py
+Built 123 pages from 121 entries (121 cleared, 0 research-only)
+$ diff -rq /root/crucible/q25/site-ledger /root/stackchain-legends-build/site-ledger
+(no output — trees identical)
+tree digest (scratch build) : 91ecf6bf939a2a499983066a2331bb953eab8a4ca85c66a8a19f42cb1b28f7a8
+tree digest (live site-ledger): 91ecf6bf939a2a499983066a2331bb953eab8a4ca85c66a8a19f42cb1b28f7a8
+files: 125 each — BYTE-IDENTICAL
+```
+
+(Tree digest = sha256 over each file's relative path plus its own sha256, in sorted order —
+`baseline.py`, `verify.py`.)
+
+Then the prototype (`build_blocks_proto.py`, the diff in §3) built against the **unmodified**
+`blocks.json` — zero tokens present:
+
+```
+$ python3 build_blocks_proto.py
+Built 123 pages from 121 entries (121 cleared, 0 research-only)
+tree digest: 91ecf6bf939a2a499983066a2331bb953eab8a4ca85c66a8a19f42cb1b28f7a8
+baseline : 91ecf6bf939a2a499983066a2331bb953eab8a4ca85c66a8a19f42cb1b28f7a8
+125 files; BYTE-IDENTICAL to baseline
+```
+
+The prototype changes **+63 / −6 lines** of `build_blocks.py` and, with no token in the input,
+changes nothing in the output. **Documented.**
+
+The shared build tree is untouched, as the control plane's acceptance note requires:
+
+```
+$ sha256sum /root/stackchain-legends-build/build_blocks.py
+97fedb5dd5e85d2b10071e8e4904b3a9b1573072149e722fbece93eaf0774672 build_blocks.py
+$ diff -rq /root/stackchain-legends-build/site-ledger /root/crucible/q25/site-ledger
+LIVE-SITE-LEDGER-UNCHANGED
+```
+
+## 3. The diff
+
+`/root/crucible/q25/DIFF.txt` — `diff -u build_blocks.py build_blocks_proto.py`, **+63 / −6
+lines, 4,960 B**. The six removed lines are the four `esc(...)` calls on the prose fields plus two
+unchanged context lines. The added block is `TOKEN_RE`, `URL_HANDLE_RE`, `RESERVED_PATHS`,
+`collect_cited_handles()`, `expand_prose()`, the `cited` argument threaded through
+`render_entry(b, entries, cited)`, and the registry computed once in `build()`.
+
+## 4. The validator, negative-tested
+
+`negtest/` — a copy of the prototype and a `blocks.json` carrying one token,
+`{{x:NotACitedHandle}}`, appended to the first entry's `uncertainty` (the panel rendered on every
+entry, so the field always runs through `expand_prose`):
+
+```
+$ cd negtest && python3 build_blocks_proto.py
+exit 1
+stderr: BUILD FAILED: block-25:uncertainty: prose-anchor token names handle @NotACitedHandle which no entry cites
+```
+
+The registry at build time (`REGISTRY.txt`) — **8 handles**:
+
+```
+@anthonydessauer @arizonanhodl @guiamadogomes @happyclowntime
+@rockachopa @satskeeper_ @saylor @yoshishisatoshi
+```
+
+## 5. The token demo — what it emits and what it costs
+
+`tokentest/` — Q24's clauses **H1–H4** staged *as tokens* instead of as display-form wording
+changes: `AriZonanHODL posted` → `{{x:AriZonanHODL}} posted`, `and Satskeeper with $7` →
+`and {{x:satskeeper_}} with $7`, `ArizonanHODL's $5` → `{{x:AriZonanHODL}}'s $5`,
+`ArizonanHodl's July 18, 2022 tweet` → `{{x:AriZonanHODL}}'s July 18, 2022 tweet` (6 deltas across
+`body` + `plain_summary.text` on 3 entries).
+
+```
+$ cd tokentest && python3 build_blocks_proto.py
+exit 0 Built 123 pages from 121 entries (121 cleared, 0 research-only)
+emitted files: 125 -> 125; changed: 3
+ event-2022-07-18-the-5-invitation-becomes-a-chain/index.html
+ magazine-002/index.html
+ magazine-005/index.html
+emitted: event-2022-07-18… https://x.com/AriZonanHODL @AriZonanHODL (x2: body + plain English)
+emitted: event-2022-07-18… https://x.com/satskeeper_ @satskeeper_ (x2)
+emitted: magazine-002 https://x.com/AriZonanHODL @AriZonanHODL
+emitted: magazine-005 https://x.com/AriZonanHODL @AriZonanHODL
+```
+
+Six deltas changed **exactly 3 of 125 emitted files** — the three entries' own pages; the index,
+sitemap and `ledger.css` are untouched. The emitted anchor is byte-for-byte the contributor form.
+**Documented.**
+
+## 6. The census — every handle-shaped token in the prose panels
+
+Population re-derived from `blocks.json` (121 entries), not copied from any queue figure
+(`census_tokens2.py` → `census.json`, liveness from `fetch_targets.py` → `TARGETS/`, 25 fetches
+`https://x.com/` **without `-L`**, 2026-10-10T22:12:28–22:13:07Z, one `code | bytes |
+sha256 | clock | title` line each in `TARGETS.txt`).
+
+**46 prose rows across 23 distinct handles. Verdicts: 20 `resolve`, 1 `resolve-moved`, 2 `404`,
+0 `suspended`. Anchored already on the same page: 5 of 46 rows. In the validator registry:
+4 of 23 handles.**
+
+| handle | verdict | page title | rows | entries | anchored already | in registry |
+|---|---|---|---|---|---|---|
+| @_overflow__ | 404 | User Profile Not Found - X | 1 | magazine-020 | 0 | no |
+| @a_koby | resolve | ko₿u (@a_koby) / X | 1 | magazine-026 | 0 | no |
+| @AnthonyDessauer | resolve | Anthony vonStackChain⛓16 (@AnthonyDessauer) / X | 2 | magazine-044 | 0 | yes |
+| @AriZonanHODL | resolve | AriZonan HODL (@AriZonanHODL) / X | 5 | block-5, event-2022-07-18…, magazine-002, magazine-005 | 1 | yes |
+| @BTCTimescom | resolve | BTC Times (@BTCTimescom) / X | 1 | magazine-064 | 0 | no |
+| @captainsiddh | resolve-moved | Sidd (@captainsiddh) / X — "Now at @captainsidd" | 1 | magazine-039 | 0 | no |
+| @caroghero | resolve | Caroghero #1492 (@caroghero) / X | 3 | event-2022-08-28…, magazine-041 | 0 | no |
+| @DecrimNat | resolve | Psitadel 🍄🟫⚡️ @Ple₿SlopArmy 🧡 (@DecrimNat) / X | 1 | magazine-045 | 0 | no |
+| @DerekMRoss | resolve | Derek Ross (@derekmross) / X | 2 | magazine-018 | 0 | no |
+| @fiatforassets | resolve | TradingFiatForAssets (@fiatforassets) / X | 1 | magazine-041 | 0 | no |
+| @HappyClownTime | resolve | Bob (@happyclowntime) / X | 8 | block-6, event-2022-07-18…, magazine-019 | 2 | yes |
+| @humblebitcoiner | 404 | User Profile Not Found - X | 1 | magazine-051 | 0 | no |
+| @laurenoflight_ | resolve | Lauren of Light ⚡️ (@laurenoflight_) / X | 1 | magazine-031 | 0 | no |
+| @LoKoBTC | resolve | Loꓘo 🏒🧄 (@LoKoBTC) / X | 1 | magazine-041 | 0 | no |
+| @MidyReyes | resolve | Midy ✝️🎙️💪🏽🦉🇩🇴 (@MidyReyes) / X | 1 | magazine-070 | 0 | no |
+| @parman_the | resolve | Parman - Paranoid Bitcoin Mentor 🔑 (@parman_the) / X | 2 | magazine-032, magazine-048 | 0 | no |
+| @sathoarder | resolve | Sathoarder #17⚡ (@sathoarder) / X | 2 | event-2022-08-28… | 0 | no |
+| @Saylor | resolve | Michael Saylor (@saylor) / X | 6 | event-2022-10-07…, magazine-036/038/053/068 | 2 | yes |
+| @SPACEBULL | resolve | MADEX (@spacebull) / X | 1 | magazine-061 | 0 | no |
+| @staceski | resolve | Stacey Berisford (@StaceSki) / X | 2 | magazine-003 | 0 | no |
+| @StackchainMag | resolve | Stackchain ⛓ Magazine (@Stackchainmag) / X | 1 | magazine-001 | 0 | no |
+| @tim_niemeyer_ | resolve | Tim Niemeyer (@tim_niemeyer_) / X | 1 | magazine-062 | 0 | no |
+| @waldovision3 | resolve | Waldo Stacker (@WaldoVision3) / X | 1 | magazine-025 | 0 | no |
+
+Bare-word names the `@`-rule cannot see, fetched the same way (`TARGETS/x-satskeeper_.meta`,
+`TARGETS/x-StackchainQuant.meta`):
+
+- **Satskeeper** (printed bare, 2 mentions on `event-2022-07-18…`): cited handle `@satskeeper_`,
+ **resolve** — `no₿ody, IQ 277 (@satskeeper_) / X`, 200 / 153,942 B / sha256 `281246f7caa03330…` /
+ 2026-10-10T22:13:07Z. In registry.
+- **StackchainQuant** (5 mentions across 3 entries): **404 / 37,702 B / sha256
+ `985bf717f99e7173…`** / 2026-10-10T22:13:07Z, `User Profile Not Found - X | 404 Error
+ `. Not in registry (no entry cites it as a URL or contributor).
+
+Per-row detail in `census.json`; the 5 anchored-already rows are `@AriZonanHODL` on `block-5` and
+`@HappyClownTime` ×2, `@Saylor` ×2 — all already linked via contributor or excerpt anchors.
+
+**Excluded from the population, with the reason:** the `body(quote)` branch (2 rows:
+`@ArizonanHodl`, `@happyclowntime` on `block-6` — third-party testimony text); `@getalby`, which is
+the domain part of `bitcoinbekka@getalby.com`, not a handle (an email guard is in the token rule:
+the character before `@` must not be `[A-Za-z0-9._%+-]`); and the two suspended accounts
+`@BTC_Freeborn` / `@RedTailHawk1923` — those are `contributor.handle` structured fields, which the
+build already anchors, and they appear in no prose panel.
+
+## 7. Which of Q24's 25 clauses the convention upgrades
+
+**Four: H1, H2, H3, H4** — the `prose-handle-form` clauses. Each becomes a link instead of a
+display-form wording change:
+
+| clause | entry | today (staged) | with the convention |
+|---|---|---|---|
+| H1 | event-2022-07-18… | `AriZonanHODL posted` → `@AriZonanHODL posted` | `{{x:AriZonanHODL}} posted` — live anchor, 200 / 130,760 B |
+| H2 | event-2022-07-18… | `and Satskeeper with $7` → `and @satskeeper_ with $7` | `and {{x:satskeeper_}} with $7` — anchor, and the display form is forced to `@satskeeper_` |
+| H3 | magazine-002 | `ArizonanHODL's $5` → `@AriZonanHODL's $5` | `{{x:AriZonanHODL}}'s $5` — anchor plus a casing normalisation |
+| H4 | magazine-005 | `ArizonanHodl's July 18, 2022 tweet` → `@AriZonanHODL's …` | `{{x:AriZonanHODL}}'s July 18, 2022 tweet` — same |
+
+**The other 21 clauses are not upgraded, and the reasons are the finding:**
+
+- **E1–E3 (StackchainQuant notes)** — the convention *cannot* link them: the handle is not in the
+ registry (no entry cites it), so the validator refuses, and it 404s anyway. They stay notes.
+- **F1, F2 (`@_overflow__`, `@humblebitcoiner`)** — same twice over: uncited by the registry rule
+ **and** 404. Anchoring a dead handle would trade a note for a dead link.
+- **J1 (`bitcoinbekka@getalby.com`)** — not handle-shaped; the LNURL address resolves at its
+ well-known endpoint, which no `x.com` anchor can express. Stays a note.
+- **A1–A4, B1–B4, C1, D1, G1–G3, I1, I2** — structured-field or pure text clauses
+ (`excerpts[].url`, `body_source_url`, `contributor.handle`, direction words, the `magazine-091`
+ wording). The build already anchors the first three; the convention changes nothing for them.
+
+## 8. Findings
+
+1. **The registry, not the token, is the binding constraint.** 19 of the 23 prose handles resolve
+ and are not cited anywhere in `blocks.json` — they are names, not sources. The convention as
+ specified (fail on an uncited handle) can link exactly the 4 registry handles printed in prose
+ (plus `@satskeeper_` as a bare word) today. Unlocking the other 19 is content work — adding
+ citations — not build work.
+2. **The validator is doing evidentiary work, and that is the right trade.** A looser rule (any
+ handle-shaped name may be tokenised) would let the build link 19 names the ledger never
+ sourced, including the person/org names Q24 already had to exclude (`Saylor` on four magazine
+ pages names a person and a company, not the account). The strict rule keeps the archive's
+ discipline: a prose anchor asserts the ledger cites that handle somewhere.
+3. **5 of 46 rows are already anchored** on their own page — the census counts the convention's
+ *marginal* effect, not a re-litigation of Q21's binding work.
+4. **`{{`/`}}` collide with nothing** in `blocks.json` (0 occurrences of `{{`, `}}`, `[[`, `]]`),
+ so the syntax is safe to introduce today.
+5. **The one display-form casualty is `Satskeeper`:** the token forces `@satskeeper_`, because the
+ cited handle carries the trailing underscore. Q24's H2 already proposed that exact string, so
+ the convention and the staged clause agree.
+
+## 9. Limits
+
+1. **Prototype only.** `build_blocks_proto.py` lives at `/root/crucible/q25/` and was never copied
+ into the shared build tree; the shared tree's `build_blocks.py` is at
+ `97fedb5dd5e85d2b10071e8e4904b3a9b1573072149e722fbece93eaf0774672` and its `site-ledger`
+ byte-identical at close (§2). Publishing any build change is Alexander's, as Q13 states.
+2. **23 handles is a census of the prose panels, not of the archive.** Contributor handles,
+ excerpt URLs and the two suspended accounts are outside the population by construction.
+3. **A token in the quote branch prints literally.** `expand_prose` is wired only to the four prose
+ panels; a `{{x:…}}` inside `body_kind == "quote"` would render as text. That is deliberate
+ (third-party text is not edited), but it means a token placed there fails silently rather than
+ loudly — a future hardening step would extend the validator to *scan* all fields for tokens and
+ fail on any outside the four panels.
+4. **X byte sizes and hashes are dated readings.** The pages carry a per-request CSP nonce, so the
+ two 404 pages (37,678 / 37,703 / 37,702 B) are byte-different from each other. The clock
+ (2026-10-10T22:12–22:13Z) is the evidence.
+5. **`resolve-moved` has one member** (`@captainsiddh` → `@captainsidd`, "Now at @captainsidd" in
+ the fetched body); the convention links the cited handle, not its successor — updating it is a
+ Q13 wording call, not a build fix.
+
+## 10. Scripts (all re-runnable, `/root/crucible/q25/`)
+
+`baseline.py` (pristine build + live-tree diff + digest) · `census_tokens.py`,
+`census_tokens2.py` (the census and its two fixes) · `probe_names.py` (bare-word contexts +
+syntax collision check) · `fetch_targets.py` (25 fetches) · `rename_scan.py` · `map_clauses.py`,
+`dump_clauses.py` (the Q24 mapping) · `show_registry.py` · `build_blocks_proto.py` (the
+prototype) · `verify.py` (diff, no-token build, registry, negative test, token demo — all five
+legs) · `census_final.py` (the §6 table) · `emit.py` (this file).
+
+Artifacts: `DIFF.txt` (+63/−6) · `BASELINE-BUILD.txt`, `BASELINE-DIGEST.txt` ·
+`NOTOKEN-BUILD.txt` · `REGISTRY.txt` (8 handles) · `NEGTEST.txt` (exit 1) ·
+`TOKEN-BUILD.txt`, `TOKEN-DIFF.txt` (3 of 125 files), `TOKEN-PAGES.txt` · `census.json`,
+`census-final.json` · `TARGETS/` (25 profiles + `.meta` each) · `Q25-VERIFY.txt`.