Q50 closed: the admission probe's environment -- one rule, one function, two callers

Taken on the pipe's own offer (reply 34, asks 1). The Q48 value-redirect and Q49
default-redirect are factored into _q50_rebuild_env(_target) and called twice: at the
rule's original site for the legs (target the legs' sandbox) and above the admission
probe's spawn for the probe (target the probe's own tree, which exists before the
spawn -- the legs' sandbox does not). The spawn now carries env=, and a per-window
census of both live objects around the spawn is the class's detector: a move is
restored byte-exact in-gate, recorded, printed as a dated Q50 DETECTOR note, and
fails the named check "Q50 the admission probe's spawn moved neither live object".

Measured, all five matrix cells under a digest-verified guard (subject env-root-v1.py):
  base-q49 x named  MOVED  held pin 120 B / 5b6d7071... -> 14 B / 784b9bde..., guard-restored
  q50-armed x unset  delta 0  0 value + 1 default redirect
  q50-armed x named  delta 0  1 value redirect  -- the blank cell, filled
  q50-dis   x unset  delta 0  the probe copy's own Q45 literal relocation covers the
                              default shape; the redirect is load-bearing only in the named cell
  q50-dis   x named  delta 0  the DETECTOR fires: pin moves inside the gate, restored
                              byte-exact, foot prints FAIL Q50 (165/10 vs armed 166/9)
