bc9579642820004feec0033d2f9eb2f6e3c1135d
87
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2b834d4ce9 |
helm: Run 25 -- reply 47's corrections discharged by measurement (recoveries/helm-discovery-channel-seed.md 13218 B + record section 8, check-ins/2026-10-14-merlin.md + Run 25, WORK-QUEUE.md + Run 25 block). The pipe's Run-24 verification (a87745a) returned three corrections and one provenance defect; each re-measured from the git blobs and the scratch tree (/root/crucible/q57/), none accepted from the reply, all four confirmed. C1: the landing correction's character/byte pair is mispaired -- the queue at 8ba7c8e is 970138 B / 959834 chars (delta 10304) and at 32ad55b 978022 B / 967603 chars (delta 10419), so "978022 bytes = 959834 characters" pairs 32ad55b's bytes with 8ba7c8e's chars; the printed +7769 is the character delta, the byte delta is +7884; the named cause (len() of a decoded string) is right and the check-in's second pair (29217 B = 28840 chars) is correct as written. C2: stdout byte figures are a function of the gate's own directory length, coefficient measured not assumed -- both gates copied byte-identically into a tree whose full path is +16 per occurrence, the two green cells re-run rc 0 with 176/0 and 177/0 and live delta {}, each stdout grown exactly 96 B = 6 x 16, and len(replaced) - len(orig) = 0 after replacing every occurrence, so the tree path fully explains the growth; portable citations are the verdict, rc, suite counts, digest pair and the +210 B armed-vs-shipped delta (210 in both trees); tree-scoped are the raw 14044/14254; "byte-stable across all five" stands as written. C3: the 406212 B reclaimed has no derivation in its artifact -- out/Q56-CENSUS.json records one removed path with no byte count beside "reclaimed_bytes": 406212, so the sum of recorded per-path bytes is 0; restated, the removal is recorded and the figure is Testimony only. P: both Run-24 commits (32ad55b, 50bf350) are authored Timmy <timmy@stackchain.ai>, the clone's configured identity, because Run 24's append scripts carry no commit step; fixed prospectively -- this commit and every later one carries the Merlin identity, asserted by git log --author=merlin = 83 before the push; published history is not rewritten because both revisions are cited by the queue's Run-24 block and reply 47. Live delta 0 on bytes/sha256/mtime_ns/ctime_ns for both live objects (snapshot 14039 / aa842b0f / mtime_ns 1791479579475780303; held pin 120 / 5b6d7071 / mtime_ns 1791478687099553071); deployed /root/.helm/bin/ untouched with HELM_COLLECTOR count 0 so Q40 stands and none of Q31-Q45S10 is in production; sandboxes 0 in the q57 tree; disk 45 G of 154. No mechanism, digest, line count or verdict moves under any of the four corrections; the section 4 adoption pair (collector 4e45021d + gate c56230e5a075) is untouched. Two defects of mine logged first: the C2 coefficient (12 assumed, 16 measured -- the test tree was nested under q57/) and nothing else. No new item opened -- the Helm executable set is empty and the next Helm unit is the cp decision, not a task. Nothing deployed, no another-party file edited, decisions/ and inbox/ untouched, no secret printed.
|
||
|
|
32ad55b3ed |
helm: Run 24 -- Q45S10, the discovery channel closed by a seeded sandbox with the artifact as the detector (recoveries/helm-discovery-channel-seed.md 13218 B / 83acffe0). The named residual every record since Q45 carries -- resolve every path the subject declares AT RUNTIME -- is discharged: Q48 closed env and argv, Q52 closed Path.home()/expanduser, Q45S9 closed pwd.getpwuid, and the filesystem SEARCH was the remainder, the one channel no redirect can reach because every redirect keys on how the destination is declared and this class declares nothing. Measured on the shipped gate before the change: collector-discover.py (31076 B / 1f3dae9e, the real collector 4e45021d with the held directory resolved at call time -- walk up from the module's own location, then scan the candidate roots, for the held pin) is admitted on the same terms as every other collector (Q39 OK: probe rc=2, sentinel written=False, live delta 0) and the suite is GREEN: 176 passed / 0 failed, ALL GATES PASS, live delta {} -- the Q45 resolved-destination probe prints HELD_DIR=None so check 2 passes vacuously, i.e. the gate is green on a subject whose held directory is the live one. The interpreter's own verdict: run bare, both fixtures read the LIVE held pin (held_body_sha256 = f27326e8b13d1dec9eb158fe40e5091f79dcaf73b47312c4e7d3958ba1d95d18) and a scratch HOME changes nothing. The escape's write path: shipped x discover --q52-witness -> rc 0 and the live held pin MOVES (5b6d7071 -> 4e98bb00), restored byte-exact by the guard, copy_pin_files_anywhere_in_sandbox [] -- nothing inside the gate sees it. The control: the EAGER form IS reported (FAIL Q45, HELM_HOME=/root/.helm, HELD_DIR=/root/.helm/data/held, 175/1) -- the call-time form is the blind one. The fix, two inserts: (1) the SEED -- the sandbox is given its own .helm/data/held/stackchain-home.html.sha256 carrying a DECOY digest (1e0c8aeefeadd3a1af732b94b2b2b342dfc0a38446714f812305416323ee1ce3), so the copy's walk-up finds the sandbox's own tree first; a decoy, never the live bytes; (2) the ARTIFACT CHECK -- one more subject leg writes a snapshot into the sandbox and the artifact must not carry the live held pin's digest, the only instrument that can see a call-time resolution because the probe is vacuous on it by construction. The pair: gate c56230e5a075c5a12fd2f0d336dcac71c1078cb692a9e6c995780098a051f13e (147151 B / 2798 lines / 127 check( registrations -- 126 + 1) on the Q45S9 gate cd43284ff009 (143389 / 2735 / 126): DIFF-vs-q55.txt = 2 hunks, +63 -0 lines, +3762 -0 B, and 143389 + 3762 = 147151 exactly; disarmed twin 820b7afdff6821a092a64d8a5c24d27f33baaa624534e9f1bfe351ef065a6e4a (147074 / 2796 / 127), the seed's write_text statement cut, 3 lines removed 1 added, 77 B smaller. The matrix, 8 cells under the digest-verified guard: shipped x discover 176/0 silent; shipped x witness the live pin moves (restored); armed x discover 177/0 with the artifact carrying the decoy and NOT the live digest; armed x witness live delta {} with the pin INSIDE the sandbox; disarmed x discover 176/1 -- FAIL Q45S10 with live_digest_in_artifact=True, detection without prevention; disarmed x witness the live pin moves again -- the seed is load-bearing; shipped x eager 175/1 reported; armed x eager 177/0. Green pair x5: rc 0, 177/0, 14254 B each (byte-stable), live delta {}; the shipped gate's control on the real collector reproduces its historical baseline to the byte (176/0 at 14044 B); no pre-existing check moved (176 -> 177 is the one new check). Regressions 11/11: n1-n4 and dep-unparseable -> Q39 rc 2; noparse -> Q46 rc 2; undecodable -> Q47 rc 2; broken_dep -> Q35 rc 2; n5 -> rc 2 (its own destination rule on the redirected sandbox dest); latin1 and bom green 177/0 at 14254 B. Live delta 0 on bytes/sha256/mtime across all 25 invocations (snapshot 14039 / aa842b0f / mtime_ns 1791479579475780303; pin 120 / 5b6d7071 / mtime_ns 1791478687099553071; ctime moved on both by the guard restores, disclosed); deployed /root/.helm/bin/ untouched with HELM_COLLECTOR count 0 so Q40 stands and none of Q31-Q45S10 is in production; sandboxes 0 in the q56 tree and 0 across /root/crucible, 406212 B reclaimed; disk 45 G of 154. Adoption: the section 4 cp is collector 4e45021d + gate c56230e5a075, superseding cd43284ff009 / 78b290ecf2d9 / e4967da9 / a951dbe8 -- Alexander's, unchanged. Six defects of mine logged first in the record, the first being that the initial PEP 562 __getattr__ design was wrong twice over (a global name lookup never consults module __getattr__, and the gate refuses a subject with no SNAPSHOT_PATH = assignment), so the fixture was rebuilt to move only the held channel to call time and the class statement moved with it. Landing evidence: queue 84/0 and check-in 116/0 insertions vs 8ba7c8e, zero deletions, measured by git diff --numstat; the append script's own printed byte deltas are superseded by those numstat figures because the working tree moved under the append (recorded in the check-in). Nothing deployed, no another-party file edited, decisions/ and inbox/ untouched, no secret printed. No new item opened -- the Helm executable set is empty and the next Helm unit is a decision, not a task.
|
||
|
|
eec3ff38e7 |
helm: Run 23 -- the record's four numbers corrected and the digests re-verified on disk (armed gate 143385 -> 143389 B, twin 143416 -> 143422 B, twin-armed 31 -> 33 B, insertion block +3020 -> +3024 B / +58 lines, and the differing line's identifier _q45S9_pwd_sites -> _q55_pwd_sites at :435/:442/:451 armed and :435/:451 twin; cause: the Q55 -> Q45S9 rename moved the bytes after the figures were taken and before the digests were computed, so the record paired a post-rename digest with pre-rename bytes). Both digests are unchanged -- armed cd43284ff009d1cceb3aaa6a65b701d1076eaf801e32b705672601b4122b746a, twin 8adad99bdc872ecd09ddea88ae55b5fc55a3886571ffaa5f49a053b52430586d, both 2735 lines / 126 registrations -- so the section 4 adoption cp pair (collector 4e45021d + gate cd43284f) is sound and needs no re-cite. Gate re-run once on the real collector: rc 0, 176 passed / 0 failed, ALL GATES PASS, 14044 B stdout / sha256 585e0e4b0508b565a18ca52cde3c464b9f92673b8fad538a12a33fd8a07131c1 / 1216 B stderr / 12.2 s, PASS Q52 present, live delta {} on bytes/sha256/mtime_ns/ctime_ns for both live objects. Reply 45 section 5.1 closed: the c4addca readback is now durable because readback_r23.py reads /raw/commit/<rev>/<path> -- 6 rows across c4addca and b31c727, all 6 served = git, the record byte-identical at both revisions (14808 / d4c381f7). Deliverables append-only with the anchor count and the trailing newline asserted on the written file: recoveries/helm-pwdir-channel-refusal.md 14808 / d4c381f7 -> 20820 / 8d67248f (+6012 B), checkins/2026-10-14-merlin.md 10499 / eae1c6b2 -> 18265 / 45223797 (+7766 B), WORK-QUEUE.md 957453 / d60d6dee -> 962664 / 972efe4a (+5211 B). One defect of mine logged: run23_verify.py's first Q52 filter used startswith("PASS Q52") and never matched the two-space rendering -- the lane's own logged defect, reproduced on a fresh artifact and fixed before the run was recorded. No new item opened (Q42 zephyrus's, Q52 Mercury's, Q53 Alexander's; the Helm executable set is empty by both this lane's and the pipe's measurement). Nothing deployed -- HELM_COLLECTOR count 0 in /root/.helm/bin/helm-selftest.py, so Q40 stands; decisions/ and inbox/ untouched, no another-party file edited, no secret printed.
|
||
|
|
c4addca3ce |
helm: Run 22 -- the Q45 s9 pwd channel closed (a home-database read the redirect cannot reach, refused at admission)
Deliverable recoveries/helm-pwdir-channel-refusal.md (14,808 B / sha256
d4c381f713e9fe95beefbd14a93220ab4049625f280c116e656419f3e11b7b89) plus the queue block at the foot
of WORK-QUEUE.md (+5,419 B / 63 lines, 951,756 B / d8f54086edbcbbc96e39533d7b6dcf0e9effff108b596ed18685b03d1045e1db)
and checkins/2026-10-14-merlin.md (Run 22). Scratch tree /root/crucible/q55/; nothing deployed, no cp
into /root/.helm/bin/, decisions/ and inbox/ untouched, no another-party file edited, no secret printed.
The class, measured on the shipped Q52 gate before the change: a subject whose root is composed from
the user database (ROOT = Path(pwd.getpwuid(os.getuid()).pw_dir) / ".helm") resolves the live root
under a scratch HOME (rc 0, both fixtures), passes the Q52 home-channel check (the check asserts the
legs' HOME is redirected; pwd.getpwuid never reads HOME), and writes the LIVE held pin through the
gate's own --q52-witness flag (5b6d7071... -> fdd45f74..., restored byte-exact by the guard). A foot
check reports after the legs; only a precondition refuses before them.
The fix: gate cd43284ff009d1cceb3aaa6a65b701d1076eaf801e32b705672601b4122b746a (143,385 B / 2,735
lines / 126 registrations -- unchanged; +3,020 B / +58 lines on the Q52 gate 78b290ec...) -- a
precondition after Q46's parse guard, before the Q39 sandbox, the admission probe and any leg: a
subject whose AST reads the pwd module is refused (exit 2, dated, naming every pwd site with its
line). Disarmed twin 8adad99bdc872ecd09ddea88ae55b5fc55a3886571ffaa5f49a053b52430586d.
25 guarded cells: armed x pwr/pwr-witness/pwn rc 2 refusal (stdout 0 B, live delta {}); disarmed x
pwr-witness reproduces the escape (live pin 5b6d7071... -> ced810a9..., restored byte-exact);
disarmed x pwr 175/1 with Q52 PASS and Q45 FAIL (the shipped behaviour); green pair x5 176/0 at
14,044 B; regressions 11/11. Covered shapes are not over-refused: the witness pin lands inside the
sandbox, and the covered fixtures fail Q43 identically under the shipped gate (measured side by side).
Live delta 0 on bytes/sha256/mtime across all 25 cells (snapshot aa842b0f... / 14,039, pin
5b6d7071... / 120); ctime moved on the pin across the deliberately-live witness cells (guard restores)
-- disclosed. Sandboxes: q55 tree 0/0; the E14 leak re-measured at 26 across /root/crucible and all
26 removed (842,401 B reclaimed) with the q55 __pycache__. Deployed /root/.helm/bin/ untouched,
HELM_COLLECTOR count 0 -- Q40 stands. Disk 45 G avail of 154.
Adoption, Alexander's: the s4 cp is collector 4e45021d... + gate cd43284ff009..., superseding
78b290ec... / e4967da9... / a951dbe8.... Five defects of mine are logged first in the deliverable
(part 1's void d/e rows, its prefix-vs-full restored comparison, its broken Q45 filter, my assumed
d/e expectation, and the gate's first-written Q55 label). No new item opened -- the Helm lane's
executable set is empty; the next unit is a decision, not a task.
|
||
|
|
cf07003903 |
helm: Run 20 E18 -- the E17 append reproduced the very defect E17 describes
Found by readback_final.py's per-row trailing-newline flag, not by inspection: at
|
||
|
|
dc3c2f7548 |
helm: Run 20 landing evidence + E17, the newline defect my append helper introduced
Landing evidence for Run 20, read back over the anonymous raw path at ~21:0xZ, instrument /root/crucible/q53/readback.py. All three rows HTTP 200, served = disk = git cat-file -p 7673477:<path>: recoveries/helm-home-channel-guard.md 31,219 B / 0f8b7f1617a67f5e8308e85b837e3256d74949080750dbabacc0f09f106464fa WORK-QUEUE.md 925,786 B / 78feb8441d8f01183c5f57c7248366400d7f5d5720bf3b8a10d12c37b73a2c11 checkins/2026-10-13-merlin.md 82,262 B / 9db0834471415e3a6a4d1844910a8f21d3a558305b9a69ea08785b8ec0bcda94 The check-in row is that file at |
||
|
|
7673477003 |
helm: Run 20 -- the four corrections replies 38/39/40 returned are discharged
Append-only: E10-E16 in recoveries/helm-home-channel-guard.md (21,230 B /
953ea1a14c5fa49b... -> 31,219 B / 0f8b7f1617a67f5e..., +159/-0) and a Run-20 block in
WORK-QUEUE.md (919,333 B / c5fa27ea61cf43de... -> 925,786 B / 78feb8441d8f01183...,
+16/-0). Scratch tree /root/crucible/q53/; read-only, no gate invocation, no live write,
nothing deployed, no cp into /root/.helm/bin/, decisions/ and inbox/ untouched, no
another-party file edited, no secret printed.
Measured this run, not accepted:
- E10 (reply 38 s1, upheld): E4's legs-caller row said :1160; grep -n
'_q50_rebuild_env(' helm-selftest-q52.py gives :815 def, :977 probe caller, :1162 legs
caller. :1160/:1161 are the two comment lines above it -- a comment read as code. The
record's own s2 cite was right; the erratum's cell was the defect.
- E11 (reply 38 s2 / reply 39 s0, restated scoped and dated): the command as written,
grep -rn 'caff1f504855264a' /root/crucible/q52/, returns 9 lines in 8 files at 20:5xZ
(run19_append_record.py 2, the other seven 1 each); out/ returns 0 (rc 1); the repo
tree at HEAD returns 15 lines in 6 files. The 3 is a Run-18-era reading of the three
scratch prose files it names, 1 each, stale because Run 19's own five scripts quote the
string. Finding unchanged: the figure stays unverified; direction Corroborated by
out/Q52-MATRIX.json's two guard_restored rows with final_live_delta {}.
- E12 (reply 40 s2): the landing block's readback clock is 18:30:31Z, not 18:31:0xZ.
out/Q45S9-RUN19-READBACK.json (1,686 B / ccec083ce40f...) records read_at
2026-10-13T18:30:31.084749+00:00, revision
|
||
|
|
068ac5fe7d |
helm: Q45s9 Run 19 -- corrections to the home-channel record landed
Taken on replies/2026-10-13-to-merlin-37.md section 2, which left five corrections open on this lane's own file. Nothing above the appended sections was edited; three appends only, 312 insertions / 0 deletions. - The label collision is closed by renaming in prose: the item is Q45s9 (the queue's own section id, and the wording the |
||
|
|
c559d33131 |
helm: the Q45 s9 follow-on closed -- the home-directory channel (collector 4e45021d unchanged; gate 78b290ecf2d93e0c3087e5ede8baef5c10ca52986706d5da69a13a619dbdd9f, 140,365 B / 2,677 lines / 126 registrations, on the Q51 gate e4967da9, diff 6 hunks + 122 - 3 = 2,677 lines; disarmed twin be7b306a one line different)
The last runtime-resolution route in Q45 s7-limit-1's family with neither a literal nor an ast-visible env read: HELM_HOME = Path.home() / ".helm" composes the live root because CPython's posixpath.expanduser reads HOME itself. The channel is live in the shipped subject -- the real collector calls Path.home() twice (its himalaya search). Mechanism: an ast inventory of home-database reads (_q52_home_sites, :786) and, inside Q50's one rebuild function (two callers, probe :977 / legs :1162), a redirect of HOME + USERPROFILE to <target>/q52-home -- a redirect, not a refusal -- with a conditional Q52 NOTE and one new check("Q52", :1204). A --q52-witness extra flag (:1261) runs the copy with --deploy --pin --skip-tests --out <sandbox dest>, the pin shape the suite itself refuses (Q44's rule), because the class's write path is unreachable from the green run.
Measured: green 5/5 at 176 passed / 0 failed (= 175 + 1), 14,044 B stdout, live delta {}; the real collector's own .home() read makes the check non-vacuous. Discriminator, five cells under the digest-verified guard: armed x collector-home.py (d62888c4) -- the copy resolves inside the sandbox and the pin lands at q52-home/.helm/data/held/stackchain-home.html.sha256 in the sandbox, live delta 0; armed x collector-expanduser.py (99c49d39) -- same; disarmed x home -- HOME stays /root, the copy resolves HELM_HOME=/root/.helm and the live pin moves to sha256 caff1f504855264a / mtime_ns 1791907100434371499, restored byte-exact; disarmed without the witness flag -- the foot's own check fails (FAIL Q52, home_reads=['.home()'] HOME='/root', 174 passed / 2 failed); direct invocation, ambient env -- the live pin moves (the class's floor). Regressions 11/11 hold; live delta 0 on bytes/sha256/mtime across all 21 invocations (snapshot 14,039 B / aa842b0f, held pin 120 B / 5b6d7071); ctime moved on both objects (1791907371554631194), the guard restores of the two deliberately-live rows -- disclosed. Final tree 0 sandboxes; deployed /root/.helm/bin untouched (7ed1bca8, grep -c HELM_COLLECTOR = 0 -- Q40 stands, so none of Q31-Q52 is in production); disk 46 GiB free of 154.
Five defects of mine logged first in the deliverable: the witness's missing local subprocess import; the witness anchored before the leg copy was written; the witness's live census reading the held directory (IsADirectoryError); the NOTE claiming a redirect that had not fired (the disarmed twin's own FAIL contradicted it -- the NOTE is now derived from the actual env); and the first matrix measuring nothing because the real collector pins only under --pin, which the suite refuses. Limits stated as the item's own: the original-subject legs inherit the ambient environment (Q48's residual); pwd.getpwuid().pw_dir bypasses HOME (the inventory names it, the redirect cannot relocate it); the inventory is static (Q46's limit).
Files: recoveries/helm-home-channel-guard.md (13,173 B / sha256 3f813ca23202590c675f7ac7587f0d5e7d8b0ff9ee9f4f0711996530a2d6d7db), WORK-QUEUE.md (897,771 B), checkins/2026-10-13-merlin.md (Run 18). Scratch tree /root/crucible/q52/ not committed. No deployment, decisions/ and inbox/ untouched, no another-party file edited, no secret printed. The Helm lane's executable set is empty again; the adoption cp (collector 4e45021d + gate 78b290ec, superseding e4967da9) remains Alexander's.
|
||
|
|
57d8e6bcc7 |
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.
|
||
|
|
050b0e654c | Q42: non-magazine excerpt placement fresh-bytes recompute (zephyrus) | ||
|
|
4c6cd50baf |
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.
|
||
|
|
26d4020ce2 |
Q49 landing evidence: the three post-push digests, read back from main
The record's deferred table and the check-in's deferred line are filled from the anonymous raw read at main, taken after the push (a pre-push readback returns 404 for a new file), so the figures are stable for any outsider. All three HTTP 200 and byte-identical to disk. |
||
|
|
de74527209 |
Q49 closed: the Q48 live-held invocation named and reproduced; the class closed
- (a) ATTRIBUTION, measured not withdrawn. env-root-v1.py = q48/env-root.py with its
two default literals returned to /root/.helm (asserted count 2). Under a
digest-verified guard: the fixture run --dry-run / bare / --deploy --out <scratch>
with Q48_ROOT UNSET each moved the live held pin, and
HELM_COLLECTOR=<that fixture> Q48_ROOT unset python3 q48/helm-selftest.py moved it
too. All three Q48_ROOT=<scratch> controls: no move. Written bytes measured by a
5 ms watcher: 14 B / sha 784b9bded20e9e22... / 'q48-env-pin\n'. Q48's 6.2
attribution is upheld as a measurement; the pipe's negative reproduces exactly.
The writing leg is named verbatim by the per-leg census: the G8 --dry-run
original-subject leg, whose own source comment asserts "it writes nothing".
- (b) THE CLASS IS CLOSED, green 5/5. helm-selftest-q49.py (128,948 B / 2,445 lines /
085ddf4f42ae80ea...): the DEFAULT redirect (every os.environ.get("X","<default>")
pair inventoried by ast; a default literal resolving inside the live data root is
redirected to the per-invocation sandbox), a per-leg census of BOTH live objects as
detection, and a guard on the in-process --deploy leg. Checks 120 -> 123
(+3 check("Q49"), nothing else moved). Five green runs: rc 0, 174 passed /
0 failed, 13,863 B stdout, digests 13cf5cf7 / d4666b9f / 0abd28cd / 5ed81c37 /
bbbd1901, live delta 0 on both objects.
- TWO FINDINGS BEYOND THE ITEM. (i) G8's --deploy branch calls hcg.main() IN-PROCESS
with only SNAPSHOT_PATH patched, so a subject that resolved its root at import time
wrote the LIVE SNAPSHOT (14,039 B -> 137 B) -- reproduced, restored, and now
guarded. (ii) In the fully disarmed configuration the live pin still moves and no
spawn the traced instrument can attribute it: one fixture main() per run, banner
naming the sandbox, both censuses silent, watcher still seeing 120 B -> 14 B at
t+280 ms. The write is real, the mechanism is UNATTRIBUTED, and it is named as the
Helm lane's next unit rather than guessed at.
- Six live writes under guard, every one restored from a digest-verified backup that
refuses to restore unless the backup digest matches. Both live objects byte-identical
at session end: snapshot aa842b0fd88037f8... / 14,039 B and held pin
5b6d70716153c3f4... / 120 B. Nothing deployed: /root/.helm/bin/helm-selftest.py is
still 7ed1bca8... and reads HELM_COLLECTOR 0 times, so none of Q31-Q49 is in
production and the cp of collector 4e45021d... + gate 085ddf4f... stays Alexander's.
Deliverable: recoveries/helm-resolved-root-live-path.md (15,220 B).
Scratch tree /root/crucible/q49/. Record, check-in Run 15 and queue block.
|
||
|
|
0b0b591cc7 | Q48 CLOSED (Merlin): the runtime-resolution redirect — an env- or argv-resolved live root is relocated into the sandbox; 169 -> 171 green 5/5; the class measured with prevention separated from detection; the live-held incident caused, restored and disclosed | ||
|
|
8cb941fc26 |
Q47 CLOSED (Merlin): the read path — a subject the interpreter can run neither crashes the gate nor is falsely refused
The subject's source is now read the way CPython reads it: bytes through tokenize.detect_encoding (BOM stripped, PEP 263 cookie honoured), with a dated exit-2 refusal for a file that cannot be read that way, at Q46's guard point — after Q35's precondition, before the admission probe, before any leg, before any sandbox. All four subject-source reads route through the one reader; one stderr NOTE when the encoding is not plain utf-8; the Q46 parse guard, the Q45 redirect, every check() and the 169 count untouched. Deliverable recoveries/helm-read-path-guard.md (12,821 B), scratch tree /root/crucible/q47/, evidence out/Q47-NEGATIVES.json (21 rows), out/Q47-VERDICT.json, out/Q47-GREEN.json, out/DIFF-vs-q46.txt. Measured, not asserted: latin-1 cookie subject rc 0 / 169 passed / ALL GATES PASS under the shipped gate (Q39-Q45 crash with UnicodeDecodeError); BOM'd collector rc 0 / 169 passed (Q43/Q45 SyntaxError at the sandbox copy, Q44 AttributeError, sandboxes left); genuinely undecodable subject rc 2 with the dated Q47 REFUSAL and no sandbox. Q39 runs the BOM subject green with 164 passed — the read-path class is the text-compiling gates (Q43-Q46), not subject handling generally. Green pair 5x rc 0, 169/0, 13,312 B stdout / 425 B stderr, live and held delta 0 on all 26 invocations. Adoption pair moves to collector 4e45021d + gate 76f2292b. Nothing deployed, no cp into /root/.helm/bin/, decisions/ and inbox/ untouched, no another-party file edited, no secret. Run 13 in the check-in; queue block appended. |
||
|
|
a6b1ae3434 |
Q46 closed: the parse guard
An unparseable subject is refused with exit 2 and a dated message naming the parse failure, not admitted-then-crashed. Discriminator on all five gates, digest-identical to the revision history: Q39 rc 1 SyntaxError at the legs import of the original subject, Q43 rc 1 SyntaxError at the sandbox-copy import, Q44 rc 1 AttributeError at the unguarded _q39_tree.body, Q45 rc 1 SyntaxError at the sandbox-copy import, Q46 rc 2 dated refusal with stdout 0 B and no sandbox created. dep-unparseable refused by Q39 repair 3 on all five gates. C2 reproduced: the ROOT glob sees no leftover while the recursive census sees legacy/q4X/helm-legs-*. Green pair 5x rc 0, 169 passed, 13,312 B, live delta 0; atexit sandbox cleanup closes the n5 leak measured before and after. Live snapshot aa842b0f 14,039 B and held pin 5b6d7071 delta 0 across 27 invocations. Adoption pair is now collector 4e45021d + gate ae69d5dd. |
||
|
|
57895b2e7c |
Q45 CLOSED: the redirect keys on the resolved value, not on a name (Helm lane, Merlin)
One rule replaces Q44's two: every absolute path literal in the subject that
resolves inside the live data root is relocated to the same relative path inside
the per-invocation sandbox, on the parsed node's own span, wherever in the module
it sits and whatever the constant holding it is called. Q44's HELM_HOME =
assignment rewrite is subsumed, not removed; the SNAPSHOT_PATH rewrite (Q39/Q43's
mechanism) is kept. Q39's literal refusal is retired for this class and its reason
printed where the refusal stood -- the refusal's own text said the rewrite "cannot
redirect a path that is not the module constant the rule keys on", which is the
statement Q45 makes false.
Gate 2bbca218871bc5613e1fe3c4d3a84fe2e6da05ffc526e51b2865a8c8ad00ee5e (105,477 B /
1,942 lines) against Q44's 18580c102ddb1c3f (94,819 / 1,731); diff = 426 lines =
2 file headers + 7 hunk headers + 284 added + 73 removed + 60 context, sums exactly;
1,731 + 284 - 73 = 1,942. Collector byte-identical to Q44's 4e45021d80395d3f
(measured by sha256 comparison, not asserted).
Green pair 5 consecutive: rc 0, 13,312 B stdout each, 169 passed / 0 failed,
VERDICT: ALL GATES PASS. 169 = 166 + 3, unchanged from Q44 -- two new Q45 checks
plus Q44's held-directory check carried verbatim and still labelled Q44.
The five Q39 negatives still refused under both gates (all ten rc 2, live delta 0).
n5 is the class change: Q44 refuses it (stdout 0 B), Q45 admits it and lets its legs
run (stdout 575 B), still rc 2 with live delta 0.
Discriminator on a scratch protected root, rule (3) disarmed, two subjects (a
differently-named root constant; the held directory built at call time from a
literal, with HELD_DIR left in place as a decoy) x three variants (redirect armed /
disarmed / the Q44 gate blind):
armed rc 0, 169/0, the only held write in the run's own sandbox, protected delta 0
disarmed rc 1, 165/4, the held write in the protected root, protected NONZERO
q44blind rc 0, 169/0, VERDICT: ALL GATES PASS, and the held write in the protected root
Prevention separated from detection by measurement, on both shapes.
INCIDENT, disclosed in full: the first discriminator run wrote the LIVE held
directory. The subject's root literal was /root/.helm while the variant's own live
root was the scratch protected root, so the literal sat outside the gate's live
root, was never in the redirect's inventory, and the copy the legs ran still named
live. Restored byte-exact from /root/crucible/q43z/backup/held.bak by
restore_held.py, verified against the recorded digest 5b6d70716153c3f4 (120 B,
mtime_ns 1791478687099553071). ctime_ns moved to 1791846636217687791 -- the trace
of the restore, which a restore cannot undo. Three mechanical fixes followed in
this tree: make_subjects.py retargets the root literal with an asserted count;
run_negatives.py's guard() refuses any discriminator subject whose code names the
live data root (it fired during this build); abort_if_live_moved() stops the whole
run the moment a live object moves.
Four instrument defects of mine, each caught by its own measurement: the span list
visited each AST node once per ancestor (ast.walk nested inside _q39_iter_nodes),
which produced Path('<sandbox>'le/<sandbox>'le/<sandbox>'le/<sandbox>') on the
HELM_HOME line; the subject header swap stripped the original docstring's opening
triple quote; make_subjects.py's truthiness and its HELM_HOME expectation; and the
q44blind variant's PIN block reading the wrong environment variable.
Two defects in others' code, found here and not fixed here: the Q44 gate accesses
_q39_tree.body with no None guard (unexercised, so reported rather than repaired);
and Q39's _q39_live_literals list appends once per ancestor yielded, so one literal
site printed 12 times (deduped for display where Q45 reads it; Q39's list unchanged).
Live state delta 0 on all 21 rows: snapshot 14,039 B / aa842b0fd88037f8 /
mtime_ns 1791479579475780303 / ctime_ns 1791789490146606356; held pin 120 B /
5b6d70716153c3f4 / mtime_ns 1791478687099553071 / ctime_ns 1791846636217687791
(that ctime is the restore trace). Deployed /root/.helm/bin/ untouched
(ee0b36a82f23fcf3 / 7ed1bca8f9da624f / 5c67473d89283c3d). Nothing deployed, no cp,
decisions/ and inbox/ untouched, no another-party file edited, no secret.
|
||
|
|
534be9e32c |
Q44 CLOSED: the pin class is closed by construction, not by argv rule (Helm lane, Merlin)
Deliverable recoveries/helm-root-redirect.md (12,811 B / sha256
e68be4cdd5a535e22eae87f5ffbf9cb6841ea3d8a044ae34c13e74f86460568b). Scratch tree
/root/crucible/q44/; harness run_negatives.py -> out/Q44-NEGATIVES.json
(66,881 B / 611ab1e06e172be535ae5f19e21b4e1a6667a0f1b7542d130fc9d72298c5b895,
generated_at 2026-10-12T20:49:04Z), 17 invocations. Nothing deployed, no cp into
/root/.helm/bin/, decisions/ and inbox/ untouched, no another-party file edited,
no secret.
The pair: subject collector 4e45021d80395d3f... (29,954 B / 720 lines) byte-identical
to Q43's -- every change is in the gate. Gate 18580c102ddb1c3f...
(94,716 B / 1,731 lines) against Q43's c22601ecea4192d9... (88,468 / 1,612);
diff -u = 195 lines = 3 header + 133 added + 14 removed + 45 context, and
1,612 + 133 - 14 = 1,731 exactly.
What changed: the copy builder rewrites the HELM_HOME = assignment ITSELF to the
per-invocation sandbox, so every constant composed from it follows by construction;
the composed set is found by ast on the subject's own source (module-level Assign
nodes whose value references HELM_HOME) and is [SNAPSHOT_PATH, HELD_DIR]. The
SNAPSHOT_PATH rewrite is kept as defence in depth. Rule (3)'s --pin refusal is kept
but reclassified as defence in depth. Q39's admission, its behavioural probe and its
per-leg watch are untouched; no check's meaning changed. Three new checks, so the
green count is Q43's 166 + 3 = 169 passed / 0 failed: (1) the root constant is
redirected; (2) every constant composed from the root resolves inside the sandbox,
measured by a subprocess that imports the copy and prints the live values (not a
source marker); (3) the live held directory is unchanged by every leg, censused on
bytes, sha256, mtime_ns and ctime_ns before any leg and again at the foot.
Green pair, 5 consecutive invocations: rc 0 each, 12,966 B stdout, "helm-selftest:
169 passed, 0 failed" / VERDICT: ALL GATES PASS, 12.00-12.14 s; log digests
5c519157eaec4a40... / 97916ab27b8521c0... / 6f5553364b7a0d09... / ad57bad15689dfc5...
/ 00a1ae6dc2519efc... . Q43's counters unchanged (4 legs redirected, 3 --dry-run legs
original, 0 refusals, sandbox holds 3 paths with no data/snapshot.json).
The five Q39 negatives, still refused -- each run under the Q44 gate and, as the
pre-Q44 comparator, under the Q43 gate: all ten rc 2, stdout 0 B
(e3b0c44298fc1c149...), no report block, live delta 0, live held delta 0, 0.14-0.21 s.
The discriminator, on a scratch protected root (never live), rule (3) disarmed and a
--pin --deploy leg appended at the foot so it runs through the same leg wrapper.
pinleg (redirect armed): rc 0 / 169 passed, the pin lands in the sandbox
("pinned e40308e573d046d7d503acecbfbde82b8ccd10758a345b0440d55b796e2d821e ->
<sandbox>/data/held/stackchain-home.html.sha256"), protected held file delta 0
(114 B / b58d2d0eb9f45862b... / mtime_ns 1791478687000000000 / ctime_ns
1791838054542473495 before and after). pinleg-noredirect (redirect disarmed, live root
stood in for the scratch root): rc 1 / 166 passed / 3 failed, the same shape lands in
the protected root's held file (120 B / f98215b460bf89764924096c6fc657e62173e4c26f99bc4a2e5c489f3ed0f416 /
mtime_ns 1791838143983952745 / ctime_ns 1791838143991953414 -- NONZERO). Prevention
separated from detection by measurement. The live-path write was NOT re-measured: it
would clobber /root/.helm/data/held, Q43 already measured it (pinned 5e5cd6d26a70...)
and this run's pinleg reproduces the same body digest e40308e5... from the same code
path, so the class is the same one.
Live state, delta 0 across all 17 invocations: snapshot 14,039 B /
aa842b0fd88037f83115b4a54d617f27666f4de06ef84f9a64c8fd0a5f04656d / mtime_ns
1791479579475780303 / ctime_ns 1791789490146606356; held pin 120 B /
5b6d70716153c3f4bd82045d5d71edf88f793a240ce1275c3dfca5759d0ee297 / mtime_ns
1791478687099553071 / ctime_ns 1791478687102553111. Deployed bin untouched
(ee0b36a82f23fcf3... / 7ed1bca8f9da624f... / 5c67473d89283c3d...).
Limits and this run's own defects, logged first in the deliverable: the redirect keys
on the HELM_HOME = assignment, so a differently-named root or a call-time path is not
covered by the substitution (Q39's literal scan and the per-leg watch remain the
backstop); the shipped gate never exercises the redirect on a --pin leg, so that
shape's evidence is the variant; my first green run read 168 passed / 1 failed because
my own comparator excluded the sandbox root itself (startswith(dir + "/") is false for
HELM_HOME, whose value IS the root) -- fixed in patch_gate2.py and the only reason the
count is 169; the Q43 resolution-leg check false-fails under the variants and was fixed
in the variant only (patch_variants.py), the shipped gate untouched by that edit; and
one unexplained observation -- in pinleg-noredirect the sandbox census holds 5 paths
including data/snapshot.json, i.e. a leg wrote a snapshot inside the sandbox, which is
the sandbox doing its job and rides on nothing, recorded rather than explained.
Q7, measured: after this item the Helm lane's executable set is empty again (Q9 blocked
on the audio handover, Q13/Q19 gated on Alexander, Q42 open and zephyrus's). No new item
opened. The section 4 adoption is now a three-file ask: collector 4e45021d... + gate
18580c1..., with Q40's finding unchanged -- the deployed gate 7ed1bca8... has no
subject-selection path at all.
|
||
|
|
0fb8b2096c |
Q43: the legs run the rewritten subject -- prevention, not detect-and-restore
Q39's repair 5 is a net, not a fence: live bytes and mtime are read around every
subprocess and around the subject's in-process import, a change restores the
bytes and refuses the run, so the write has already happened when the watch
fires. Q39's own record names the residual and the Q39 closure asks for "the
stronger form (the legs against a rewritten copy)". This is that form, and it is
the Helm lane's next unit rather than a new invention.
Three rules in the one choke point the gate already has -- the leg wrapper
installed on subprocess.run, so every future leg inherits them:
1. a subject leg carrying --dry-run runs AS THE ORIGINAL, watched: it writes
nothing by the collector's own rule (helm-collect.py:698), and running the
original is what lets the resolution leg read the subject's own destination
out of its banner;
2. every other subject leg, and the in-process import, runs the REWRITTEN COPY
-- every SNAPSHOT_PATH = assignment pointing at a per-invocation sandbox
built after admission and removed at the end of the run -- so module-level
code can write at most into the sandbox;
3. a subject leg is REFUSED (exit 2, the leg never runs) if its argv names the
live snapshot or passes --pin.
Rule 3 is load-bearing, not decoration: the collector refuses only when --out
resolves to its OWN SNAPSHOT_PATH (helm-collect.py:702), so a copy whose
destination is the sandbox would write the live path if a leg named it. The
gate's own "explicit live --out is refused" leg therefore now names the copy's
own destination -- the Q30 discipline applied to the collector. The rule under
test is unchanged; it is measured on a path that is not live, and the semantic
change is stated in the gate and the record rather than left implicit.
Measured, 20 gate invocations, live delta 0 on every one (snapshot 14,039 B /
aa842b0f... / mtime_ns 1791479579475780303; held pin 120 B / 5b6d7071... /
mtime_ns 1791478687099553071):
- green pair, 5 consecutive runs: rc 0, 166 passed / 0 failed, ALL GATES PASS,
12,248 B stdout; 4 subject legs redirected, 3 --dry-run legs run as the
original, 0 refusals; the sandbox holds 3 paths (the copy and its bytecode
cache, no data/snapshot.json); 18 legs watched, delta 0 on each;
- the five Q39 negatives refuse under BOTH gates (rc 2, stdout 0 B, no report
block, live delta 0) -- but they are refused at Q39's admission, before the
Q43 sandbox exists, so they are a regression control and nothing in the
five-fixture suite exercises the redirect. Stated, not hidden;
- the discriminator, on a scratch protected path: argvlive (the gate's own
pre-Q43 leg shape, rule 3 armed) -> rc 2, the refusal naming the leg's argv,
the copy's destination and the rule at :702, and the protected file's sha,
mtime AND ctime unchanged -- the leg never ran. argvlive-norule3 (rule 3
disarmed) -> rc 2 via Q39's watch, protected ctime MOVED while sha and mtime
were restored -- the leg ran, the copy wrote a real snapshot over the
protected file, and only the restore put it back. ctime_ns is the
discriminator: the stamp a restore cannot undo;
- the --pin residual: pin_stackchain() writes HELD_DIR, composed from HELM_HOME
and untouched by the rewrite, so a --pin leg is refused (rc 2, naming --pin
and /root/.helm/data/held); a copy with HELD_DIR also redirected to scratch,
run --pin --deploy, writes the scratch held file, so the path is real; bare
--pin is refused by the destination gate before `if args.pin`, so it is one
flag combination away, not on the default path;
- the rewrite is behaviour-preserving: original vs copy, both --dry-run, both
20,570 B, scrubbed over the gate's full clock set -> diff_keys [].
Three defects of mine, logged first in the record: the redirect keyed on argv[0],
which is the interpreter (the subject is an argv element, and the first run
measured 0 redirects while its legs ran the original -- the Q40 shape); the
behaviour-preservation scrub omitted generated_at and duration_seconds; and the
first --pin measurement ran bare, so the held path was never reached.
Limits stated in the record: prevention is total for the SNAPSHOT_PATH class, not
for every class; the legs still execute the subject's code, and damage to
anything other than the snapshot is out of this gate's scope; the five-fixture
suite no longer reaches Q43's machinery, so the two variants and the pin residual
are the coverage.
Nothing deployed: no cp into /root/.helm/bin/, the deployed bin is unmoved
(ee0b36a8... / 7ed1bca8... / 5c67473d...), decisions/ and inbox/ untouched, no
another-party file edited, no secret printed. Adoption stays Alexander's, and Q40
measured that the deployed gate has no subject-selection path at all, so the cp
is the only thing that puts any of Q31-Q43's protection into production.
|
||
|
|
ff32a440e2 | recoveries: Q41 erratum applied -- literal rows' offsets re-measured in the space their row names (s3e.py:81 returned an S0 offset under an S1 label; fraction was a mixed-space ratio). 6 of 42 measured rows move at 4dp, class mean 0.229448 -> 0.229590 (0.2294 -> 0.2296); the magazine contrast (0.4735) and the fifths split (21/10/9/1/1) are unchanged. Second defect of mine found while applying: s3e.py:105 counted the raw excerpt against the S1 body, so 16 occurrence counts were under-counts and the multi-occurrence aggregate moves 8 -> 9. Also applied: the anchor-census label fix (46 anchored + 4 no-anchor = 50), the 25 -> 35 anchor revision logged (Q41 35 = 25 unique + 10 shared with the magazine 91; union 116 = Q21's census figure), and the fifths bucketing rule printed (min(int(f*5),4)). Verified from the patched markdown itself: 50 rows / 0 malformed, class counts 30/5/7/4/4 summing to 50, Q21 join 42 bound / 4 mis-bound / 4 unanchored, 50/50 rows agreeing with the S1-measured set. Scratch /root/crucible/q41/; no page re-fetched; decisions/ and inbox/ untouched; nothing deployed. | ||
|
|
6abf898bd1 | queue: Q41 excerpt fidelity class and placement, the 30 non-magazine entries DONE (Merlin) -- deliverable recoveries/excerpt-placement-nonmagazine.md (33645 B / sha256 d16761a827dfe5de9868dc0e1e513153d6acaa802983000959172d873de98c84); population 30 entries / 50 excerpts / 35 distinct anchors all HTTP 200 (index 200 / 34488 B / 4f764ef4, 2026-10-12T13:46:28Z); tally 30 literal / 5 normalised-literal / 7 elided / 4 paraphrase / 4 unreadable; placement n=42 over 26 of 30 entries, mean 0.2294 vs the magazine class 0.4735, fifths 21/10/9/1/1; Q21 join exact 42 bound / 4 mis-bound / 4 unanchored = 42/4/4; two of my own defects logged first (source-type unreadable rule on 23 non-article rows; three URL-tokenisation failures fixed, witness event-2022-08-21-...-1414 seq 1); scratch /root/crucible/q41/; no new item opened, Q37 pipe's, Q9/Q13/Q19 and Helm adoption gated | ||
|
|
9dd6ae1b17 | Q20 excerpt-placement audit (zephyrus, 2026-10-11): 9 magazine entries every-10th (magazine-002 to magazine-082), 26 excerpts graded 26 literal / 0 normalised-literal / 0 elided / 0 paraphrase / 0 unreadable; all 9 cited article URLs answered HTTP 200 and every excerpt is an exact substring of the fetched target page after the printed detag/normalisation rule; placement fractions 0.0000-0.9381 across ledes, middles and closes; one placement caveat (magazine-082: the guest piece is an embedded interactive app, so the two matches land on the page title and the magazine's author-note, graded literal by the stated rule). Readings from this host 2026-10-11T13:09-13:10Z, committed local-only - push to origin owed. Awaits Timmy verification. | ||
|
|
e56f959246 |
Q40: the deployed gate admits all five negatives, and has no admission path at all
Reproduces the Q39 closure's C26 from this host and states the mechanism: `grep -c HELM_COLLECTOR /root/.helm/bin/helm-selftest.py` -> 0; the deployed gate imports its subject at module scope from a hard-coded path (`:39`), so the five fixtures are never loaded -- the shipped gate has no subject-selection path at all, which is why Q34-Q39's admission machinery has nothing in production to guard. Measured: deployed gate x5 fixtures -> rc 0 / 7,488 B stdout / `119 passed, 0 failed` / `VERDICT: ALL GATES PASS`, stderr 0 B, live snapshot `aa842b0f...` delta 0 across all 15 invocations plus the green control; Q38 admits n1 and n2 (its C22/C23 holes); Q39 `9b09f328...` refuses all five (rc 2 / stdout 0 B / no fixture file touched / live delta 0) and runs the pair green (164 passed / 0 failed, `Q39 OK`, 17 legs watched, delta 0 on each). Nothing deployed, no `cp`, `/root/.helm/bin/`, `decisions/` and `inbox/` untouched. Deliverable `recoveries/helm-deployed-gate-admission.md` (5,633 B / sha256 5bdd2e7503cced8eee5b31a0256500aaa8eda9f8e3adbb80d3702eeb5e8df61a). |
||
|
|
66104f66a0 |
queue + record: Q39 closed and verified from this host (18 gate invocations, delta 0; the incident's own artifact refused at rc 2 / stdout 0 B); Q37 claimed with the v1.3 census landed -- branch-head line, one line of output, measured against the stored 09:40:52Z baseline as the push v1.2 hid (c2a0dc3710); its cost and its open boundary named
|
||
|
|
fad802c0f5 |
Q39: the destination guard's admission is keyed on the subject's own destination, and every leg is watched (Helm lane)
Five repairs to the Q38 gate (74aba348 -> 9b09f328, +309/-38): the probe injects from pathlib import Path so the rewritten line cannot NameError; EVERY SNAPSHOT_PATH assignment is rewritten; a probe that dies with a traceback is refused; an ast scan refuses an absolute path literal resolving inside /root/.helm/data/; and live bytes and mtime are read around every subprocess and around the subject's in-process import, a change dying the run and restoring the bytes it read. Negative controls (q39-negatives.py, Q39-NEGATIVES.json): five fixtures, each run under BOTH gates. n1 (the incident's shape) and n2 (a probe that dies with a traceback) are ADMITTED by the Q38 gate - n1's in-process import writes the fixture's destination inside the gate's own run - and refused by the Q39 gate. Green pair: 5 consecutive invocations, rc 0, 164 passed / 0 failed, 17 legs watched, live delta 0 on every run. Live snapshot aa842b0f delta 0 throughout; nothing deployed, decisions/ and inbox/ untouched. |
||
|
|
0776480b51 |
Q38: the destination guard is behavioural, not documented (Helm lane, Q36 candidate 2)
Q35's precondition reads a source marker (--deploy and SNAPSHOT_PATH.resolve() in the subject's source), so a subject that documents the rule in a comment without implementing it is admitted — the pipe measured exactly that at 02:4xZ today. The gate now, at helm-selftest.py:117-211, after the Q35 check and before the subject import (:784) and G8's bare-refusal leg (:1079-1081, the Q34 incident's leg), runs a copy of the subject's source with its SNAPSHOT_PATH = assignment rewritten to a sentinel path as a subprocess bare, and refuses the subject (exit 2, no leg runs) unless that run exits non-zero, writes nothing, and leaves the live snapshot delta 0. The probe never imports the subject into the gate's process. Measured this run (scratch tree /root/crucible/q38/, nothing deployed): - 7 consecutive green invocations, exit 0 each, 164 passed / 0 failed and ALL GATES PASS in every log, the Q38 OK line present in all seven, live snapshot sha256+mtime read around every invocation with delta 0 throughout. - Discriminator on both sides: fake-documented-no-rule.py (both markers in its docstring, so the Q35 predicate over its source is true) exits 2 with the refusal naming probe rc=0 and the sentinel written (74 B / 817e3830...); fake-marker-only.py (the pipe's measured shape) exits 2 at the assignment step; the deployed collector exits 2 at the Q35 source check with zero Q38 lines — the ordering. The admission control: fake-marker-only.py under the Q35 gate runs G7 and dies at the subject import (AttributeError, helm-selftest.py:688) — the admission measured end to end. - Gate 1,044 -> 1,141 lines (+97/-0; DIFF.txt 108 lines / ac8e52bb...), sha256 643e5387... -> 74aba348...; collector byte-identical to the Q34/Q35 pair (4e45021d..., 720 lines). - Live snapshot /root/.helm/data/snapshot.json 14,039 B / aa842b0f... / mtime_ns 1791479579475780303, delta 0 across every leg; deployed bin untouched (ee0b36a8 / 7ed1bca8 / 5c67473d); decisions/ and inbox/ untouched; no another-party file edited. Section 4 consequence: the adoption pair is now collector 4e45021d... + gate 74aba348... (superseding 643e5387...); the cp into /root/.helm/bin/ remains Alexander's. |
||
|
|
5fe58aafbe | Q20: the landing-evidence self-citation regress is broken by indirection, not by chasing it -- the record's own row and the check-in's own row defer to each other, the three revisions of the record are named with their bytes, and the stable artifacts (rows file, queue) keep their digests | ||
|
|
48bf86b93e | Q20: the record cites both of its own revisions (pre-fill 9dced5eb / 12,914 and post-fill e72062a3 / 13,062), the self-reference loop closed on the record rather than left circular, and the check-in landing table carries the same pair | ||
|
|
95dfbf37de |
Q20: landing evidence filled in on the record (readback table at 9a1de9a, post-push reads) and the Run 3 check-in appended (Did / Blocked / Next / Ask)
|
||
|
|
9a1de9a6ad |
Q20 DONE: excerpt fidelity class and placement for all 91 magazine entries (recoveries/excerpt-placement-audit.md + recoveries/excerpt-placement-rows.md)
Population: 91/91 magazine entries, no sampling -- 269 excerpts (87 entries x 3, 4 x 2). Class counts: 269 literal / 0 normalised-literal / 0 elided / 0 paraphrase / 0 unreadable; no row needed quote-style, dash, whitespace or URL normalisation, so the excerpt text as the entry page prints it is already an exact substring of the detagged article body at S0. Placement measured for 269/269 in one canonical space (S1, both sides): offsets 0-20,641 chars, fractions 0.0000-0.9472, mean 0.4735, fifth-buckets 53/65/51/61/39 -- near-uniform, 0 entries quoting only the last 20%, 2 only the first (magazine-043, magazine-050). Named extremes: magazine-057 widest spread 0.8923, magazine-027 narrowest 0.0094, magazine-019 highest mean 0.7905, magazine-050 lowest 0.0394. All 91 anchors are www.stackchainmagazine.net article URLs, all HTTP 200, so unreadable has no population in this class. Two archive-side facts, neither a defect: every one of the 269 magazine blockquotes prints its own anchor URL as the <cite> text (so the URL appears twice per excerpt on the rendered page -- any external matcher must strip the <cite> first), and 2 of the 269 excerpts (magazine-080 seq 1, magazine-082 seq 1) are the article's own headline, occurring 4x each at offset 0, so their 0.0000 fraction is a document position rather than a placement inside the prose. magazine-040 seq 2 is a refrain occurring 6x; placement is first-occurrence, stated in advance. Independent second method 269/269: stripping both strings to lowercase letters and digits only, the excerpt's image is a substring of the article's image AND a prefix of the image of the body slice at the reported offset -- tests the offset arithmetic rather than re-running the matcher. Cross-check against recoveries/excerpt-binding-audit.md: its 269 magazine rows are all bound with settling evidence "verbatim at" (2026-10-10) -- two days, two independent passes, same verdict. Limits stated: the detagged body includes site chrome, so fraction is a document-position measure not a position within the article prose; placement is first-occurrence (3 rows have more than one occurrence); 45 excerpts are under 40 characters (shortest 9, magazine-008 seq 2 "eat tacos") so placement is weak evidence of representativeness for those though the match is exact; byte counts and hashes are dated readings (2026-10-12T04:41-04:43Z). Three defects of mine logged first: (1) the excerpt unit included the <cite> URL so all 269 rows first read paraphrase -- caught by reading magazine-091's HTML rather than trusting the tally; (2) offsets were taken in S0 space and divided by an S1 body length so every fraction was wrong until re-run in one space -- 72 of 269 offsets moved; (3) my first second-method test was itself wrong (it compared the gap against the head length), replaced by the character-class method. No archive-side wording fix follows. Nothing deployed; decisions/ and inbox/ untouched. |
||
|
|
dd0bc69061 |
Q35 DONE: the destination-rule guard is a precondition, not a report (recoveries/helm-precondition-gate.md, scratch /root/crucible/q35/).
The subject collector named by HELM_COLLECTOR is checked for the rule (--deploy and SNAPSHOT_PATH.resolve() in its source) at helm-selftest.py:96-114 BEFORE the subject is imported (:679) and before G1, and refused with exit 2, a stderr message naming the subject and the missing rule, and the live snapshot read before/after the refusal. Gate +61/-2 (985 -> 1044 lines, 45e53187 -> 643e5387); collector byte-identical to the Q34 pair (4e45021d); the precondition registers no check() so the suite count is unchanged at 164 and the old foot-of-suite source check is kept at :1032. Seven consecutive green invocations (164 passed / 0 failed each, log digests in the deliverable), live snapshot delta 0 across every leg (aa842b0f / 14039 B / mtime_ns 1791479579475780303). Discriminator both sides in one run pair: deployed pre-gate collector as subject -> exit 2, stdout 0 bytes (e3b0c442, no summary and no PASS line, which is how "no leg executed" is measured), live delta 0; missing subject refused the same way, not crashed. Mechanism probe from this tree (q35-mechanism.py, mech-probe.log): default destination rebound to scratch, bare main() -- deployed ee0b36a8 rc=0 no refusal, 18523 B written (9331b218, schema helm.snapshot/1); pair 4e45021d rc=2, refusal naming the path and --deploy, nothing written. Limits stated: the gate run bare now refuses itself (default subject is the pre-gate deployed collector; the section-4 cp is what makes the shipped gate green, Alexander's); the precondition reads a source marker, not behavioral proof; the rule protects the gate's legs, not a direct CLI run. Nothing deployed; decisions/ and inbox/ untouched. |
||
|
|
a16d9e45be | Q34 DONE: the pointer's null case is gated (recoveries/helm-null-gate.md, scratch /root/crucible/q34/). Four checks planted in the gate's own scratch tree between two collects on a profile with sessions/ and logs/ and no files: latest_active_path null, source_path still the profile directory, the key present with value null rather than absent, and a follow-up collect where a planted file sets the pointer while the citation holds; the member analogue stated unreachable by construction. Seven consecutive green gate invocations (exit 0, 164 passed 0 failed each, the Q33 160 plus these four), log digests in the deliverable. Red on both comparators: Q33-before collector under the full gate exit 1 / 153 passed 11 failed (the Q33 nine plus Q34 checks 3 and 4); pre-Q33 collector 2 passed 2 failed on the four checks by module import with no subprocess leg; checks 1-2 pass vacuously on both comparators and are reported as vacuous. The collector did not move (byte-identical to the Q33 pair, 4e45021d, 720 lines); the gate moved +56/-0 (929 -> 985 lines, 45e53187), so the §4 adoption ask is now a three-file ask. One incident logged first: the first red leg ran the full gate against the pre-Q33 collector, which has no destination gate, so the bare --skip-tests leg wrote the live snapshot (aa842b0f -> e4d1ad5, 14039 -> 18524 B, mtime_ns 1791479579475780303 -> 1791764761933369921 at 00:26:01Z); restored byte-exact from /root/crucible/q31/snap-before.json with the mtime re-stamped and the comparator re-run by import only; live snapshot delta 0 across every leg after the restore. Deployed bin untouched. Limits: a live empty profile is unobserved; checks 1-2 vacuous on the comparators; the null value is not schema-constrained. | ||
|
|
8bb88da1d3 | Q33 DONE: the newest-file pointer is its own named field (evidence.latest_active_path / newest_checkin_file), source_path is a stable citation everywhere, the Q32 pointer carve-out is deleted from the gate; 6 consecutive green gate invocations (160/160 each), 8 paired runs with zero differing leaf paths without the carve-out, the gate red on the pre-Q33 collector (8 behavioural checks), consumer walk done; C5 appended to helm-g8-scrub.md Limits; collector 4e45021d + selftest 4a442f62 scratch-local, adoption is Alexander's | ||
|
|
7e12c82506 | Q32 DONE: the G8 two-run agreement check is deterministic — declared scrub set covers every clock-derived field (age_seconds, last_active_at, last_checkin_at, the newest-file pointer), 6 consecutive green gate invocations, 8 paired runs with zero differing leaf paths; collector 8a477376 unchanged, adoption identity is now collector 8a477376 + selftest 9ce6a4e9 | ||
|
|
1c0d660c2a | Q31 Run 7: dead digest corrected to the artifact that exists (8a477376), restore-mtime root-caused and fixed, gate re-run 137/137 (Merlin) | ||
|
|
300228b44c | Q31: collector's entry point executed, destination gated, G8 gate (Merlin run 6) | ||
|
|
adfff7a39a |
Q30 DONE: the renderer's entry point runs, and it refuses production by default
- reproduced the defect first: python3 helm-render.py exits 1 with NameError: name 'build_page' is not defined (guard at :90-91 above def build_page at :232), same with --out, no file written - moved the __main__ guard to the end of helm-render.py (:495 of 495); --out/--as-of semantics unchanged; fixed renderer 21,766 B sha256 5c67473d89283c3d4434928dc96d45ff48a41d8f443ecc3c98e9236be8b597f9 - destination gate per the 09:21Z control-plane addendum: OUT_DEFAULT is production, so main() resolves the destination and refuses without --deploy (exit 2, message naming the path and flag); parse_args now returns (out, now, explicit_out, deploy) - gate G7: the CLI executed in a subprocess, asserting emitted bytes (exit 0, file exists, bytes equal build_page()'s at the pinned clock, byte count matches, --as-of honoured, live root untouched); production destination refused by default and by explicit --out at the prod path (resolve(), not string equality); --deploy branch exercised against a patched OUT_DEFAULT; source-order guard so the defect cannot return - gates re-run: 119 passed, 0 failed (102 before; G7 adds 17); the gate discriminates - the same command against the pre-fix file exits 1 - CONTRACT.md appended append-only: transcripts, digests, gate, and a revision note on :213-214 (deploy is now --deploy) - nothing deployed: live page 9,865 B / 427b7b54c69b18d2... and snapshot aa842b0fd88037f8... unchanged before and after every run; --deploy alone never executed |
||
|
|
ca4889584e |
Helm run 2: the page aged and it lied about it
recoveries/helm-render-aging.md — the served Helm page (9,865 B / sha256
427b7b54c69b18d270288910fc810732463129f48ec4bdd7dbc75cdb22997ce4, read
2026-10-11T08:53:51Z) carried five false FRESH badges and a "5 green" pill on a
snapshot 2d 15h 40m old, against its own rule "green = measured OK and fresh".
The honest reading was 0 green / 6 not green. The same page served
manifest_match: true while a fresh collect returns False (live
5e5cd6d26a700423... vs held f27326e8b13d1dec...). No Helm cron job exists.
helm-render.py now re-derives every badge from each source's own observed_at
against the collector's threshold table (imported, not copied), recomputes the
verdict, and stamps the page with its snapshot age, a STALE READING/CURRENT
banner naming the stalest source and its ratio, and a per-card evidence line
carrying the limit each reading aged past. --as-of pins the render clock.
Roster identity was filename_body.split("-")[0], which registers
zephyrus-sparkborn as two members; it is now the longest roster prefix, with
unrecognised bodies reported in troupe_standup.unrecognised_bodies rather than
silently attributed. Fresh collect: 4 members, unrecognised_bodies: [].
The site digest was unreproducible from outside: run() uses text=True, so
live_body_sha256 is over the CRLF->LF-normalised body. 217,162 wire bytes /
9a5ee1328d4a3fedd... vs 217,149 normalised / 5e5cd6d26a700423..., delta 13 = the
13 CRLF pairs, lone CR 0, raw.replace(CRLF,LF) == normalised. The snapshot now
records live_body_sha256_wire and names the rule; both reproduce via
curl | sha256sum and curl | tr -d '\r' | sha256sum.
helm-selftest: 102 passed, 0 failed, six gates. Three defects of mine are logged
first in the deliverable: (1) the first renderer ran at module scope, so
importing it overwrote the production page — reverted byte-exact from a pre-run
copy and re-verified; (2) build_page() referenced an undefined SNAPSHOT and
78/78 passed because nothing called it; (3) card() was passed the evidence
sub-dict instead of the source block, so no card printed its limit. A gate that
does not execute the path it protects is not a gate — G5 renders real HTML at a
pinned clock and asserts on the emitted bytes.
Nothing deployed: /var/www/alexander-whitestone/helm/index.html unchanged at
9,865 B / 427b7b54c69b18d2... throughout; staged pages live in
/root/crucible/helm/. Deploy is one command and Alexander's under section 4.
/root/.helm/data/snapshot.json not clobbered (aa842b0fd88037f8... before and
after). No secret printed; decisions/ and inbox/ untouched; no other party's
file edited; no X page fetched.
|
||
|
|
cf3547cd1a | Q29 DONE: render-site-guards.md - the guards that are not the walk (22 fixtures + control, second guarantee stated, one finding: unguarded int(sats) leaves a partial tree) | ||
|
|
7ce4ea0a4b | queue: Q28 render-site channels DONE (Merlin) - 20 channels, 13 by fixture (19 fixtures, every negative exit 1 with 0 writes), 7 measured exclusions; the walk is the guarantee restated in one sentence; the no-token control byte-identical at 91ecf6bf with 125 files; two corrections in prose (36 esc call lines / 43 occurrences, not 28; Q27's fixture breakdown sums to 41-42 against its own 35-row log); shared tree untouched at 97fedb5d / 63c9cee6, live index 200 / 34,488 B / 4f764ef4dfb9; nothing executed | ||
|
|
08013ba55a |
queue: Q27 render-site coverage DONE (Merlin)
32 emit sites in build_blocks.py enumerated from the source (28 esc calls plus the 4 prose panels): 28 covered by fixture, 4 stated structural exclusions. 35 fixtures, every negative exit 1 with 0 files written (4 valid-panel builds expand, 12 malformed-panel dies, 4 uncited dies, 10 non-panel field dies, 2 URL-field dies, 3 class-field dies, head/index/pager/gap/genesis, 1 no-token control). URL-field scope decided: a token in a URL must die - safe_url rejects the non-https body_source_url value, the pre-write sentinel scan catches the https-with-token and excerpt cases, all exit 1 with 0 files. The queue's scope clause closed with option (a): check_uncited() runs above OUT.mkdir so all four token classes are pre-write (the uncited fixture wrote 20 files before the patch, 0 after, same message). No-token build still byte-identical at 91ecf6bf (125 files, diff -rq vs the shared site-ledger silent). Shared tree untouched at 97fedb5d with blocks.json 63c9cee6; live index 200 / 34,488 B / 4f764ef4dfb9. Patched builder c4127c6c is scratch-local, nothing staged; decisions/ and inbox/ untouched. |
||
|
|
f05179e89e | queue: Q26 hardening DONE (Merlin) - the two silent token classes now die at build time (pre-write sentinel scan, per-entry panel set so the quote branch is caught by construction, render_genesis panels routed through expand_prose), all five Q25 legs re-run green on the hardened prototype (no-token build byte-identical at 91ecf6bf, 125 files, diff -rq vs live tree silent, registry still 8, uncited-handle negative still exit 1, H1-H4 demo still 3 of 125 files), 12 negative fixtures all exit 1 with their own message (7 malformed incl. open brace/empty handle/whitespace/wrong namespace/over-15-chars/lone close/close-then-open, 5 outside-panel incl. quote branch, title, excerpt, display_name, coverage gap) plus a cited-token positive control expanding on the entry page AND the genesis page; contributor.display_name fifth slot measured without building it - G2 and I2 take the handle-only fix, G1 needs display_name as a fifth panel to anchor @AnthonyDessauer beside the single handle slot (a build change, Alexander's); hardened convention carries 4 of Q24's 31 deltas as links (H1-H4); shared build tree untouched at 97fedb5d with site-ledger byte-identical and nothing modified since 2026-10-10 18:00Z, live index 200/34,488 B/4f764ef4dfb9, blocks.json read-only, decisions/ and inbox/ untouched, nothing executed | ||
|
|
a9f23a69a4 | queue: Q25 prose-anchor convention DONE (Merlin) - {{x:HANDLE}} token prototyped in scratch, no-token build byte-identical (tree digest 91ecf6bf, 125 files), validator negative-tested, census 46 rows/23 handles (20 resolve/1 moved/2 404), 4 of 24 Q24 clauses upgraded (H1-H4); shared build tree untouched | ||
|
|
8b56abe688 | queue: Q23 named-unlinked source liveness DONE (Merlin) - 4 sources, 3 resolve / 1 404 | ||
|
|
5c7a75fd9b | queue: Q22 named-source liveness DONE (Merlin) — 28 fetches, 27 sources, 6 entries with a dead name | ||
|
|
e3b2f4ba18 | queue: Q21 excerpt-binding census DONE (Merlin) - 121/121 entries, 319 excerpts, 116 anchors all 200: 311 bound (300 verbatim / 7 marked-elision / 4 URL-tokenised), 4 mis-bound, 4 unanchored, 0 unreadable; the four mis-bound are one defect (block-25/88/89/90 attach the Skinner Stacking corroboration quote to the bare site root, which detags to 5,009 chars with Skinner x0 while the passage is verbatim at /a-conversation-with-burnchainers/, the publication's only ?s=Skinner article result); the four unanchored are the The-block testimony quotes on the same pages (correct for Testimony only); 38 named-source rows with Q12's StackchainQuant (handle 404) and @a_koby findings reproduced; zero anchors with empty excerpts, zero internal bindings; two comparator defects of mine logged (</a> as block closer + single unescape vs double-escaped entry pages; spaced-only ellipsis splitter); Q22 proposed (named-source handle liveness) as the queue fell to one executable item (Q20) | ||
|
|
a4fc298022 |
re-apply reply-50 note fix ('July–August'->'July') dropped in the 7843cfc amend - d0d4241 had it, the amend reverted the line; no grade effect
|
||
|
|
7843cfc4e2 | Q10 COMPLETE: batch C (bl-001..bl-008) graded - 29 clauses, 29 holds; 30/30 entries, 129 clauses: 126 holds / 1 overstated / 1 unsupported / 1 unverified; reply-49 ev-003 retraction annotated in place; reply-50 note fix 'July–August'->'July'; queue record; tree-repair: reflog shows an unexplained 'reset: moving to origin/main' between the rebase finish (c35f491) and the note-fix commit, so the WORK-QUEUE.md record is re-attached here; tree verified line by line below | ||
|
|
67b892d7bb | Q12 DONE: are the evidence tiers earned? (recoveries/evidence-tier-audit.md) - 18/18 justified, 0 too strong / 0 too weak / 0 indeterminate, no badge in the sample misstates its evidence; sample rule stated before any fetch (every non-documented entry = all 6: 5 testimony_only + 1 corroborated + 0 unrecovered, plus every 9th documented entry in index order = 12, total 18 of 121); census reproduced from the live index (200 / 34,488 B / sha256 4f764ef4dfb9, grep 'badge documented' 115 / testimony 5 / corroborated 1 / unrecovered 0 = 121 <li id=); all 18 entry pages and 17 cited sources fetched live this run, 33 of 37 quoted excerpts >=25 chars verbatim in a source the entry cites, and 8 of 12 sampled magazine entries carry a displayed date equal to the cited article's own article:published_time to the day; five named findings, none a badge overstatement - (1) block-25/88/89/90 quote the 'Skinner Stacking' passage and link the bare site root which contains 'Skinner' 0 times while the passage is verbatim at /a-conversation-with-burnchainers/ (200 / 155,217 B / sha 50a2e57e3ba6), the publication's only ?s=Skinner result; (2) event-2022-08-18 names StackchainQuant's October account twice, links it nowhere, and x.com/StackchainQuant returns 404 'User Profile Not Found', its badge still earned by the post text verbatim x3 and tx ae63c499 confirmed at height 749999 / 2022-08-18T17:45:12Z / 851,979 sats; (3) magazine-026 names @a_koby with no anchor; (4) the legend's Unrecovered tier is defined and applied 0/121 times while material fitting it sits in lower-tier Uncertainty panels; (5) Documented on the 91 magazine entries is a liveness claim not a held-artifact claim (Receipt panel says 'No receipt is held', and bitcoinmagazine.com answered 403 to a Chrome UA and 200 / 341,802 B to a bare curl UA in the same minute); limits: 18 of 121 is a sample, the 6 non-documented are a complete census of their tiers and the 12 documented license no rate for the other 103, all byte counts and hashes are dated readings from this host ~09:0x UTC, nothing live edited; check-in 2026-10-10-merlin.md; Q12 status block appended; Q20 next, Q9 blocked on the audio handover, Q13/Q19 gated on Alexander, depth not thin so no new item opened |