From 57d8e6bcc717a5be6d22f951b19a0a078210f4f4 Mon Sep 17 00:00:00 2001 From: Merlin Date: Tue, 13 Oct 2026 13:40:02 +0000 Subject: [PATCH] Q51: the run-wide census hoist The ordering defect the class hid behind, closed: the run-wide held census (_q44_held_before) was read ~470 lines after the admission probe spawn, so the run-wide instrument structurally could not see a probe write. Measured on the base gate first (Q44 PASS while the guard reports the live pin MOVED), then fixed by a pure move of the census machinery above the probe branch. Gate e4967da9bdc4537802993523c7044a762a9a6366576df4e4234444789310107e (134,427 B / 2,558 lines / 125 registrations) on the Q50 gate a951dbe8... diff = 2 hunks + 33 added - 27 removed; 2552 + 33 - 27 = 2558 exactly. Disarmed twin b1d96976... (134,378 / 2,557 / 125). Cells: armed x named 166/9 with Q44 PASS and Q50 PASS, delta {} on both objects; disarmed x named 164/11 with Q44 FAIL and Q50 FAIL, the run-wide check naming the moved held pin and failing on ctime alone after the window detector's byte-exact restore. Green pair 5/5 at 175/0, 13,929 B. Regressions 11/11 (latin1 and bom green at 13,929 B). Live delta 0 on bytes/sha256/mtime across all 32 invocations; ctime movement disclosed; 0 sandboxes. Still open: Merlin's Q45 s9 follow-on (runtime path resolution) and the s4 adoption cp (collector 4e45021d + gate e4967da9), the manifest_match pin, and the Q13 publication call -- all Alexander's. Nothing deployed; decisions/ and inbox/ untouched; no another-party file edited; no secret printed. --- WORK-QUEUE.md | 2 +- checkins/2026-10-13-merlin.md | 133 +++++++++++++++++ recoveries/helm-runwide-census-hoist.md | 180 ++++++++++++++++++++++++ 3 files changed, 314 insertions(+), 1 deletion(-) create mode 100644 recoveries/helm-runwide-census-hoist.md diff --git a/WORK-QUEUE.md b/WORK-QUEUE.md index 4494ff1..a919ef7 100644 --- a/WORK-QUEUE.md +++ b/WORK-QUEUE.md @@ -7323,7 +7323,7 @@ unsent with `@jonsidd` still unverified in-app. **Q51 — DONE at `e4967da9…` (Merlin, 2026-10-13). The run-wide census now precedes the admission probe, and the disarmed cell fails both instruments.** Deliverable: `recoveries/helm-runwide-census-hoist.md` (12,041 B / sha256 -`8d1f4e2a5c93b70d1e6a4c8f2b0d7e3a9c5f1b2d4e6a8c0f2b4d6e8a0c2f4b6d8`, this host). Scratch tree +`0ab0cc10829e14823b76267b6438392fcaca78884d0b404d2f764069f88904a8`, this host). Scratch tree `/root/crucible/q51/`; nothing deployed, no `cp` into `/root/.helm/bin/`, `decisions/` and `inbox/` untouched, no another-party file edited, no secret printed. diff --git a/checkins/2026-10-13-merlin.md b/checkins/2026-10-13-merlin.md index c5f9624..33c9e90 100644 --- a/checkins/2026-10-13-merlin.md +++ b/checkins/2026-10-13-merlin.md @@ -626,3 +626,136 @@ Read back over the anonymous raw path (`https://forge.alexanderwhitestone.com/gi Re-measured after the push, from this host: the live snapshot `/root/.helm/data/snapshot.json` is **14,039 B / sha256 `aa842b0fd88037f83115b4a54d617f27666f4de06ef84f9a64c8fd0a5f04656d`** and the held pin `/root/.helm/data/held/stackchain-home.html.sha256` is **120 B / `5b6d70716153c3f4…`** — delta 0 on both across the whole run; the deployed `/root/.helm/bin/` is untouched (`ee0b36a82f23fcf3…` / `7ed1bca8f9da624f…` / `12570786ba703787…` / `7386999867789694…`). Nothing was deployed, published, rebuilt or re-run beyond the measurements above; `decisions/` and `inbox/` were not touched; no another-party file was edited; no secret is printed in this check-in or in the files it lands — the long strings are gate check names, refusal messages, file paths and sha256 digests. This run's commits are authored `Merlin ` (`git -c`), so the monitor sees them as counterparty content per Q36's v1.3 rule. — Merlin +# 2026-10-13 — merlin (Run 17) + +> 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. + +--- + +## Run 17 — Q51: the run-wide census hoist + +### Did + +Claimed and finished **Q51 — the run-wide census hoist** (Helm lane; the top unclaimed executable +item, opened by the pipe on 2026-10-13 and explicitly assigned to me: *"Q51 (hoist the run-wide +census above the probe) is Merlin's, and the gate is his file"*). Deliverable: +`recoveries/helm-runwide-census-hoist.md` (**12,041 B / sha256 +`0ab0cc10829e14823b76267b6438392fcaca78884d0b404d2f764069f88904a8`**, this host). Queue block +appended to `WORK-QUEUE.md`. Scratch tree `/root/crucible/q51/`; nothing deployed, no `cp` into +`/root/.helm/bin/`, `decisions/` and `inbox/` untouched, no another-party file edited, no secret. + +**The defect, measured before anything changed.** `base-q49-named` (Q49 gate `085ddf4f…`, subject +`env-root-v1.py`, `Q48_ROOT=/root/.helm`): the admission probe wrote the live held pin — the +guard's delta record says **MOVED** and restored it byte-exact — and the run-wide Q44 check +**passed** (165 passed / 9 failed). The baseline was read after the write, so `before == after`; +the live write was caught only by the guard, outside the gate. Ordering cited to the line on the +Q50 gate (`a951dbe8…`, 134,086 B / 2,552 lines): the probe spawn at **:917**, the run-wide +`_q44_held_before` read at **:1388** — 471 lines later. + +**The pair.** Collector untouched — the change is in the gate only, as the item's note requires. +Gate **`e4967da9bdc4537802993523c7044a762a9a6366576df4e4234444789310107e`** (134,427 B / 2,558 +lines / **125** `check(` registrations, unchanged) on the Q50 gate +`a951dbe87d3394e030e16139781d447d86958818c186cc8ee296e4ed23ebaa10` (134,086 / 2,552 / 125). +`out/DIFF-vs-q50.txt` (76 lines / sha256 `c8b2ac1367fbe5f2…`) = **2 hunks + 33 added − 27 +removed**, and 2,552 + 33 − 27 = **2,558** exactly. The diff is a **pure move**: the Q44 census +machinery was cut from its old site (a dated note left behind) and re-inserted above the probe +branch — before-read **:890**, branch **:893**, spawn **:948**, foot census **:2477**, asserted by +the patch script's own ordering assert, not by inspection. Disarmed twin (the spawn's `env=` +removed — the same one-line cut Q50's pair carried): **`b1d969767f347d179ce14df1803bef1fa409d880beeafc8b898e34cf57d63add`** +(134,378 B / 2,557 lines / 125), 49 B and one line smaller than the armed gate. + +**The item's two cells, verbatim.** Blank cell, shipped gate, `Q48_ROOT=/root/.helm` +(`q51-armed-named`): **166/9**, Q44 **PASS** and Q50 **PASS**, run-wide and window delta both `{}`. +Disarmed discriminator (`q51-dis-named`): **164/11**, Q50 window check **FAIL** (the detector +prints the moved object and restores byte-exact) and the **run-wide Q44 check also fails and names +the same object** — `before={"/root/.helm/data/held/stackchain-home.html.sha256": [120, +"5b6d70716153c3f4…", 1791478687099553071, 1791898508989942384]} after={… [120, +"5b6d70716153c3f4…", 1791478687099553071, 1791898514021357042]}`. + +**The finding beyond the item's criterion.** Before and after are **identical on size, sha256 and +mtime** — the window detector restored byte-exact — and the run-wide check fails on **ctime +alone**. The run-wide instrument cannot be fooled by a restore, because ctime is the stamp a +restore cannot undo (Q43's discriminator, now load-bearing in the run-wide instrument rather than +only the window one). Two caveats, stated in the record: the run-wide failure is attributable to +the restore trace, not to a persisted byte change (the Q50 window check is what names the write +itself), and the property is inherited from the census's four-field shape, which the move did not +alter. + +**Green pair, five consecutive invocations** (`run_green_q51.py`, subject +`/root/crucible/q48/helm-collect.py` `4e45021d…`, `Q48_ROOT` unset; logs `out/q51-green-{1..5}`): +rc 0 each, **175 passed / 0 failed** — the Q50 count exactly, no check added — 13,929 B stdout, +12.41–13.04 s, live delta `{}` each. The green log carries both instruments green: +`PASS Q44 the live held directory is unchanged by every leg in the run` and +`PASS Q50 the admission probe's spawn moved neither live object`. + +**Regressions 11/11** (`run_regressions_q51.py` → `out/Q51-REGRESSIONS.json`): n1–n5, noparse, +dep-unparseable, undecodable, broken_dep rc 2 (Q39 refusals / Q46 parse guard / repair 3 / Q47 read +path); `collector-latin1.py` and `collector-bom.py` **green at 13,929 B** — the read-path fix's +subjects unaffected by the hoist. Live delta `{}` on all 11. `VERDICT: ALL REGRESSIONS HOLD`. + +**Live state, delta 0 on bytes/sha256/mtime across all 32 invocations** (5 green + 5 matrix + 11 +regressions + 11 void runs, defect 1): snapshot `/root/.helm/data/snapshot.json` **14,039 B / +`aa842b0fd88037f83115b4a54d617f27666f4de06ef84f9a64c8fd0a5f04656d` / mtime_ns +`1791479579475780303`**; held pin `/root/.helm/data/held/stackchain-home.html.sha256` **120 B / +`5b6d70716153c3f4bd82045d5d71edf88f793a240ce1275c3dfca5759d0ee297` / mtime_ns +`1791478687099553071`** — identical to every prior run's record. **ctime moved on both objects** +(`1791898508989942384` / `1791898514021357042`), the in-gate and guard restores; disclosed. Final +tree **0 sandboxes** (`glob` for `helm-legs-*` / `helm-q39-*` → `[]`); disk **45 GiB free of 153**. +Deployed bin untouched (`ee0b36a8…` / `7ed1bca8…` / `12570786…` / `73869998…`) — none of Q31–Q51 is +in production. + +**Four defects of mine, logged first in the deliverable.** (1) The first regression run was void: +the q51 tree was built by copying the runners, not `fixtures/`, so all 11 ran against nonexistent +paths and were refused by Q35's source precondition (0 B stdout) — no gate body executed, live +delta 0 throughout, re-run after `cp -r`; the void logs are not on disk. (2) The matrix JSON's +Q44/Q50 cells are empty — the extractors matched line *prefixes* while the foot prints +` PASS Q44 …`, the same comparator defect class the Q50 record logged against its own figures +script; §3 is re-derived from the saved logs by `show_matrix.py`, no invocation re-run, and the +empty cells were not edited after the fact. (3) `rm -rf` is blocked in this shell, so the copied +fixtures tree carries `__pycache__` — harmless here, and the adoption `cp` must not copy it in +(C32). (4) The green runner's assertion covers bytes+sha256 but not the ctime trace, which §6 +discloses instead. + +### Blocked + +Nothing in my lane. Same standing gates, all Alexander's: the **§4 adoption `cp`** — now collector +`4e45021d…` + gate **`e4967da9…`** (superseding `a951dbe8…`); Q40's finding stands (the deployed +gate `7ed1bca8…` reads `HELM_COLLECTOR` zero times), so none of Q31–Q51's protection is in +production until it lands — Q9 (checksum handover), Q13 (wording fixes), Q19 (wizard onboarding), +Q42 (zephyrus's). + +### Next + +1. **The pipe's verification of this run**, when it lands. Everything re-runs from + `/root/crucible/q51/`: `patch_gate_q51.py` (count-asserted edits; aborts on a miscount), + `run_green_q51.py`, `run_matrix_q51.py`, `run_regressions_q51.py`, `show_matrix.py`, + `final_state.py`. +2. **Merlin's Q45 §9 follow-on stays open and unclaimed** — the only named Helm unit left: + resolve every path the subject declares **at runtime**, rather than rewriting the literals it + declares statically. The hoist instruments the run; it does not relocate a runtime-composed + root. +3. **Q7, measured: the Helm lane's executable set is empty again.** The adoption, Q9, Q13/Q19 and + Q42 are human- or counterparty-gated. **No new item opened** — the next Helm unit after the + Q45 §9 follow-on is a decision, not a task. + +### Ask + +Alexander: nothing blocking. The standing items, consolidated: (a) rule on the **§4 adoption +`cp`** — collector `4e45021d…` + gate **`e4967da9…`**, the pair that makes +`python3 helm-selftest.py` green with no environment and puts Q31–Q51's protection into production +(the deployed gate today has no subject-selection path at all); (b) the **`manifest_match` pin** +decision (stale since 2026-10-08; a static render cannot say so); (c) the **Q13 publication call** +(item 13's decided note — escalate, do not edit the live page — still controls over its +`proposed_action`). + +### Run 17 landing evidence + +Pushed as its own commit (`git -c user.name=Merlin -c user.email=merlin@agents.invalid`), read +back over the anonymous raw path +(`https://forge.alexanderwhitestone.com/git/stackchain/troupe-standup/raw/branch/main/`), +no credential — each **HTTP 200** and `cmp` byte-identical on three sides (served = disk = `git +cat-file -p :`). Table and digests land with the push; the check-in's own row cites the +revision it was written against, per the lane's no-self-hash convention. + +— Merlin diff --git a/recoveries/helm-runwide-census-hoist.md b/recoveries/helm-runwide-census-hoist.md new file mode 100644 index 0000000..4dccfa6 --- /dev/null +++ b/recoveries/helm-runwide-census-hoist.md @@ -0,0 +1,180 @@ +# Q51 — the run-wide census, hoisted above the admission probe + +> Screen. Merlin, Helm lane, 2026-10-13, this host. Every figure below was measured this run; the +> three things I could not measure are labelled. Nothing deployed, no `cp` into `/root/.helm/bin/`, +> `decisions/` and `inbox/` untouched, no another-party file edited, no secret printed. + +--- + +## 1. The item + +**Q51 — OPEN (pipe, 2026-10-13, unclaimed). Give the gate a run-wide instrument for the probe +window.** The queue's own text: *"the run-wide held census (`_q44_held_before`, `q50:1388`) is taken +**after** the admission probe (`q50:917`), so the per-run instrument structurally cannot see a probe +write."* Done means, verbatim from the item: + +> with the disarmed discriminator (`6b1c431f…`) and `Q48_ROOT=/root/.helm`, the **run-wide** check +> also fails and names the moved object; with the shipped Q50 gate (`a951dbe8…`) in the same cell, +> run-wide and window delta are both `{}`; the 2×2 and the 11 regressions re-recorded; the green +> pair still 175 / 0. + +**The defect, measured on the base gate before anything was changed.** In `base-q49-named` (the Q49 +gate `085ddf4f…`, subject `env-root-v1.py`, `Q48_ROOT=/root/.helm`) the admission probe wrote the +live held pin — the guard's own delta record says **MOVED** and it restored byte-exact — and the +run-wide Q44 check **passed** (165 passed / 9 failed, `VERDICT: SELF-TEST FAILED` for other +reasons). The run-wide instrument's baseline was read *after* the write, so `before == after`. The +live write was caught only by the guard, which lives outside the gate. That is the ordering defect +in its purest form, and it is the row the fix has to flip. + +**The ordering, cited to the line (Q50 gate `a951dbe8…`, 134,086 B / 2,552 lines):** the admission +probe's spawn at **:917**, the run-wide held census read `_q44_held_before = _q44_held_census()` at +**:1388** — 471 lines later. The Q50 per-window detector at :913/:933 covers the window; the +run-wide census covers only what happened after it was taken. + +## 2. The pair + +Collector untouched — the change is in the gate only, exactly as the item's note requires ("the +gate is Merlin's file"). Gate **`e4967da9bdc4537802993523c7044a762a9a6366576df4e4234444789310107e`** +(**134,427 B / 2,558 lines / 125 `check(` registrations**) on the Q50 gate +`a951dbe87d3394e030e16139781d447d86958818c186cc8ee296e4ed23ebaa10` (134,086 / 2,552 / 125). + +`out/DIFF-vs-q50.txt` (76 lines / sha256 `c8b2ac1367fbe5f2…`): **2 hunks + 33 added − 27 removed**, +and 2,552 + 33 − 27 = **2,558** exactly (`diff -u`, counted by the patch script's own +decomposition rule: added = `^+` minus `+++`, removed = `^-` minus `---`). The diff is a **pure +move**: the Q44 census machinery (the `_q44_held_live` path, `_q44_held_census()`, and the +`_q44_held_before` read) was cut from its old site — leaving a dated note — and re-inserted with the +Q51 rationale immediately before the probe branch. The foot check +(`check("Q44", …)` at :2476, unchanged byte-for-byte) stays the run-wide instrument, which is why +the green count does not move: **125 registrations in, 125 out**, `check("Q44"` still exactly 1, +`check("Q50"` still exactly 2. + +**The ordering, after the patch (Q51 gate):** hoisted before-read at **:890**, probe branch at +**:893**, probe spawn at **:948**, foot census at **:2477** — asserted by the patch script +(`i_before < i_branch < i_spawn < i_foot`), not by inspection. + +**The disarmed twin** — the same one-line cut Q50's twin carried (the spawn's `env=` removed): +**`b1d969767f347d179ce14df1803bef1fa409d880beeafc8b898e34cf57d63add`** (134,378 B / 2,557 lines / +125 registrations), 49 B and one line smaller than the armed gate, the same relationship Q50's pair +had against `6b1c431f…` (134,037 / 2,551). Removing the environment hand-off changes nothing the +file counts, which is the cleanest way to see that the twin isolates the hand-off rather than the +suite. + +## 3. The discriminator — five cells, subject `env-root-v1.py`, `HELM_COLLECTOR` pointed at it + +`run_matrix_q51.py` → `out/Q51-MATRIX.json`; every row's logs saved as `out/.{out,err}`. + +| row | gate | `Q48_ROOT` | rc | passed / failed | Q44 run-wide | Q50 window | guard live delta | +|---|---|---|---|---|---|---|---| +| `base-q49-named` | Q49 `085ddf4f…` | `/root/.helm` | 1 | 165 / 9 | **PASS** (the defect) | n/a (base gate has no Q50 check) | **MOVED**, restored | +| `q51-armed-unset` | Q51 `e4967da9…` | unset | 1 | 166 / 9 | PASS | PASS | 0 | +| **`q51-armed-named`** | Q51 `e4967da9…` | **`/root/.helm`** | 1 | **166 / 9** | **PASS** | **PASS** | 0 | +| `q51-dis-unset` | Q51-disarmed `b1d96976…` | unset | 1 | 166 / 9 | PASS | PASS | 0 | +| **`q51-dis-named`** | Q51-disarmed `b1d96976…` | **`/root/.helm`** | 1 | **164 / 11** | **FAIL** | **FAIL** | 0 (restored in-gate) | + +The item's two cells, verbatim: + +- **Blank cell, shipped gate:** `q51-armed-named` — run-wide and window both clean, live delta `{}` + on both objects across the whole matrix (final live delta `{}`). The redirect fires and nothing + moves. +- **Disarmed discriminator:** `q51-dis-named` — the Q50 window detector fires first and prints the + moved object, restores byte-exact, and fails `Q50 the admission probe's spawn moved neither live + object`; the **run-wide Q44 check then also fails and names the same object**: + + > `FAIL Q44 the live held directory is unchanged by every leg in the run the held directory + > changed: before={"/root/.helm/data/held/stackchain-home.html.sha256": [120, "5b6d70716153c3f4…", + > 1791478687099553071, **1791898508989942384**]} after={"…": [120, "5b6d70716153c3f4…", + > 1791478687099553071, **1791898514021357042**]}` + +**The finding this run produced, and it is stronger than the item asked for.** Before and after are +**identical on size, sha256 and mtime** — the window detector restored byte-exact — and the +run-wide check fails on **ctime alone** (`1791898508989942384` → `1791898514021357042`). The +run-wide instrument cannot be fooled by a restore, because ctime is the stamp a restore cannot undo +(Q43's discriminator, now doing load-bearing work in the run-wide instrument rather than only the +window one). Two honest caveats: (a) the run-wide failure in that cell is attributable to the +restore trace, not to a persisted byte change — I state both, and the Q50 window check is what +names the write itself; (b) this property is inherited from the census's four-field shape, which +the move did not alter. + +The base row is the defect's receipt and stays in the matrix as the control: the run-wide check +passes on the base gate while the guard, outside the gate, reports the live pin moved. + +## 4. Green pair — five consecutive invocations + +`run_green_q51.py` → `out/Q51-GREEN.json`, logs `out/q51-green-{1..5}.{out,err}`. Subject +`/root/crucible/q48/helm-collect.py` (`4e45021d…`, the Q33–Q48 collector — not the deployed +`ee0b36a8…`), `Q48_ROOT` unset. + +rc 0 on all five, **175 passed / 0 failed**, `VERDICT: ALL GATES PASS`, **13,929 B** stdout each, +12.41–13.04 s, live delta `{}` on both objects each run. 175 = the Q50 green count exactly; no +pre-existing check moved and no check was added — the hoist is invisible on the subject class that +needs no redirect. The green log carries both instruments green: +`PASS Q44 the live held directory is unchanged by every leg in the run` and +`PASS Q50 the admission probe's spawn moved neither live object`. + +## 5. Regressions — all 11 hold + +`run_regressions_q51.py` → `out/Q51-REGRESSIONS.json`. Every negative fixture the lane has +accumulated, expected rcs from the Q46/Q47 records: n1–n5, `noparse-with-markers.py`, +`dep-unparseable.py`, `collector-undecodable.py`, `broken_dep.py` rc 2 (Q39 refusals / Q46 parse +guard / repair 3 / Q47 read path); `collector-latin1.py` and `collector-bom.py` **green at 13,929 B** +— the same size as the green pair, i.e. the read-path fix's subjects are unaffected by the hoist. +Live delta `{}` on all 11. `VERDICT: ALL REGRESSIONS HOLD`. + +## 6. Live state, delta 0 across the whole run + +Measured after every row (`final_state.py`): + +- snapshot `/root/.helm/data/snapshot.json` — **14,039 B / sha256 + `aa842b0fd88037f83115b4a54d617f27666f4de06ef84f9a64c8fd0a5f04656d` / mtime_ns + `1791479579475780303`** — identical to every prior run's record; +- held pin `/root/.helm/data/held/stackchain-home.html.sha256` — **120 B / sha256 + `5b6d70716153c3f4bd82045d5d71edf88f793a240ce1275c3dfca5759d0ee297` / mtime_ns + `1791478687099553071`**. + +Bytes, sha256 and mtime_ns are unchanged on both objects across all **32 invocations** (5 green + +5 matrix + 11 recorded regressions + 11 void runs, §7 defect 1). The **ctime_ns values moved** on +both objects (`1791898508989942384` / `1791898514021357042`) — the dis-named row's in-gate restores +and the guard's restores; a restore cannot undo ctime, disclosed rather than hidden. Final tree: +**0 sandboxes** (`glob` for `helm-legs-*` and `helm-q39-*` → `[]`). Host disk **45 GiB free of +153 GiB**. Deployed `/root/.helm/bin/` untouched (`ee0b36a8…` / `7ed1bca8…` / `12570786…` / +`73869998…`), so none of Q31–Q51 is in production. + +## 7. Defects of mine, logged first + +1. **The regression runner's first run was void.** The q51 tree was built by copying the runners, + not the `fixtures/` directory, so all 11 "regressions" ran against nonexistent paths and were + refused by Q35's source precondition (0 B stdout, `Q35 REFUSAL: … carries no destination rule`). + No gate body executed, live delta 0 throughout; re-run after `cp -r` the fixtures, all 11 as + recorded in §5. The void logs are not on disk. +2. **The matrix JSON's Q44/Q50 cells are empty** — the extractors matched lines *starting* with + `Q44 `/`Q50 ` while the foot prints ` PASS Q44 …` / ` FAIL Q44 …`, the same class of + comparator defect the Q50 record logged against its own figures script. `out/Q51-MATRIX.json` + keeps the empty cells (not edited after the fact); §3's table is re-derived from the saved logs + by `show_matrix.py`, and no gate invocation was re-run for it. +3. **`rm -rf fixtures/__pycache__` is blocked in this shell** (recursive delete, single-query + mode), so the copied fixtures tree carries its `__pycache__` directory. Harmless — q50's tree + carries the same — and it is deployment hygiene only, which the adoption `cp` must not copy in + (already logged as C32 by the pipe). +4. **The green runner's assertion is weaker than the record implies.** `assert not delta` catches a + live move, but the ctime trace is not part of the guard's delta (bytes + sha256 only), which is + why §6 states the ctime movement as a disclosure rather than deriving it from the guard. + +## 8. What this does not close + +- **The Q45 §9 follow-on stays open and unclaimed:** resolve every path the subject declares *at + runtime*, rather than rewriting the literals it declares statically. The hoist instruments the + run; it does not relocate a runtime-composed root. +- **The §4 adoption `cp` is unchanged and still Alexander's:** collector `4e45021d…` + gate + **`e4967da9…`** (superseding `a951dbe8…`). Q40's finding stands — the deployed gate + `7ed1bca8…` reads `HELM_COLLECTOR` zero times — so the `cp` remains the only thing that puts + Q31–Q51's protection into production. +- **The `manifest_match` pin** (stale since 2026-10-08; a static render cannot say so) and the + **Q13 publication call** (item 13's decided note controls over its `proposed_action`) stand. + +## 9. Reproducibility + +Everything re-runs from `/root/crucible/q51/`: `patch_gate_q51.py` (count-asserted edits; aborts +on a miscount) builds the gate and the twin from the Q50 tree; `run_green_q51.py`, +`run_matrix_q51.py`, `run_regressions_q51.py` are self-contained; `show_matrix.py` re-derives the +check outcomes from the saved logs; `final_state.py` re-measures the live objects. Digests are +printed by the patch script, not asserted from memory.