Green 5 x rc 0 / 175 passed / 0 failed (174 + 1) / 13,929 B / live delta 0.
Regressions 11/11 hold (n1-n5, noparse, dep-unparseable, undecodable, broken_dep rc 2;
latin1 and bom green 175/0). Live objects byte-identical to session start on
(bytes, sha256, mtime_ns); deployed bin untouched (ee0b36a8... / 7ed1bca8...,
HELM_COLLECTOR count 0 -- Q40's finding stands). Named follow-on, not fixed here:
hoist the run-wide held census above the probe. Three harness defects of mine logged
first in the record. Nothing deployed; no another-party file edited; no secret.
This commit is contained in:
Merlin committed 2026-10-13 11:26:36 +00:00
1 parent e2a9a6d205
commit 4c6cd50baf
3 files changed
+347

No files matched your search

+21
View File
@@ -6993,6 +6993,27 @@ a digest-verified backup; both live objects byte-identical at session end
configurations. **Finish line:** live delta 0 on both objects in every cell, the fix in a gate revision, and configurations. **Finish line:** live delta 0 on both objects in every cell, the fix in a gate revision, and
the census extended to that spawn (or the probe's own held-write delta measured) so the class has a detector the census extended to that spawn (or the probe's own held-write delta measured) so the class has a detector
as well as a prevention. Nothing in Q50 is deployed; the gate is Merlin's file. as well as a prevention. Nothing in Q50 is deployed; the gate is Merlin's file.
- **Q50 — DONE 2026-10-13 (Merlin, run 16; record `recoveries/helm-probe-spawn-env.md`).** Taken on the
pipe's own offer in reply 34 §Asks 1. Gate **`a951dbe87d3394e0…`** (134,086 B / 2,552 lines / 125 `check(`
registrations) on the Q49 gate `085ddf4f…` (128,948 / 2,445 / 123): `out/DIFF-vs-q49.txt` = 388 lines =
5 hunks + 226 added − 119 removed, 2,445 + 226 − 119 = 2,552 exactly. One rule, one function
(`_q50_rebuild_env(_target)`), two callers — the legs (target `_q43_dir`) and the admission probe (target
`_q39_dir`, hoisted above the spawn because `_q43_dir` does not exist there); the spawn at q50:917 now
carries `env=`; and a per-window census of both live objects around the spawn is the detector (move →
byte-exact in-gate restore + dated `Q50 DETECTOR` note + named failing check). **All five matrix cells
measured** (subject `env-root-v1.py`, each under a digest-verified guard): the base gate with
`Q48_ROOT=/root/.helm` moves the live held pin 120 B / `5b6d7071…` → 14 B / `784b9bde…` (restored by the
guard — the defect reproduced from this host); armed × unset delta 0 (default redirect); **armed × named
— the blank cell — delta 0, the value redirect fires, Q50 check PASS**; disarmed × unset delta 0 (the
probe copy's own Q45 literal relocation covers the default shape — the redirect is load-bearing only in
the named cell); disarmed × named: the detector fires, the pin moves inside the gate, is restored
byte-exact, and the foot prints `FAIL Q50 …` (165/10 against the armed rows' 166/9 — the counts isolate
the check). Green 5 × rc 0 / **175 passed / 0 failed** (174 + 1) / 13,929 B / live delta 0; regressions
11/11 hold (n1–n5, noparse, dep-unparseable, undecodable, broken_dep rc 2; latin1 and bom green 175/0).
Live objects byte-identical to session start on (bytes, sha256, mtime_ns); deployed bin untouched
(`ee0b36a8…` / `7ed1bca8…`, `HELM_COLLECTOR` count 0 — Q40's finding stands). Named follow-on, not fixed
here: hoist the run-wide held census (`_q44_held_before`) above the probe so the per-run instrument sees
the window too.
- **Adoption, now conditional — still Alexander's.** Collector `4e45021d…` + gate `085ddf4f…` is not to be - **Adoption, now conditional — still Alexander's.** Collector `4e45021d…` + gate `085ddf4f…` is not to be
paraphrased as the closure of this class until Q50 lands: as written that gate reports `live delta 0` in every paraphrased as the closure of this class until Q50 lands: as written that gate reports `live delta 0` in every
one of its own channels while writing the live held pin whenever the ambient environment names the live root. one of its own channels while writing the live held pin whenever the ambient environment names the live root.
+110
View File
@@ -504,3 +504,113 @@ Pushed as **`de74527`** (`a7b52e5..de74527`, remote `main`), 3 files / +335. Ano
- `WORK-QUEUE.md` — **823,541 B**, sha256 **`d0bf9080f9ef61570d30cb85a639b56adbf98bceb3ba30adf2b15e142677b8d2`** - `WORK-QUEUE.md` — **823,541 B**, sha256 **`d0bf9080f9ef61570d30cb85a639b56adbf98bceb3ba30adf2b15e142677b8d2`**
- Commit: `git -c user.name=Merlin -c user.email=merlin@agents.invalid commit -F /root/crucible/q49/q49-commit-msg.txt` → `de74527`. - Commit: `git -c user.name=Merlin -c user.email=merlin@agents.invalid commit -F /root/crucible/q49/q49-commit-msg.txt` → `de74527`.
- Scratch tree `/root/crucible/q49/` is **not** committed; the gate copies (`helm-selftest-q49.py` `085ddf4f42ae80ea…`, `-nodefault` `46ba830cd4b9e690…`, `-inproc-off` `d1481ca890fcd543…`, `-disarmed` `63d5409b3502a22e…`) and the fixture (`env-root-v1.py` `76b55b8ab0203c4f…`) stay in the scratch tree, reproducible by `patch_gate_q49.py` and `reproduce_q49.py`. - Scratch tree `/root/crucible/q49/` is **not** committed; the gate copies (`helm-selftest-q49.py` `085ddf4f42ae80ea…`, `-nodefault` `46ba830cd4b9e690…`, `-inproc-off` `d1481ca890fcd543…`, `-disarmed` `63d5409b3502a22e…`) and the fixture (`env-root-v1.py` `76b55b8ab0203c4f…`) stay in the scratch tree, reproducible by `patch_gate_q49.py` and `reproduce_q49.py`.
—
---
## Run 16 — Q50: the admission probe's environment
### Did
Claimed and finished **Q50** (Helm lane; the top unclaimed executable item, opened by the
pipe in `replies/2026-10-13-to-merlin-34.md` §Asks 1 with an explicit offer to take it —
"the gate is your file and I will not edit it, only propose"). Deliverable:
`recoveries/helm-probe-spawn-env.md`. Queue block appended to `WORK-QUEUE.md`. Scratch
tree `/root/crucible/q50/`; nothing deployed, no `cp` into `/root/.helm/bin/`,
`decisions/` and `inbox/` untouched, no another-party file edited, no secret printed.
**The item's three parts, all measured.** (1) The spawn's rebuilt environment: the
Q48 value-redirect + Q49 default-redirect, factored into one function
`_q50_rebuild_env(_target)` with two callers — the legs (original site, target the legs'
sandbox) and the admission probe (target its own tree, hoisted above the spawn because
the legs' sandbox does not exist there). (2) The blank cell (armed × named root): the
base gate with `Q48_ROOT=/root/.helm` moves the live held pin **120 B / `5b6d7071…` → 14 B
/ `784b9bde…`** (guard-restored; the defect reproduced from this host, same payload digest
as my Q49 attribution record); the Q50 gate in the same cell holds **delta 0** with the
value redirect firing (`Q50 NOTE … 1 value redirect(s) … Q48_ROOT /root/.helm ->
<probe tree>`). (3) The detector: a per-window census of **both** live objects around the
spawn — a move is restored byte-exact in-gate, recorded, printed as a dated
`Q50 DETECTOR` note, and fails the named check. Exercised on the disarmed variant
(prevention cut): the pin moves inside the gate, is restored, and the foot prints
`FAIL Q50 the admission probe's spawn moved neither live object` (165/10 against the
armed rows' 166/9 — the counts isolate the check; the 9 fixture-subject failures are
pre-existing scratch-root checks, identical on the base gate).
**The pair.** Collector byte-identical to Q33–Q49's (`4e45021d…`, 29,954 B, measured by
sha256) — every change is in the gate. Gate **`a951dbe87d3394e0…`** (134,086 B / 2,552
lines / **125** `check(` registrations, 2 × `check("Q50"` in mutually exclusive branches)
on the Q49 gate `085ddf4f…` (128,948 / 2,445 / 123, digest verified against the queue's
record); `out/DIFF-vs-q49.txt` = **388 lines = 5 hunks + 226 added − 119 removed**, and
2,445 + 226 − 119 = 2,552 exactly. Disarmed discriminator `6b1c431f…` — one line
different (the spawn's `env=` removed).
**Green pair, five consecutive invocations** (`run_green_q50.py`, subject
`/root/crucible/q48/helm-collect.py`, `Q48_ROOT` unset): rc 0 each, **175 passed / 0
failed**, ALL GATES PASS, 13,929 B stdout, 13.82–15.11 s, digests `b5391473` / `dd0b0989`
/ `d8905c97` / `21e04379` / `aea1ebb3`, live delta 0 on all five. 175 = 174 + 1; no
pre-existing check moved. **Regressions 11/11** (`run_regressions_q50.py`): n1–n5, noparse
(Q46 REFUSAL), dep-unparseable (repair 3), undecodable (Q47 REFUSAL), broken_dep (Q35
REFUSAL) rc 2; latin1 and bom green 175/0; live delta 0 on all 11.
**The full matrix, all five cells** (`run_matrix_q50.py`, subject `env-root-v1.py`, each
under the digest-verified guard `q50_guard.py`): base × named **MOVED** (above); armed ×
unset delta 0 (0 value + 1 default redirect); armed × named delta 0 (1 value redirect —
the blank cell); disarmed × unset delta 0 (the probe copy's own Q45 literal relocation
covers the default shape — the Q50 redirect is load-bearing only in the named cell);
disarmed × named delta 0 with the detector firing (in-gate restore). The `other probe
files` count in the Q39 OK line is the tell: **1** on armed rows (the redirected write
lands inside the probe tree, counted by the containment census), **0** on dis-named (the
write went live, and only the window census sees it).
**Live state, delta 0 across all 27 invocations** (5 green + 5 matrix + 11 regressions +
guard reads): snapshot **14,039 B / `aa842b0fd88037f8…` / mtime_ns `1791479579475780303`**;
held pin **120 B / `5b6d70716153c3f4…` / mtime_ns `1791478687099553071`**. `ctime_ns` moved
twice (the in-gate detector restores + the guard restore of the base row) — disclosed,
not hidden. Deployed bin untouched (`ee0b36a8…` / `7ed1bca8…` / `12570786…` / `73869998…`,
`grep -c HELM_COLLECTOR` on the deployed gate = **0** — Q40's finding stands). Final tree:
0 sandboxes. Nothing of Q31–Q50 is in production.
**Three defects of mine, logged first in the deliverable:** the green runner's first
attempt used the deployed collector (`ee0b36a8…`, the pre-Q33 collector — Q40's finding)
and was refused by Q35; re-run with the Q33–Q48 subject. The matrix/regression extractors
matched lines *starting* with `Q50 ` while the foot prints ` PASS Q50 …`; the Q50 lines
were re-derived from the saved logs (`figures_q50.py`), no invocation re-run. The
disarmed variant still prints the `Q50 NOTE` although the env it names is not the one the
spawn receives — a scratch discriminator's known inaccuracy.
### Blocked
Nothing in my lane. The named follow-on (record §7.2): hoist the run-wide held census
(`_q44_held_before`, taken after the spawn) above the probe so the per-run instrument
sees the window too. Same standing gates, all Alexander's: the **§4 adoption `cp`** —
now collector `4e45021d…` + gate **`a951dbe8…`** (superseding `085ddf4f…`, which the pipe
made conditional on this item) — Q9, Q13, Q19, Q42.
### Next
1. **The pipe's verification of this run**, when it lands. Everything re-runs from
`/root/crucible/q50/`: `patch_gate_q50.py`, `run_green_q50.py`, `run_matrix_q50.py`,
`run_regressions_q50.py`, `figures_q50.py`.
2. **The held-census hoist** above (my named follow-on) and Merlin's Q45 §9 follow-on
both stay open and unclaimed.
3. **Q7, measured: the Helm lane's executable set is empty again.** No new item opened —
the next Helm unit after the two follow-ons is a decision, not a task.
### Ask
Alexander: nothing blocking. The standing items, consolidated: (a) rule on the **§4
adoption `cp`** — collector `4e45021d…` + gate **`a951dbe8…`**, the pair that makes
`python3 helm-selftest.py` green with no environment and puts Q31–Q50's protection into
production; (b) the **`manifest_match` pin** decision (stale since 2026-10-08); (c) the
**Q13 publication call** (item 13's decided note still controls over its
`proposed_action`).
### Run 16 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/<path>`),
no credential — each HTTP 200 and `cmp` byte-identical on three sides (served = disk = `git
cat-file -p <rev>:<path>`). 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.
+216
View File
@@ -0,0 +1,216 @@
# Q50 — the admission probe's environment
> Screen. Merlin, Helm lane, 2026-10-13, this host. Every figure below was measured
> this run from `/root/crucible/q50/`; the two things labelled as defects of mine
> are logged in §7. No secret is printed; the long strings are sha256 digests,
> refusal messages and file paths.
**The item** (queue, opened by the pipe on `replies/2026-10-13-to-merlin-34.md` §Asks 1,
2026-10-13): *"Give the Q39 probe spawn at `helm-selftest-q49.py:719` a rebuilt
environment by the Q48 rule (any ambient value resolving inside the live root → a
sandbox), hoisted above the probe so `_q43_dir` exists; then fill the matrix's blank cell
(armed × named root) and re-record all four configurations. Finish line: live delta 0 on
both objects in every cell, the fix in a gate revision, and the census extended to that
spawn (or the probe's own held-write delta measured) so the class has a detector as well
as a prevention."* Taken from the pipe's own offer in the same reply ("If you would
rather own it, say so and take it"). Nothing deployed; the gate is my file.
---
## 1. The pair
| | base (Q49) | shipped (Q50) |
|---|---|---|
| gate | `helm-selftest-q49.py` | `helm-selftest-q50.py` |
| sha256 | `085ddf4f42ae80ea8b45e67ae157d0a8cf29d429fff8014dc319cd32c7f85b3c` | `a951dbe87d3394e030e16139781d447d86958818c186cc8ee296e4ed23ebaa10` |
| bytes / lines | 128,948 / 2,445 | 134,086 / 2,552 |
| `check(` registrations | 123 | **125** (2 × `check("Q50"`, mutually exclusive branches — one registers per run) |
- Base digest **verified by `sha256sum`** against the queue's recorded `085ddf4f…` — the
file this patch started from is the file the pipe verified.
- Collector **byte-identical to Q33–Q49's**: `/root/crucible/q48/helm-collect.py`,
29,954 B / sha256 `4e45021d80395d3f…` — every change is in the gate.
- Fixture: `/root/crucible/q50/env-root-v1.py`, 5,634 B / `76b55b8ab0203c4f…` — the Q49
witness fixture (default root `/root/.helm`, the live root).
- `out/DIFF-vs-q49.txt`: **388 lines = 5 hunks + 226 added − 119 removed**; the lane
decomposition, counted by `diff | grep -c`: 2,445 + 226 − 119 = **2,552** exactly.
- Disarmed discriminator: `helm-selftest-q50-disarmed.py`, 134,037 B /
`6b1c431fab857895078dedad11be66bfc6cd2653f6523bd0b9bd053ecd675bda` — one line different
from the shipped gate (the probe spawn's `env=` removed).
- Patch: `patch_gate_q50.py` — six count-asserted edits (each anchor must occur exactly
once or the run aborts), edit log in §9.
## 2. What changed — one rule, one function, two callers
1. **The rebuild is factored.** `_q50_rebuild_env(_target)` carries the Q48
value-redirect and the Q49 default-redirect, parameterised by sandbox root, and
returns `(env, redirects, default_redirects)`. It is called **twice**: at the rule's
original site for the legs (target `_q43_dir`, the legs' sandbox) and at a new site
above the probe for the admission probe (target `_q39_dir`, the probe's own tree).
One rule, two targets — the two paths cannot drift because they are the same code.
2. **The hoist.** The env machinery (`import os as _q48_os`, the two `ast` walkers, the
names/defaults computation, the live-root predicate) moved from ~line 862 to above the
probe branch (~line 685), because the probe spawns ~240 lines above the rebuild and
before Q50 it inherited the gate's ambient environment. The target had to be a
parameter because `_q43_dir` — the legs' sandbox — is created ~115 lines *after* the
probe; the probe's redirect target is its own tree, which exists before the spawn.
3. **The prevention.** The spawn (`helm-selftest-q50.py:917`, q49:719) now carries
`env=_q50_probe_env`: every variable the subject reads whose **ambient value**
resolves inside the live root is redirected into the probe tree, and every name whose
**default literal** resolves there is redirected when the ambient value is absent.
4. **The detector.** `_q50_census()` reads both live objects — the snapshot and every
file in the live held directory, bytes carried — on **both sides of the spawn**. A
move is restored byte-exact (`_q50_restore_census`, mtime re-stamped from the before
value), recorded in `_q50_probe_moves`, printed as a dated `Q50 DETECTOR` note on
stderr, and fails the named check `Q50 the admission probe's spawn moved neither live
object`. When no probe runs (no `SNAPSHOT_PATH =` assignment) the check is registered
vacuously so the suite carries the same census on every subject class.
## 3. The 2×2, all four cells plus the base-gate control
Subject `env-root-v1.py`; every row under the digest-verified guard `q50_guard.py`
(refuses to restore unless the backup digest matches); a live move is restored by the
guard and recorded. `Q48_ROOT` unset means removed from the environment, not blank.
| row | gate | `Q48_ROOT` | rc | passed / failed | guard live delta | Q50 check | probe-window redirects |
|---|---|---|---|---|---|---|---|
| `base-q49-named` | q49 `085ddf4f` | `/root/.helm` | 1 | 165 / 9 | **MOVED** — held pin 120 B / `5b6d7071…` → **14 B / `784b9bde…`** (`'q48-env-pin\n'`, the fixture's own payload), restored by the guard | — (no such check) | n/a |
| `q50-armed-unset` | q50 `a951dbe8` | *unset* | 1 | 166 / 9 | 0 | **PASS** | 0 value + **1 default**: `Q48_ROOT /root/.helm -> <probe tree>` |
| **`q50-armed-named`** | q50 `a951dbe8` | `/root/.helm` | 1 | 166 / 9 | **0** | **PASS** | **1 value**: `Q48_ROOT /root/.helm -> <probe tree>` — **the blank cell, filled** |
| `q50-dis-unset` | disarmed `6b1c431f` | *unset* | 1 | 166 / 9 | 0 | PASS | 0 value + 1 default (printed, unused) |
| `q50-dis-named` | disarmed `6b1c431f` | `/root/.helm` | 1 | **165 / 10** | 0 (**in-gate** restore) | **FAIL — the detector fired** | 1 value (printed, unused) |
- **The defect on the base gate, reproduced from this host.** `base-q49-named` moved the
live held pin exactly as reply 34's strace recorded: the same 14 B payload, digest
`784b9bded20e9e22…` — the content my own Q49 attribution file measured
(`out/Q49-ATTRIBUTION.json`). The guard restored byte-exact.
- **The blank cell.** Armed × named root: live delta 0, the value redirect fires
(`Q50 NOTE … 1 value redirect(s), 0 default redirect(s) into <probe tree>: Q48_ROOT
/root/.helm -> /root/crucible/q50/helm-q39-cyl_yd_e`), the Q50 check PASSes. On the
base gate the same cell moves the pin; one line apart, the outcome flips.
- **The detector's exercise.** Dis-named: the probe spawn inherited the ambient
`Q48_ROOT=/root/.helm` (prevention cut), the census saw the held pin go
120 B / `5b6d7071…` → 14 B / `784b9bde…` *inside the gate*, restored byte-exact at
`2026-10-13T11:21:39.333132+00:00`, and the foot printed
`FAIL Q50 the admission probe's spawn moved neither live object the probe-window
census moved: […before [120, 5b6d7071…, 1791478687099553071], after [14,
784b9bde…, 1791890499488341075]…]` (`out/q50-dis-named.out` line 176). The guard's
post-run compare saw delta 0 — the in-gate restore did its job.
- **The counts isolate the check.** The 9 failures on the fixture subject are
pre-existing scratch-root checks, identical on the base gate (165/9). The arithmetic
is exact: base 165/9 → armed 166/9 (the Q50 check added and passing) → dis-named
165/10 (the same check failing). Nothing else moved.
- **`other probe files` is the tell** (`Q39 OK` line, stderr): armed rows print
`other probe files=1` — the redirected write lands inside the probe tree, where the
containment census counts it (the disclosed side effect); dis-named prints
`other probe files=0` — the write went live, and only the window census sees it. The
count now distinguishes where the write landed, which the Q39 message could not say
before.
## 4. Green pair — five consecutive invocations, real collector
`run_green_q50.py`, subject `/root/crucible/q48/helm-collect.py` (`4e45021d…`),
`HELM_COLLECTOR` set, `Q48_ROOT` unset. rc 0 each, **175 passed / 0 failed**,
`VERDICT: ALL GATES PASS`, **13,929 B** stdout, 13.82–15.11 s; stdout digests
`b53914732ac2` / `dd0b0989ab12` / `d8905c971857` / `21e04379e825` / `aea1ebb31fe9`
(identical size; digests differ only in the gate's own randomised names). **175 = 174
(Q49) + 1 (Q50)**; no pre-existing check moved. Live delta 0 on all five;
`out/Q50-GREEN.json`. Leftovers after the set: **0** (`find … -name 'helm-legs-*' -o
-name 'helm-q39-*'` → 0).
## 5. Regressions — every accumulated negative, on the shipped Q50 gate
`run_regressions_q50.py`, 11 fixtures, `Q48_ROOT` unset: **11/11 hold, live delta 0 on
all 11.**
| fixture | rc | refusal / result |
|---|---|---|
| `n1-incident-shape.py` … `n4-comment-only.py` | 2 | `Q39 REFUSAL: … carries no WORKING destination rule` |
| `n5-live-literal.py` | 2 | Q39 refusal (575 B stdout, the legs' own refusal) |
| `noparse-with-markers.py` | 2 | `Q46 REFUSAL: … does not parse - invalid syntax` |
| `dep-unparseable.py` | 2 | `Q39 REFUSAL` — repair 3 (the dependency's SyntaxError raises with a traceback) |
| `collector-undecodable.py` | 2 | `Q47 REFUSAL: … cannot be read the way CPython reads a source file` |
| `broken_dep.py` | 2 | `Q35 REFUSAL: … carries no destination rule` |
| `collector-latin1.py` | 0 | **175 / 0**, ALL GATES PASS |
| `collector-bom.py` | 0 | **175 / 0**, ALL GATES PASS |
## 6. Live state, delta 0 across the whole run
27 invocations under census (5 green + 5 matrix + 11 regressions + the guard's own
reads). Session start = final, on (bytes, sha256, mtime_ns):
- snapshot `/root/.helm/data/snapshot.json` — **14,039 B / `aa842b0fd88037f8…` /
mtime_ns `1791479579475780303`**
- held pin `/root/.helm/data/held/stackchain-home.html.sha256` — **120 B /
`5b6d70716153c3f4…` / mtime_ns `1791478687099553071`**
`ctime_ns` moved on both objects (to `1791890492529766997` / `1791890499523343963`) —
the two in-gate detector restores' traces plus the guard restore of the base row.
A restore cannot undo ctime (Q43); the traces are the durable record and are disclosed,
not hidden. **Deployed bin untouched**: `/root/.helm/bin/` — `ee0b36a82f23fcf3…`
(helm-collect.py), `7ed1bca8f9da624f…` (helm-selftest.py), `12570786ba703787…`,
`7386999867789694…`; `grep -c HELM_COLLECTOR` on the deployed gate = **0**, so Q40's
finding stands and none of Q31–Q50 is in production. Scratch tree `/root/crucible/q50/`
is not committed; everything re-runs from `patch_gate_q50.py`, `run_green_q50.py`,
`run_matrix_q50.py`, `run_regressions_q50.py`, `figures_q50.py`.
## 7. Findings and limits
1. **The unset shape is covered twice.** Dis-unset (prevention cut) still holds delta 0:
the probe copy's own Q45 literal relocation covers the default literal, so the Q50
redirect is load-bearing only in the **named** cell. Both arms measured — the
prevention is not redundant, it is the only cover for the shape that actually moved
the pin.
2. **The detector is per-window, not per-run.** This run's once-per-run held census
(`_q44_held_before`, q49:1281 → q50:1388) is taken *after* the spawn and structurally
cannot see a probe write — the ordering defect that made the class invisible. The
permanent fix is to hoist that census above the probe, or to keep the window census
as the instrument. Named, not fixed: out of Q50's one-line scope, and the next
Helm-side follow-on.
3. **Citation shift from the hoist.** The probe spawn is q49:719 → **q50:917**; the
in-process `--deploy` branch is q49:2292–2325 → **q50:2399–2432** (`_rc =
hcg.main()` at q50:2432; the `# --dry-run at the live destination is NOT gated`
comment at q50:2390, q49:2283); the legs' env hand-off q49:1117 → **q50:1310**.
Reply 34's corrected citations describe the same regions.
4. **What is not covered** (carried from Q48/Q49's limits, unchanged): a variable name
the `ast` inventory cannot see (dynamically constructed), an argv-composed root passed
to the probe (the probe passes no argv), and a subject that reaches the live path
through a mechanism neither environment nor literals express. The parse, read-path and
redirect classes are untouched: 11/11 regressions hold.
5. **Harness defects of mine, logged first.** (a) The green runner's first attempt used
the deployed collector `/root/.helm/bin/helm-collect.py` (`ee0b36a8…` — the pre-Q33
collector, Q40's finding) and was refused by Q35's source precondition; re-run with
the Q33–Q48 subject (`4e45021d…`). (b) The matrix/regression extractors matched lines
*starting* with `Q50 ` while the foot prints ` PASS Q50 …`; the Q50 check lines were
re-derived from the saved logs by `figures_q50.py` — no invocation re-run. (c) The
disarmed variant still prints the `Q50 NOTE` although the environment it names is not
the one the spawn receives — a scratch discriminator's known inaccuracy, disclosed.
6. **Shared live objects, re-measured.** The queue's standing note (the live objects and
scratch trees need a lock or separate roots) stands: this session's ctime traces are
this run's own restores, and no foreign writer appeared in this tree's window. The
fixture's default live root means any unguarded invocation of `env-root-v1.py` writes
live; every row here either set `Q48_ROOT` explicitly or ran under the guard.
## 8. Verdict
**Q50 DONE.** The class the pipe attributed to the probe spawn is closed for both
shapes: prevention (the spawn's rebuilt environment) measured delta 0 in the blank cell
and in every other armed configuration, the detector measured firing and restoring in
the one configuration where prevention is cut, and the 2×2 now isolates the two
mechanisms row by row. The adoption pair the pipe made conditional on this item is now
collector `4e45021d…` + gate **`a951dbe8…`**.
## 9. Evidence
| claim | command / artifact | output |
|---|---|---|
| base gate identity | `sha256sum /root/crucible/q49/helm-selftest-q49.py` | `085ddf4f42ae80ea…` (matches the queue) |
| new gate identity | `sha256sum helm-selftest-q50.py`; `wc -l` | `a951dbe87d3394e0…`, 134,086 B / 2,552 lines |
| diff decomposition | `diff -u q49 q50 \| grep -c` | 388 = 5 hunks + 226 − 119; 2,445+226−119 = 2,552 |
| patch integrity | `python3 patch_gate_q50.py` | 6 edits, each anchor count 1; `py_compile` rc 0 ×2; 2 × `check("Q50"` |
| green | `python3 run_green_q50.py` | 5 × rc 0, 175/0, 13,929 B; `VERDICT: ALL GREEN` |
| matrix | `python3 run_matrix_q50.py` | 5 rows; base MOVED 120→14 B restored; armed cells delta 0; dis-named Q50 FAIL, in-gate restore |
| regressions | `python3 run_regressions_q50.py` | 11/11 rc as expected, delta 0; `VERDICT: ALL REGRESSIONS HOLD` |
| live state | `python3 q50_guard.py snapshot / compare / restore` | session start = final on bytes/sha/mtime; FINAL delta `{}` |
| deployed bin | `sha256sum /root/.helm/bin/*`; `grep -c HELM_COLLECTOR helm-selftest.py` | `ee0b36a8…` / `7ed1bca8…` / `12570786…` / `73869998…`; count **0** |
| artifacts | `out/Q50-{GREEN,MATRIX,REGRESSIONS,FIGURES}.json`, `out/DIFF-vs-q49.txt`, per-row `.out`/`.err` | on disk, re-runnable |