101 KiB
2026-10-12 — merlin
Screen. Every claim below carries a path, a hash, a URL, or a command with its output, or the word
unverified. Nothing secret is printed anywhere in this check-in or in the files it lands.
Did
Claimed and finished Q34 — gate the pointer's null case, the last open item and the one named to
my lane (Q20 is zephyrus-sparkborn's, Q9 blocked on the audio handover, Q13/Q19 gated on Alexander).
Deliverable: recoveries/helm-null-gate.md (9,924 B / sha256
84b1e714bfad1c0be948845f0f0a9f92a739ce5b64b68a197523bbb7c0808a58, this host). Queue block
appended to WORK-QUEUE.md (was 519,292 B / sha256
592523616f8b9a8ca45f4dabc631508baeb173c68de1f01cc125c0b0682a4f3e before this run's append).
Scratch tree /root/crucible/q34/; nothing deployed.
What was gated. Q33's one unverified limit: evidence.latest_active_path: null for a profile
with a sessions/ directory and no files, with the Q32 pointer carve-out deleted, needed an
exercised branch. The gate now plants, in its own scratch tree between two collects, a profile
empty carrying sessions/ and logs/ and no files, and asserts four things — pointer null,
citation 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 is stated unreachable by construction, not asserted (a member row exists only for an
existing check-in file); the rule is a comment in the gate, not a test.
Seven consecutive green invocations, exit 0 each, helm-selftest: 164 passed, 0 failed (the
Q33 suite's 160 plus these four), log digests in the deliverable:
7f01728d… / 997b1308… / 96678a66… / f13e5cd6… / 532ab633… / 00bea997… / d4e39936….
Both comparators red where it matters. The Q33-before collector under the full gate:
exit 1, 153 passed, 11 failed (q34-gate-q33before.log, sha256
7e53bb46adcdfda5f66c93dd780732fdc89207e5d5073bf56bd5921f3a94f1d5) — the Q33 nine plus Q34 checks
3 and 4. The pre-Q33 collector on the four Q34 checks by module import, no subprocess leg:
2 passed / 2 failed (q34-red-pregate.log, sha256
093f60e587186827a77e7f31247735b2048a8e86540a47485acf6324f95f1571). Checks 1 and 2 pass
vacuously on both comparators (an absent key satisfies .get() is None, and a pointer-less
citation falls back to the directory) and are reported as vacuous, so the discriminating checks are
3 and 4 — both red on both comparators.
Before/after evidence for the empty profile (from the gate's own dump, run 7,
Q34-NULL-EVIDENCE.txt, sha256 c625bc07068c5040d91117d604208152b33f34f0416e96dfc51d326546c3473c):
before — "latest_active_path": null, "source_path": "/tmp/tmp5r57iyp4/q34-null/profiles/empty";
after planting sessions/landed.jsonl at mtime +60 s — the pointer is that file and
source_path is unchanged. The key is present in the before block; the citation does not move.
What moved. The collector did not: /root/crucible/q34/helm-collect.py is byte-identical to
the Q33 pair's (4e45021d80395d3f…, 720 lines). The gate moved +56/−0
(git diff --no-index --numstat → 56 0), 929 → 985 lines, sha256
45e53187c01398df7a0edcbf24a08b7d91255eda0cfe93b9cb01e8398f0d7091. Deployed bin untouched:
helm-collect.py ee0b36a82f23fcf3…, helm-selftest.py 7ed1bca8f9da624f…,
helm-render.py 5c67473d89283c3d….
One incident, logged first and corrected. The first red leg ran the full gate with
HELM_COLLECTOR pointed at the pre-Q33 collector — which has no destination gate, Q31's own
finding — so the gate's bare --skip-tests refusal leg executed it and it wrote the live
snapshot: aa842b0fd88037f8… → e4d1ad5293fa58cb…, 14,039 → 18,524 B, mtime_ns
1791479579475780303 → 1791764761933369921 (2026-10-12T00:26:01.933369921Z). Restored byte-exact
from /root/crucible/q31/snap-before.json with the mtime re-stamped (q34-restore.py,
VERDICT: RESTORED BYTE-EXACT); post-restore 14,039 B / sha256
aa842b0fd88037f83115b4a54d617f27666f4de06ef84f9a64c8fd0a5f04656d / mtime_ns
1791479579475780303, and the comparator re-run by import only. Live snapshot delta 0 across
every leg after the restore — seven green runs, the Q33-before comparator and the import-only
comparator. This is the harness rule the skill already carries (a negative control must never run
ungated code against a live default) walked into anyway; the standing rule is now mechanical in
this tree: no HELM_COLLECTOR naming a pre-gate collector is ever passed to the full gate.
Blocked
Nothing in my lane. Q13/Q19 wait on Alexander, Q9 on the audio handover, Q20 is zephyrus's — none of them is mine to move. One thing my own run made Alexander's, below.
Next
Two things, both stated in the deliverable and neither mine to execute:
- §4 adoption, now a three-file ask. Collector
4e45021d…unchanged from Q33; selftest4a442f62…(Q33) superseded by45e53187…(this run). The pair production has never run under the gate that ships with it, so thecpinto/root/.helm/bin/approves collector + Q34-gated selftest together. Alexander's call. - A live empty profile is still unobserved — every profile under
/root/.hermes/profiles/has files today, so the null branch is exercised by the gate's fixture, not by production data. The branch is proven; the data class is not. Nothing to do unless a profile is emptied.
Also worth naming for the queue: the Q34 gate writes its Q34-NULL-EVIDENCE.txt dump into the
gate's own directory on every invocation, so that file is a dated reading, not a stable artifact —
digest run 7 is quoted above, and the deliverable says so.
Ask
Alexander: nothing blocking. If you want the Helm render's manifest_match pin re-taken, say so —
it has been stale since 2026-10-08 (f27326e8… pinned against a live body now
e7df6364… / 215,836 B) and a static render cannot say so, which is a separate decision about
whether that pin should exist at all.
Landing evidence, appended after commit and 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 to disk:
| file | status / bytes | sha256 |
|---|---|---|
recoveries/helm-null-gate.md |
200 / 9,924 | 84b1e714bfad1c0be948845f0f0a9f92a739ce5b64b68a197523bbb7c0808a58 |
WORK-QUEUE.md |
200 / 521,480 | 602746a5f202cdc39c9d53f59af814e8ab8d0fd8930a0042c47c258ddc1a0aa0 |
checkins/2026-10-12-merlin.md |
200 / 6,442 (pre-append revision, as pushed in a16d9e4) |
08afa9193238bd68c06a2d54dccdd9659ba9601a8164edb06126d008d57822db |
Re-verified in the same breath, from this host: the live snapshot
/root/.helm/data/snapshot.json is 14,039 B / sha256
aa842b0fd88037f83115b4a54d617f27666f4de06ef84f9a64c8fd0a5f04656d / mtime_ns
1791479579475780303 — delta 0 across the whole run after the restore. The deployed
/root/.helm/bin/helm-collect.py is unchanged at ee0b36a82f23fcf3…, helm-selftest.py at
7ed1bca8f9da624f… and helm-render.py at 5c67473d89283c3d… — nothing deployed, the §4 cp
remains Alexander's. decisions/ and inbox/ were not touched; no another-party file was edited;
no secret is printed in this check-in or in the files it lands — the long strings are gate check
names, snapshot JSON keys, file paths and sha256 digests.
— Merlin
Run 2 — Q35: the destination-rule guard is now a precondition
Claimed and finished Q35 (Helm lane, opened by the pipe this day after Q34 closed) — the twice-fired
class from Q34's incident, made mechanical at the gate instead of at the operator. Deliverable:
recoveries/helm-precondition-gate.md (8,261 B / sha256
a5d3ad6af205f3c1c8caf4f4baa2ee01548b5a89697ff5478e3fc52a9a61656b, this host). Scratch tree
/root/crucible/q35/; nothing deployed; the §4 cp is untouched.
What changed. The gate's source check — "the collector the gate runs carries the destination rule" —
used to sit at the foot of the suite, after every leg that could execute the subject collector. Q34's
incident is what that costs: the bare --skip-tests refusal leg executed an ungated subject and wrote the
live snapshot, and the check red-flagged it only afterwards. The check is now a precondition at
helm-selftest.py:96–114: the subject named by HELM_COLLECTOR is verified to carry the rule
(--deploy and SNAPSHOT_PATH.resolve() in its source) before the subject is imported (:679) and
before G1, and refused with exit 2 and a stderr message naming the subject and the missing rule, with
the live snapshot read before and after the refusal. Gate moved +61/−2 (985 → 1,044 lines), sha256
45e53187… → 643e53874e42f7ae58160c06c47eb07846725eae397104d9265964506bfcd0c9; the collector is
byte-identical to Q34's (4e45021d…). The precondition registers no check(), so the suite count is
unchanged — the old foot-of-suite check is kept at :1032 and passes on every green run.
Green pair, 7 consecutive invocations, exit 0 each, helm-selftest: 164 passed, 0 failed /
VERDICT: ALL GATES PASS (HELM_COLLECTOR=/root/crucible/q35/helm-collect.py python3 helm-selftest.py;
log digests 32faa0e9… / bfa6d09e… / 8bd47387… / 62756176… / 1c9639ef… / f9992981… / e5a2c5fd…).
Discriminator, both sides in one run pair. Deployed collector as subject
(HELM_COLLECTOR=/root/.helm/bin/helm-collect.py, pre-gate ee0b36a8…): exit 2, stdout 0 bytes
(q35-refuse-deployed.log sha256 e3b0c442…, the empty-file digest — no helm-selftest: summary and no
PASS line exists, which is how "no leg executed" is measured, since the report block is at the suite's
foot), stderr the refusal with live-snapshot readings, live snapshot delta 0 (sha256
aa842b0fd88037f83115b4a54d617f27666f4de06ef84f9a64c8fd0a5f04656d / mtime_ns 1791479579475780303 /
14,039 B — identical before and after, and across all seven green runs). A missing subject is refused the
same way, not crashed (q35-refuse-missing.err, sha256 90038616…).
Mechanism probe from the pipe's tree (q35-mechanism.py / mech-probe.log, sha256
31df78f6… / c4714d74…): each collector imported, its default destination (SNAPSHOT_PATH) rebound to
scratch, main() called bare — deployed ee0b36a8… rc=0, no refusal, 18,523 B written (sha256
9331b218…, schema helm.snapshot/1); pair 4e45021d… rc=2, refusal naming the path and --deploy,
nothing written. Rebinding does not disable the rule — it keys on the module's own SNAPSHOT_PATH — it
only removes the blast radius.
Limits stated, not buried: the gate run bare now refuses itself, because the default subject is the
pre-gate deployed collector — that is the item working as specified, and the §4 cp is what makes the
shipped gate green; the precondition reads a source marker, not behavioral proof (the probe is the
behavioral half, and sandboxing an untrusted import is the very thing the precondition prevents); and the
rule protects the gate's legs, not a direct CLI run of a pre-gate collector.
Blocked
Nothing in my lane. Same three gates as Run 1 (Q13/Q19 on Alexander, Q9 on the audio handover, Q20 is zephyrus's).
Next
Q7's never-dry obligation: the unclaimed executable set is zero after this run — Q30–Q35 are closed,
Q9/Q13/Q19/Q21 wait on humans or the counterparty, Q20 is zephyrus's active sweep. The next item I can
see is a Helm-lane one, and it is the §4 adoption itself: the gate now green-refuses its own default
subject, so the shipped pair is a two-file cp (collector 4e45021d… + gate 643e5387…) that makes
python3 helm-selftest.py green with no environment. That is Alexander's move; I have not touched
/root/.helm/bin/. If he approves, the natural follow-on item is a verification run of the deployed
pair — same discriminator, one host change.
Ask
Alexander: unchanged from Run 1 — nothing blocking. The two standing items are the §4 adoption (now a
cleaner ask: the pair travels together or the shipped gate stays self-refusing) and the manifest_match
pin decision (stale since 2026-10-08; a static render cannot say so).
Landing evidence, Run 2, appended after commit dd0bc69 and 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 to disk:
| file | status / bytes | sha256 |
|---|---|---|
recoveries/helm-precondition-gate.md |
200 / 8,261 | a5d3ad6af205f3c1c8caf4f4baa2ee01548b5a89697ff5478e3fc52a9a61656b |
WORK-QUEUE.md |
200 / 537,076 | 28c7250bec8f6dc9524e0af4cf7bafae29dd41665e07e7b2451449d41a1a1c67 |
checkins/2026-10-12-merlin.md |
200 / 12,471 (pre-append revision) | 814cda6ec174c519bd935e2ab76749b03891fc6716fcacc20df9688d2d1469fa |
Re-measured after the push: the live snapshot /root/.helm/data/snapshot.json is 14,039 B / sha256
aa842b0fd88037f83115b4a54d617f27666f4de06ef84f9a64c8fd0a5f04656d / mtime_ns
1791479579475780303 — delta 0 across the whole Run 2 (the mechanism probe, all seven green gate
invocations, and both refusal runs). The deployed bin is untouched: helm-collect.py ee0b36a8…,
helm-selftest.py 7ed1bca8…, helm-render.py 5c67473d… — nothing deployed, the §4 cp remains
Alexander's. decisions/ and inbox/ were not touched; no another-party file was edited; no secret is
printed in this check-in or in the files it lands — the long strings are check names, snapshot JSON keys,
file paths and sha256 digests.
— Merlin
Run 3 — Q20: the magazine class is clean on both halves
Claimed and finished Q20 — excerpt fidelity and placement for the 91 magazine entries, the top
unclaimed item and the one the queue's own label reconciliation names ("the oldest executable item
in the queue"). Population: all 91 magazine entries, no sampling — 269 excerpts (87 entries × 3,
4 × 2). Deliverables: recoveries/excerpt-placement-audit.md (12,914 B / sha256
9dced5eb…) and recoveries/excerpt-placement-rows.md (74,561 B / sha256
880223f35f9b…). Scratch tree /root/crucible/q20/; nothing deployed; decisions/ and inbox/
untouched; no another-party file edited.
Class counts: 269 literal / 0 normalised-literal / 0 elided / 0 paraphrase / 0
unreadable. No row needed quote-style, dash, whitespace or URL normalisation: the excerpt text
as the entry page prints it is already an exact substring of the detagged article body at S0. The
residues Q8 named in this class (072's added ellipsis, 015's unmarked elision) are summary-prose
defects and do not reproduce in the excerpts.
Placement, measured in one canonical space (S1, both sides): offsets 0–20,641 characters,
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 quoting only the first (magazine-043, magazine-050). Named:
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.
Census, all measured this run: index 200 / 34,488 B / 4f764ef4dfb9…; 121 entry slugs = 8
block + 22 event + 91 magazine; 121/121 entry pages 200 first attempt (04:41:37–04:41:48Z), magazine
pages 3,680–5,146 B; 269 magazine blockquotes, 269 carrying a <cite>, 0 anchorless; 91 distinct
article targets 200 (04:41:48–04:43:06Z), 123,566–184,748 B; S1 bodies 1,099–30,128 chars.
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; and
2 of the 269 excerpts (magazine-080 seq 1, magazine-082 seq 1) are the article's own headline,
occurring 4× 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 6×; 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.
Three defects of mine, logged first in the record: (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 the measurement was 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 from this item.
Limits stated, not buried: the detagged body includes site chrome, so fraction is a
document-position measure, not a position within the article's 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).
Blocked
Nothing in my lane. Q9 waits on the checksum handover, Q13 and Q19 on Alexander — none of them is mine to move.
Next
Q7, measured: the unclaimed executable set is now empty. Q20 was the last one. Q21–Q28 closed, Q30–Q35 closed and verified, Q9/Q13/Q19 human-gated. Nothing was opened this tick — inventing an item while one existed would be the busywork Q7's own wording warns against, and with the set empty the next item is now a genuine proposal rather than a pick. Two candidates I can see, both Helm-side and both mechanical rather than speculative:
- The §4 adoption pair, verified post-
cp. Already staged and measured in the Q35 record (/root/troupe-verify/q35/stage/): collector4e45021d…+ gate643e5387…run bare with no environment at exit 0 / 164 passed / 0 failed. The realcpinto/root/.helm/bin/is Alexander's; if he approves, the follow-on is a verification run of the deployed pair. - A behavioural destination guard. The Q35 precondition reads a source marker, so a subject
that documents the rule without implementing it is admitted (measured: a comment carrying
--deployandSNAPSHOT_PATH.resolve()passes). The stronger form runs the subject against a sentinel scratch path and asserts it refuses the live default.
Both are small; neither should be opened until Alexander says whether he wants either.
Ask
Alexander: nothing blocking. Two standing items, unchanged — the §4 adoption (collector
4e45021d… + gate 643e5387…, a two-file cp that makes python3 helm-selftest.py green with no
environment) and the manifest_match pin (stale since 2026-10-08; a static render cannot say
so). If you want the queue kept non-dry, say which of the two Helm candidates above you want opened
as Q36 — I would rather you choose than have me invent work.
Landing evidence, Run 3, appended after commit 9a1de9a and 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 to disk:
| file | status / bytes | sha256 |
|---|---|---|
recoveries/excerpt-placement-audit.md |
200 / 12,914 (9dced5eb…) → 13,062 (e72062a3…) → 13,738 (2e5241c9…) — three revisions, each read back 200 and cmp byte-identical; the record's own row defers to this table (a file cannot cite its own final digest) |
|
recoveries/excerpt-placement-rows.md |
200 / 74,561 | 880223f35f9b660e54df9fe21f3de372468fc610fb7d19cf7b820d82935c7123 |
WORK-QUEUE.md |
200 / 551,128 | 895620dd5b40a50ee6dbe4ebfbb534fe4eaad714a3b83c2fb9a92b300ba0f3fb |
checkins/2026-10-12-merlin.md |
200 / (this file — self-citation deferred to the record's note, for the same reason) | — |
Note on method, logged because it is the kind of thing that reads as evidence when it is not: the
first readback pass ran before git push and returned 404 for the two new files plus the
pre-push queue revision (548,438 B). Those figures measured nothing; the table above is the
post-push read. Nothing was deployed, published or re-run this tick; the live snapshot and the
deployed /root/.helm/bin/ were not touched by this run.
— Merlin
Process fault, disclosed after the fact (Q20 was another lane's unit)
After landing Run 3 I read WIZARDS-TABLE.md:13, which I should have read first. It names
zephyrus-sparkborn's current unit as Q20 — "excerpt fidelity & placement, 91 magazine
entries — named next pick" — and records "No 2026-10-11 check-in recorded; Q20 is the oldest open
item." My own lane's skill already carries this exact pitfall ("Before taking the oldest open queue
item, check WIZARDS-TABLE.md for a named lane… Q20 sat open and unclaimed while being
zephyrus-sparkborn's active assignment"). I did not check it, because the queue's label
reconciliation block reads "take Q20 as your next unit" — and that line is addressed to the
counterparty, not to me, which is precisely how the trap reads.
What is true either way: the deliverable is complete and independently verified (269/269 literal,
269/269 placement, second method 269/269, cross-checked against Q21's published record), so the work
stands on its own evidence regardless of whose unit it was. What is my fault is the sequencing: if
zephyrus-sparkborn is still active he will find Q20 closed and his assignment gone, and this is a
lane-discipline error, not a finding about the archive. Timmy, not me, owns WIZARDS-TABLE.md —
its Merlin row (line 15) still reads "Q30 closed 11:16Z … Q25–Q30 all closed", which this run
supersedes. I have not touched that file.
Alexander: nothing further is asked of you here. This is disclosed so the roster and the queue can be reconciled by whoever owns them, and so the next tick does not repeat it.
— Merlin
Run 4 — Q38: the destination guard is now behavioural, not documented
Claimed and finished Q38 — the behavioural destination guard (Helm lane; Q36's candidate 2, on
Alexander's direction to continue the lane). Deliverable: recoveries/helm-behavioural-guard.md
(10,232 B / sha256 69e72474c103c727c11408aca34a666309b740db66f0cd476bc13e1772d56db9, this
host). Queue block appended to WORK-QUEUE.md (was 565,470 B / sha256
b4b8b2d5561725c8205b5da18fa190aa9a143b0dc56bf2707f7fb4d71d4d5673 before this run's append).
Scratch tree /root/crucible/q38/; nothing deployed, nothing published.
What was gated. Q35's precondition reads a source marker — --deploy and
SNAPSHOT_PATH.resolve() present in the subject's source — so a subject that documents the
rule in a comment without implementing it is admitted, which the pipe measured at 02:4xZ today
(a fake collector carrying both strings in a comment ran the whole Q35 suite to completion with
live delta 0). Q38 adds, at helm-selftest.py:117–211 — after the Q35 check (:96–114, kept as
the fast filter and measured to fire first) and before the subject import (:784) and G8's
bare-refusal leg (:1079–1081, the leg that executed an ungated subject and wrote the live
snapshot in Q34's incident): a copy of the subject's source with its SNAPSHOT_PATH =
assignment rewritten to a sentinel path, run as a subprocess bare (--skip-tests, no
--out, no --deploy), and the subject is refused (exit 2, no leg runs) unless that run
exits non-zero and writes nothing and the live snapshot is delta 0 around the probe. The
probe never imports the subject into the gate's process — the subject's module-level code runs
only in the probe subprocess.
Green pair, 7 consecutive invocations, exit 0 each, helm-selftest: 164 passed, 0 failed and
VERDICT: ALL GATES PASS in every log, the Q38 OK: … (probe rc=2, sentinel written=False, live delta 0) line present in all seven, live snapshot sha256+mtime read around every invocation
(run-green.sh), delta 0 on all seven. Log digests: f606bf3877cd6a96… / 4cd6456713f2e1ff… /
02ecbf989f2d162c… / 94781f8dfa0f4ac8… / a2380b7ab4c61435… / fbd5e11713a6c048… /
d0cae43b79b4f5f1….
The discriminator, both sides. fixtures/fake-documented-no-rule.py — both markers in its
docstring, so the Q35 predicate over its source is true (measured: deploy ×2,
SNAPSHOT_PATH.resolve() ×1 by line count): exit 2, stdout 0 bytes (e3b0c442…), the refusal
naming the probe rc=0 and the sentinel written (74 B / sha256
817e38302aff70659ac8f49bd1607a7843748a917eec97dc01582d95c932f437), live delta 0.
fixtures/fake-marker-only.py — the pipe's measured shape: exit 2 at the assignment step (no
SNAPSHOT_PATH = at all), stdout 0 bytes. The deployed collector
(HELM_COLLECTOR=/root/.helm/bin/helm-collect.py): exit 2 at the Q35 source check with zero
Q38 lines in the output — the ordering. The admission control: fake-marker-only.py under the
Q35 gate ran G7 to completion and died at the subject import (AttributeError: module 'helm_collect_gate' has no attribute 'SNAPSHOT_PATH', traceback naming
/root/crucible/q35/helm-selftest.py:688) — the admission measured end to end — while the Q38
gate refuses the same subject before any leg runs.
What moved. Gate 1,044 → 1,141 lines, +97/−0 (DIFF.txt, 108 lines / sha256
ac8e52bb38090c66c8592a4aee9b39667d0cbbf6fefb8567043bf1e3ed0d588d), sha256
643e53874e42f7ae… → 74aba34860b34ac6aebd8661e3e46c01ed8c15dcec1cd958ec766114abfa1ef0.
The collector is byte-identical to the Q34/Q35 pair (4e45021d…, 720 lines, 29,954 B) — the gate
moved, the subject did not. §4 consequence: the adoption pair is now collector 4e45021d… +
gate 74aba348… (superseding 643e5387…); the cp into /root/.helm/bin/ is Alexander's.
Live state, delta 0 across every leg. /root/.helm/data/snapshot.json: 14,039 B / sha256
aa842b0fd88037f83115b4a54d617f27666f4de06ef84f9a64c8fd0a5f04656d / mtime
2026-10-08 17:12:59.475780303 +0000 (mtime_ns 1791479579475780303), identical before and after
the 7 green runs, the 3 refusals and the admission control; the sentinel temp directories are
removed at exit (0 left). Deployed bin untouched: ee0b36a82f23fcf3… / 7ed1bca8f9da624f… /
5c67473d89283c3d…. decisions/ and inbox/ untouched; no another-party file edited; no secret
printed here or in the files this lands — the long strings are check names, paths and digests.
Limits, stated in the deliverable: the probe substitutes the module's SNAPSHOT_PATH =
assignment, so a subject that resolves its destination by another route is not covered by the
substitution (the delta-0 assertion catches an actual write only after the fact); the criterion is
rc ≠ 0 and nothing written — a no-write rc=0 subject is admitted, correctly, since it cannot
clobber the live snapshot bare; the timeout branch is coded and not exercised by a fixture; and
the guard protects the gate's legs, not a direct CLI run of a pre-gate collector (the Q35 limit,
unchanged). fake-B was deliberately not run under the Q35 gate: its G8 legs include a bare run
and an explicit-live---out run, either of which writes the live snapshot with an ungated subject
— the accident class itself — so its admission was measured by the predicate directly, with fake-C
supplying the end-to-end admission.
Attribution note, mine: this run's commits are authored Merlin <merlin@agents.invalid> via
git -c, per the Q36 monitor fix — the Run 3 attribution fault is not repeated.
Blocked
Nothing in my lane. Q9 waits on the checksum handover, Q13 and Q19 on Alexander, and Q37 is the pipe's own tooling — the monitor script lives in the default profile's tree and Q37's own analysis already names where the fault is (the counterparty's push identity), so neither the harness edit nor the record is mine to make.
Next
The §4 adoption (collector 4e45021d… + gate 74aba348…, the two-file cp that makes
python3 helm-selftest.py green with no environment) and the manifest_match pin, both
Alexander's. Nothing else in my lane is executable; with Q38 closed the Helm candidates from Q36
are both discharged (candidate 1 was measured in Q35's post-cp staging; candidate 2 is this
run). If the adoption lands, the natural follow-on is a verification run of the deployed pair —
same discriminator, one host change.
Ask
Alexander: nothing blocking. The §4 adoption is the only thing between this scratch pair and a
shipped gate whose default subject is green rather than self-refusing — say the word and the cp
is one command, with the deployed-pair verification as the next unit.
— Merlin
Landing evidence, Run 4, appended after the commit and 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 to disk:
| file | status / bytes | sha256 |
|---|---|---|
recoveries/helm-behavioural-guard.md |
200 / 10,232 | 69e72474c103c727c11408aca34a666309b740db66f0cd476bc13e1772d56db9 |
WORK-QUEUE.md |
200 / 570,365 | c18ffd4c3b0ae9c8dc829d4f6f34547ac30c7c3ae56f4605fcdf6f178eea150b |
checkins/2026-10-12-merlin.md |
200 / 30,506 (pre-append revision, as pushed in 0776480) |
35a7ad50a7caefb3f026be745efcd657c147938abb79412c176645c58571acc5 |
All three cmp byte-identical to disk. Re-measured after the push, from this host: the live
snapshot /root/.helm/data/snapshot.json is 14,039 B / sha256
aa842b0fd88037f83115b4a54d617f27666f4de06ef84f9a64c8fd0a5f04656d / mtime_ns
1791479579475780303 — delta 0 across the whole run — and the deployed /root/.helm/bin/ is
untouched (ee0b36a82f23fcf3… / 7ed1bca8f9da624f… / 5c67473d89283c3d…). Nothing was deployed,
published, rebuilt or re-run; decisions/ and inbox/ were not touched; no another-party file
was edited; no secret is printed in this check-in or in the files it lands — the long strings are
gate check names, snapshot JSON keys, file paths and sha256 digests. This run's commits are
authored Merlin <merlin@agents.invalid> (git -c), so the monitor sees them as counterparty
content per Q36's v1.2 rule.
— Merlin
Run 5 — Q39: the admission admits a writer, and its legs run the unrewritten subject
Did. Claimed Q39, opened by the pipe on Run 4's falsified protective claim (reply 21, C22/C23) —
the top open item and the only executable work in the queue: the archive lane's unclaimed set is empty
(Q9 waits on the audio handover, Q13/Q19 on Alexander), and the Helm lane's remaining items are all
gated on the same adoption call. Deliverable recoveries/helm-destination-guard-fix.md (12,253 B);
fixed gate /root/crucible/q39/helm-selftest.py 1,412 lines / 77,704 B / sha256
9b09f3283008e695bb0634bc2d5492f2c24067b9eeb9ee2100099b7cbfcd1429, +309 / −38 against Run 4's
74aba348… (1,141 / 64,454 B); DIFF.txt 408 lines / b8adc9126397f61a…; subject collector
byte-identical to the Q34/Q35/Q38 pair (4e45021d…).
Five repairs, each with a measurement behind it: (1) the probe injects from pathlib import Path above
the subject's body, so the rewritten line cannot NameError on a subject that never imported it — C22's
mechanism, removed rather than detected; (2) every SNAPSHOT_PATH = assignment is rewritten, not the
first; (3) a probe whose stderr carries a traceback is refused, because a crash is not a refusal; (4) an
ast scan refuses any absolute path literal in the subject that resolves inside /root/.helm/data/ —
docstrings are excluded by a walk that does not descend into Expr nodes, which is why the deployed
collector's composed live path is admitted (assign line 30: SNAPSHOT_PATH <- 'data' / 'snapshot.json' -> resolves inside the live data root: /root/.helm/data/snapshot.json, printed on every green run);
(5) live bytes and mtime are read around every subprocess.run and around the subject's in-process
import, a change dying the run with exit 2 and restoring the bytes it read.
Measured, not asserted — python3 /root/crucible/q39/q39-negatives.py →
RESULT: ALL NEGATIVE CONTROLS REFUSED AS EXPECTED, Q39-NEGATIVES.json at 2026-10-12T09:28:09Z. Each
fixture runs under both gates, so every row is a discriminator; the fixtures' destination files are
deleted before each run so "created" is per-run:
| fixture | Q39 exit | Q39 condition named | Q38 exit | Q38 admitted? | side-effect files created (Q39 / Q38) |
|---|---|---|---|---|---|
n1-incident-shape (the incident's shape) |
2 | probe rc=0, sentinel written 2 B | 0 | yes | [] / ['n1-dest'] |
n2-traceback (raises at import) |
2 | the probe died with a traceback … |
1 | yes | [] / [] |
n3-decoy (decoy first assignment) |
2 | probe rc=0, sentinel written 2 B | 2 | no | [] / ['n3-real'] |
n4-comment-only (markers in the docstring) |
2 | probe rc=0, sentinel written 2 B | 2 | no | [] / [] |
n5-live-literal (proper refusal, live path as a literal) |
2 | names the live destination as an absolute literal |
2 | yes | [] / [] |
Every Q39 run: stdout 0 B, live delta 0, no helm-q39-* probe directory left behind. n1 is the
incident's own shape: Q38 admits it, and the gate's in-process import writes the fixture's destination
inside the gate's own run. n3 reproduces reply 21's containment finding — Q38's probe writes the
decoy's real destination; Q39 writes nothing.
Green pair, 5 consecutive invocations (bash /root/crucible/q39/run-green.sh 5): rc 0 each,
helm-selftest: 164 passed, 0 failed, VERDICT: ALL GATES PASS, Q39 OK … (probe rc=2, sentinel written=False, other probe files=0, no traceback, live delta 0), and Q39: 17 legs watched … delta 0 on each. Log digests: f0bfaca19d5d2a56… / a13a184de21a45a7… / e305e9f7dfd5fdc9… /
0b2343b6a2fb64bd… / eaaf90e14ba85777…. The leg watch itself is measured both ways
(q39-legguard-test.py, guard extracted from the shipped gate by its own Q39-LEG-GUARD-BEGIN/END
markers, lines 416–474 / 2,207 B, run against a scratch watched file): a clean leg passes and counts;
a leg whose subprocess writes the watched file exits 2, restores bytes and mtime, and prints both
digests (aef3121c6c5097a3… before, 8ddb948bf967125e… after).
Three instrument defects of mine, logged first. (1) A three-tuple live state including atime made
every leg refuse — reading the file updates atime on this filesystem — so the state is (bytes, mtime_ns). (2) The Q39 summary line on stderr landed concatenated onto the last PASS row (stdout is
block-buffered when redirected); it is now a flushed stdout print. (3) Twenty empty helm-q39-* probe
directories were left by the runs that crashed with NameError during the build (four _q38_* names had
been renamed to _q39_* in the setup block but not in the body); removed by cleanup-temp.py — 20 dirs,
0 kept — and the final runs leave none.
Live state, delta 0 across every run in this item. /root/.helm/data/snapshot.json 14,039 B /
aa842b0fd88037f8… / mtime_ns 1791479579475780303. Deployed bin unmoved: helm-collect.py
ee0b36a82f23fcf3…, helm-selftest.py 7ed1bca8f9da624f…, helm-render.py 5c67473d89283c3d….
Public page https://alexanderwhitestone.com/helm/ 200 / 9,865 B / 427b7b54c69b18d2… at
2026-10-12T09:29:03Z. /root/stackchain-legends-build/build_blocks.py still 97fedb5dd5e85d2b…, nothing
under that tree modified since 2026-10-11 08:00Z. decisions/2026-09-26.md 40b7cc14a829… and
inbox/seed-2026-09-26.json e4429147bc1b… unchanged. No another-party file edited; no secret printed.
Blocked
Nothing. One limit named rather than buried: repair 5 is detect-and-restore, not prevention — the legs still run the original subject. Running the legs against a rewritten copy is the stronger form and is not built.
Next
- The stronger form, if you want it: run the legs against a copy of the subject with every
destination rewritten, so an admitted writer can never execute against live. Measured effort: the
rewrite already exists in the probe path; the legs'
COLLECTORreferences would point at it. - Q37 is open and unclaimed — can this pipe distinguish its own commits from a counterparty's pushes under its credential? It is the only queue item with no human in front of it, and it is the pipe's tooling, not mine. I am not taking it without your nod, because it is Timmy's lane.
- No new queue item opened: the Helm lane's next unit is the adoption decision, not capacity.
Ask
Alexander — the adoption call Run 4 asked for is now sharper, because the gap is measured rather than
argued: the gate in production today (7ed1bca8…) admits n1 and n2, the two classes that wrote
the live snapshot on 2026-10-12, while the fixed gate (9b09f328…) refuses both before any leg. The pair
is collector 4e45021d… + gate 9b09f328…, a one-file cp plus the collector. Say adopt, hold,
or build the stronger form first — my recommendation is adopt-with-the-stronger-form as the next
unit, since the fixed gate already removes the two measured holes and the stronger form closes the last
one. Nothing else is blocking.
— Merlin
Landing evidence, Run 5, appended after the push (fad802c) and 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 to disk
(python3 /root/crucible/q39/readback.py, exit 0):
| file | status / bytes | sha256 |
|---|---|---|
recoveries/helm-destination-guard-fix.md |
200 / 12,253 | 56e25f160b8019d44d9b293d07ada642006bb51dacc726aece7481163294c42e |
WORK-QUEUE.md |
200 / 587,985 | 3884ebf8b731667ab3c5e99ae0e54c6e83883849d598e7b5553809626f937df5 |
checkins/2026-10-12-merlin.md |
200 / 38,651 (pre-append revision, as pushed in fad802c) |
d16b8ef27cc157008ea9773242bcd1de465778dee8d20a90cc705d091426bde2 |
Re-measured after the push, from this host: the live snapshot /root/.helm/data/snapshot.json is
14,039 B / sha256 aa842b0fd88037f83115b4a54d617f27666f4de06ef84f9a64c8fd0a5f04656d / mtime_ns
1791479579475780303 — delta 0 across the whole run — and the deployed /root/.helm/bin/ is
untouched (ee0b36a82f23fcf3… / 7ed1bca8f9da624f… / 5c67473d89283c3d…). Nothing was deployed,
published, rebuilt or re-run; decisions/ and inbox/ were not touched; no another-party file was
edited; no secret is printed in this check-in or in the files it lands — the long strings are gate
check names, refusal messages, file paths and sha256 digests. This run's commit is authored
Merlin <merlin@agents.invalid> (git -c), so the monitor sees it as counterparty content per
Q36's v1.2 rule.
Run 6 — Q40: the deployed gate admits all five negatives, and has no admission path at all
Did
Item taken: the Q39 closure's correction C26 — "the production gate (7ed1bca8…, 609 lines)
returns rc 0 / 119 passed, 0 failed on all five fixtures" — reproduced independently from this host,
because it is the last measured exposure in the Helm lane and everything else in the lane is gated on
Alexander's cp. Q34–Q39 are all closed; Q31/Q32 are adoption decisions; Q9 is blocked on the audio
handover; Q13/Q19 are Alexander's. Deliverable: recoveries/helm-deployed-gate-admission.md
(5,633 B / sha256 5bdd2e7503cced8eee5b31a0256500aaa8eda9f8e3adbb80d3702eeb5e8df61a, this host).
Method: /root/troupe-verify/q40/q40-deployed-gate-negatives.py, written from the gates' source.
Each of the five Q39 fixtures run as a subject of three gates (deployed 7ed1bca8…, Q38
74aba348…, Q39 9b09f328…) under HELM_COLLECTOR=<fixture>; side effects detected by
(sha256, size, mtime_ns) change over a pre-run census of the fixtures tree, nothing deleted; live
snapshot state read before and after every invocation. Evidence:
/root/troupe-verify/q40/DEPLOYED-GATE-NEGATIVES.json. Run transcript: /root/troupe-verify/q40/run.out.
C26 reproduces exactly. Deployed gate × all five fixtures: rc 0, stdout 7,488 B,
helm-selftest: 119 passed, 0 failed, VERDICT: ALL GATES PASS, stderr 0 B, 0.5 s each, no fixture
file touched, live delta 0. Live snapshot unchanged throughout: 14,039 B / sha256
aa842b0fd88037f83115b4a54d617f27666f4de06ef84f9a64c8fd0a5f04656d / mtime_ns
1791479579475780303 — delta 0 across all 15 invocations plus the green control.
The mechanism is stronger than C26 stated. 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
(helm-selftest.py:39, spec_from_file_location("helm_collect", Path("/root/.helm/bin/helm-collect.py"))),
so HELM_COLLECTOR never reaches it: the five green rows are not the gate approving bad collectors —
it never loaded them. The exposure is that the deployed gate has no subject-selection path at all,
so there is nothing for Q34–Q39's admission machinery to guard, and a bad collector dropped into
/root/.helm/bin/ would fail the suite uncontrolled (a module-scope AttributeError, not a dated
refusal) or run with no destination rule in the production path.
The fixed gates discriminate, re-measured in the same run. Q38 74aba348…: n1 rc 0 (admitted —
C22's hole, writes the fixture's n1-dest/), n2 rc 1 (admitted then crashed), n3/n4/n5 rc 2 refused
(n3's probe writes the decoy's real destination — the containment failure). Q39 9b09f328…: all
five rc 2, stdout 0 B, no fixture file touched, live delta 0. Positive control, this run,
HELM_COLLECTOR=/root/crucible/q39/helm-collect.py python3 /root/crucible/q39/helm-selftest.py:
exit 0, 164 passed, 0 failed, VERDICT: ALL GATES PASS, Q39: 17 legs watched … delta 0 on each,
Q39 OK: … (probe rc=2, sentinel written=False … live delta 0) — log
/root/troupe-verify/q40/q39-green.out, sha256 7fd767b72e811f46466b4acd4f214ab27e56c33012629a7bcd5fbbbc2567daa4.
No probe dirs left (ls /root/crucible | grep -c helm-q39- → 0).
Nothing touched. /root/.helm/bin/ unmoved (ee0b36a82f23fcf3… / 7ed1bca8f9da624f… /
5c67473d89283c3d…), no cp, nothing deployed; decisions/ and inbox/ untouched.
Blocked
- Adoption is still Alexander's, and now the sharper ask. The
cpdecision (collector4e45021d…+ gate9b09f328…) is the only thing that puts any of Q31–Q39's protection into production; until then/root/.helm/bin/helm-selftest.pyruns with no subject-selection path. - Disk:
/at 21 MB free / 100% (df -h /), same as the pipe's 11:07Z reading. Q40 added ~50 KB of logs. The reclaim classes remain Alexander's (log rotation +docker system prune≈1.5 GB; thestackchain-dashboard.servicecrash loop at ~600 MB/day).
Next
- On adopt: the verification run of the deployed pair — the gate re-run from
/root/.helm/bin/after thecp, with the negatives and the live snapshot measured around it. - The stronger form named in Q39: legs run a rewritten copy of the subject rather than the original.
- Q37 (pipe tooling) stays with Timmy; Q9 stays blocked; Q13/Q19 stay gated.
Ask
Alexander — Run 5's ask is unchanged and this run strengthens it: the gate production runs today has
no subject-selection path at all (HELM_COLLECTOR read count 0), so none of Q34–Q39 is in force in
/root/.helm/bin/. The pair is collector 4e45021d… + gate 9b09f328…: adopt, hold, or
build the stronger form first. My recommendation is unchanged — adopt, with the stronger form as
the next unit. Second ask, carried: approve log rotation + docker system prune (~1.5 GB) and rule on
stackchain-dashboard.service; at 21 MB free every lane's run is one ENOSPC from dying.
— Merlin
Landing evidence, Run 6, appended after the push and 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 to disk:
| file | status / bytes | sha256 |
|---|---|---|
recoveries/helm-deployed-gate-admission.md |
200 / 5,633 | 5bdd2e7503cced8eee5b31a0256500aaa8eda9f8e3adbb80d3702eeb5e8df61a |
WORK-QUEUE.md |
200 / 612,025 | 6a45671ea28009aa9a8c3f7b95c764a293f7572605aa5e598b66ad5071aa6df0 |
checkins/2026-10-12-merlin.md |
200 / 45,816 (pre-append revision, as pushed in e56f959) |
032d3a0ae9d12497405492035b8e01fed1120154d93e022ce35cb1ebad3b1257 |
Readback method: python3 /root/troupe-verify/q40/readback.py (anonymous GET, no
credential), exit 0, VERDICT: ALL READBACKS IDENTICAL, run after the push of e56f959.
Re-measured after the push, from this host: the live snapshot /root/.helm/data/snapshot.json is
14,039 B / sha256 aa842b0fd88037f83115b4a54d617f27666f4de06ef84f9a64c8fd0a5f04656d / mtime_ns
1791479579475780303 — delta 0 across the whole run — and the deployed /root/.helm/bin/ is
untouched (ee0b36a82f23fcf3… / 7ed1bca8f9da624f… / 5c67473d89283c3d…). The deployed gate,
re-run bare from /root/.helm/bin/ after the push: rc 0, helm-selftest: 119 passed, 0 failed,
VERDICT: ALL GATES PASS — the production gate is green, which is exactly what this run's finding
is about: it is green on every input it was given, including all five negatives. Nothing was
deployed, published, rebuilt or re-run beyond the measurements above; decisions/ and inbox/ were
not touched; no another-party file was edited; no secret is printed in this check-in or in the files
it lands — the long strings are gate check names, refusal messages, file paths and sha256 digests.
This run's commit is authored Merlin <merlin@agents.invalid> (git -c), so the monitor sees it as
counterparty content per Q36's v1.2 rule.
Re-read after the second push (9af42d2, this block's own append), from this host, same method and
same exit 0 / VERDICT: ALL READBACKS IDENTICAL — the three files at HTTP 200 and cmp identical,
the check-in now 47,400 B / sha256 f3d7497b48fadf45… (its own revision, byte-identical on both
sides).
— Merlin
Run 7 — Q41: excerpt fidelity class and placement, the 30 non-magazine entries
Did
Took Q41 — the top unclaimed, executable item (zephyrus's 10-12 check-in declined it: "Q37 is the only open executable item and it is claimed by the pipe … nothing unclaimed exists today, so I take nothing"). Population, method and finish line are Q41's own spec; the deliverable is a new file, not an edit of a closed one.
- Population re-enumerated from the live index (
https://alexanderwhitestone.com/stackchain/blocks/, HTTP 200 / 34,488 B / sha2564f764ef4dfb9c08b2654ff0783550d20ad1bb7a31cb0c32070974a5fe451557e, read 2026-10-12T13:46:28Z): 121 slugs = 8block-*+ 22event-*+ 91magazine-*; this run took the 30 non-magazine entries. 50 excerpts (42From the sources, 8The block), 35 distinct anchors, all fetched HTTP 200 (curl -sS -L -o … -w '%{http_code} %{size_download}', 2026-10-12T13:46:13–13:47:00Z). Anchor hosts:www.stackchainmagazine.net11,x.com12,raw.githubusercontent.com5,web.archive.org2,github.com2,bitcoinmagazine.com1,europeanbitcoiners.com1,podcasts.apple.com1. - Deliverable:
recoveries/excerpt-placement-nonmagazine.md, 33,645 B, sha256d16761a827dfe5de9868dc0e1e513153d6acaa802983000959172d873de98c84— screen header, rules (the Q20 ladder with the<br>→ space clause explicit, as Q41's finish line requires), counts, 50 per-row rows (fixed column orderentry | seq | excerpt | class | normalisation | source class | space | offset | fraction | occ | anchor | Q21 verdict | settling evidence), aggregates, findings, limits, and this run's own defects. Scratch tree/root/crucible/q41/; scriptsfetch.py,classify.py,place.py,regrade.py,s3e.py,reconcile.py,aggregate.py,emit.py; logs underout/. - Final tally: 30
literal/ 5normalised-literal/ 7elided/ 4paraphrase/ 4unreadable(of 50). Placement measured on 42 rows across 26 of 30 entries: min 0.0, max 0.8048, mean 0.2294 — against the magazine class's Q20 mean of 0.4735. Fifths: 21 / 10 / 9 / 1 / 1. 8 excerpts occur more than once in their body; placement is first-occurrence and the occurrence count is printed per row. - The Q21 join is exact, row for row: the 42
boundrows = 30 literal + 5 normalised-literal + 7 elided; the 4mis-bound= the 4paraphrase(the known home-page citation defect onblock-25/88/89/90, already carried by Q21 and Q4 — no new archive-side wording fix follows from this item); the 4unanchored= the 4unreadable(theThe blocktestimony quotes with no anchor printed). 50 = 42 + 4 + 4. - Two of this run's own defects, logged first in the deliverable:
- The source-type rule: stage 2 classed the 23 non-article-source rows (X posts, SIP text
files, repo pages, the podcast page, Wayback captures)
unreadableon source type alone — Q21 graded every one of themboundwith the text found in the detagged body, so they were re-graded under the same ladder with the source class named in the row.unreadableis reserved for the two cases Q41's finish line names: no anchor, or a non-200 target. - The URL-tokenisation rule failed three times before it held:
https?://\S+swallowed the word after a bare URL; stopping at a quote character still failed when a URL was followed by a closing quote (…down? https://t.co/Go9kd8c6dh"); and tokenising at S0 before quote normalisation left one character unmatched. The final rule (run stops at whitespace or a quote, applied at S1 on both strings) resolves the witness row —event-2022-08-21-block-1414-becomes-the-first-million-targetseq 1, where the entry prints the expandedx.com/i/spaces/…URL and the page carries thet.coshort link:paraphraseunder the broken rules,normalised-literal(URL-tokenised, S3) under the fixed one.
- The source-type rule: stage 2 classed the 23 non-article-source rows (X posts, SIP text
files, repo pages, the podcast page, Wayback captures)
- Queue updated: Q41's block now carries the DONE status line; the offered-to-zephyrus sentence is kept as history. No new item opened: Q37 stays the pipe's, and Q9/Q13/Q19 and the Helm adoption stay gated on Alexander.
Blocked
Nothing in my lane. Same standing gates: Q9 (checksum handover), Q13 (the wording fixes), Q19
(wizard onboarding) and the §4 cp adoption are Alexander's calls. Host disk: 931 MB free of
154 GB at 13:46Z, measured (df -h /root); no action taken from this lane.
Next
- Offer Q41's rows file and method to zephyrus as a second pass — the non-magazine half his lane can recompute independently (his Q20 sample agreed with the population verdict).
- The two standing Helm candidates remain un-opened and wait on Alexander: the post-
cpverification run of the deployed pair, and a stronger behavioural destination guard. - Keep the reply-reading and independent-verification discipline on whatever the counterparty lands next.
Ask
Alexander: nothing blocking. The same two standing items — the §4 adoption (collector
4e45021d… + gate 9b09f328…, a two-file cp; Q40 showed the shipped gate has no
subject-selection path at all, so none of Q31–Q39's protection is in force in /root/.helm/bin/
until it lands) and the manifest_match pin (stale since 2026-10-08; a static render cannot
say so).
Evidence table
| item | value | how obtained |
|---|---|---|
| index | HTTP 200 / 34,488 B / sha256 4f764ef4… |
curl -sS -L -o index.html -w '%{http_code} %{size_download}', 2026-10-12T13:46:28Z |
| 30 entry pages | all HTTP 200 | fetch.py, clocks 13:46:29–13:46:32Z |
| 35 anchors | all HTTP 200 | fetch.py, clocks 13:46:13–13:47:00Z |
| deliverable | 33,645 B / sha256 d16761a827dfe5de9868dc0e1e513153d6acaa802983000959172d873de98c84 |
wc -lc + sha256sum on the written file |
| final tally | 30 / 5 / 7 / 4 / 4 of 50 | aggregate.py over out/rows.json |
| placement | n = 42, min 0.0, max 0.8048, mean 0.2294, fifths 21/10/9/1/1 | aggregate.py |
| Q21 join | 42 bound / 4 mis-bound / 4 unanchored = 42 / 4 / 4 | reconcile.py against recoveries/excerpt-binding-audit.md |
| host disk | 931 MB free of 154 GB | df -h /root, 2026-10-12T13:46Z |
Nothing secret is printed. Every reading above is from this host, this run, and is reproducible from the scripts named.
— Merlin
Landing evidence, Run 7, appended after commit 6abf898 (13:57:38Z) and read back over the
anonymous raw path (https://forge.alexanderwhitestone.com/git/stackchain/troupe-standup/raw/branch/main/<path>),
no credential — python3 /root/troupe-verify/q41/readback.py, exit 0, VERDICT: ALL READBACKS IDENTICAL — each HTTP 200 and byte-identical to disk:
| file | status / bytes | sha256 |
|---|---|---|
recoveries/excerpt-placement-nonmagazine.md |
200 / 33,645 | d16761a827dfe5de9868dc0e1e513153d6acaa802983000959172d873de98c84 |
WORK-QUEUE.md |
200 / 646,471 | 12b9170a7836… |
checkins/2026-10-12-merlin.md |
200 / 54,138 (pre-append revision, as pushed in 6abf898) |
e95c0c3c0624… |
Re-measured after the push, from this host: host disk 931 MB free of 154 GB (df -h /root,
13:57:49Z). Nothing was deployed, published or rebuilt; /root/.helm/bin/ was not touched (the
Helm lane's live snapshot and deployed bin are unchanged from Run 6's readings — aa842b0f… /
14,039 B / ee0b36a82f23fcf3… / 7ed1bca8f9da624f… / 5c67473d89283c3d… — and no leg of this
run executed any gate or collector). decisions/ and inbox/ were not touched; no
another-party file was edited; no secret is printed in this check-in or in the files it lands —
the long strings are article quotes, URLs, paths and sha256 digests. This run's commit is
authored Merlin <merlin@agents.invalid> (git -c), so the monitor sees it as counterparty
content per Q36's v1.3 rule.
— Merlin
Run 8 — Q41 erratum: the literal rows' offsets were measured in the wrong space
Did
Took the one item owed to me by Run 7: the pipe's verification of Q41 (reply 24, e7f2e3f,
and reply 25, 6cb72d4, which corrected the correction's own count from 42-of-42 to
30-of-42) returned one defect and three precision items against
recoveries/excerpt-placement-nonmagazine.md. All four are applied and every figure
re-verified from the patched file. The item stays DONE; nothing is re-opened.
- E1 — the returned correction, applied.
s3e.py:81returnedb0.find(ex)— an S0-space offset — paired with the S1 body, theS1label andround(off / len(b1), 4), so the printedfractionwas a mixed-space ratio (S0 offset over S1 body length). Measured on the deliveredout/rows.jsonbefore the fix: the 30literalrows printed the S0 offset in 30 of 30 cases and the S1 offset in 24 of 30. Fixed per the reply's instruction (o1 = b1.find(s1(ex)), keep theS1label, do not relabelS0); all 42 measured rows now measure in the space their row names. 6 rows move at 4 dp —event-2022-07-19-op-backstack-experimentation-almost-immediatseq 1 (603→605, 0.3888→0.3901),event-2022-07-22-skinner-s-405-420-and-the-social-fork-questiseq 2 (1016→1018, 0.452→0.4528),event-2022-08-21-block-1414-becomes-the-first-million-targetseq 3 (958→960, 0.4327→0.4336),event-2022-08-24-a-practical-fork-resolution-offer-at-1438-14seq 1 (1027→1029, 0.4792→0.4802),event-2022-10-07-gui-s-2079-and-saylor-s-2080seq 1 (1034→1036, 0.49→0.491) and seq 2 (1040→1042, 0.5448→0.5458) — and the class mean moves 0.229448 → 0.229590 (0.2294 → 0.2296). The magazine contrast (0.4735) is untouched; the fifths split is unchanged at 21 / 10 / 9 / 1 / 1. - E2 — mine, found while applying E1, logged first.
s3e.py:105counted the raw excerpt against the S1 body: aliteralrow whose S1 normalisation changes the string (curly quotes, NFKC) counted 0 occurrences, and theelidedrows counted their whole...-joined excerpt, which never occurs whole. 16 occurrence counts were under-counts (all 0 → 1/2/3/4); the multi-occurrence aggregate moves 8 → 9 (the new row is…-op-backstack…seq 1, occ 3). No fraction depends on the count. - E3 — the anchor-census label, fixed per the pipe's own C45. "Source class of the 46 anchored rows: … no anchor 4 …" summed to 50 under a 46-row label. Re-labelled: the five anchored classes sum to 46, the 4 no-anchor rows are the remainder. No arithmetic moved.
- E4 — the 25 → 35 anchor revision, now logged where it was missing. Q20's fetch note
prints "the magazine 91 plus the 25 the non-magazine entries cite" (= 116, Q21's census
figure) while this file prints 35. Recomputed this run (
erratum_anchors.py): Q41's 35 anchors, Q20's 91 magazine anchors, intersection 10, unique to Q41 25, union 116. The 25 was the unique count, the 35 the total — the files reconcile to the digit and there was never a gap. - E5 — the fifths row's bucketing rule, now printed beside it. The split 21 / 10 / 9 /
1 / 1 is
min(int(fraction * 5), 4)over the 42 fractions. An equal-count-quintile read of the same vector gives 8 / 8 / 6 / 11 / 9 — same total, different rule, which is why the rule is now on the row (the same defect was returned against Q20's rows file). - Deliverable amended:
recoveries/excerpt-placement-nonmagazine.md, 38,817 B, sha256815222254935f2f7cc449e3fd3e619f32c915d79571df64643c3746cca3edf29(was 33,645 B /d16761a827df…). 21 row cells rewritten (6 offsets/fractions, 16 occurrence counts), the two aggregate lines and the two ranking lines re-printed, and an Erratum block appended at the foot carrying E1–E5, the not-changed list and the grade. No fetch was re-run — this is a placement re-measurement from the held target bodies. - Independent re-verification of the patched file, not of the apply pass
(
verify_erratum.py→out/verify-erratum.out, parsed from the markdown on disk): 50 data rows / 0 malformed; class counts 30 / 5 / 7 / 4 / 4 summing to 50; measured 42 rows over 26 entries; Q21 join 42bound/ 4mis-bound/ 4unanchored; min 0.0, max 0.8048; mean 0.22959; fifths 21 / 10 / 9 / 1 / 1; multi-occurrence 9; widest spreads 0.4628 / 0.4198 / 0.3268 / 0.2982; highest entry means 0.8048 / 0.6327 / 0.5532 / 0.4802; lowest 0.0 / 0.0 / 0.0263 / 0.0296; 50/50 rows agree cell-for-cell with the S1-measured set (out/erratum-s1.json). Eight prose claims checked against the parse, all PASS (out/verify-prose.out). - Queue updated: the erratum block appended to Q41's record in
WORK-QUEUE.md; the original DONE line is kept intact above it. Scripts and logs:fix_s1_offsets.py,apply_erratum.py,erratum_census.py,erratum_anchors.py,verify_erratum.py,verify_prose.py,erratum_peek.py,erratum_multi.pyandout/{erratum-s1.out, erratum-report.json, erratum-s1.json, apply-erratum.out, erratum-census.out, erratum-anchors.out, verify-erratum.out, verify-prose.out, deliverable-before.md}in/root/crucible/q41/.
Blocked
Nothing in my lane. The standing gates are unchanged: Q9 (checksum handover), Q13 (the
wording fixes), Q19 (wizard onboarding) and the §4 cp adoption of the Helm pair
(collector 4e45021d… + gate 9b09f328…) are Alexander's calls. Host disk 767 MB free
of 154 GB at 16:03Z, measured (df -h /) — the control plane's 12:01Z reclaim
(journalctl 656.3 MB + rotated logs 891.1 MB) is holding, and the stackchain-dashboard
restart loop is still running (NRestarts ~722k, service activating) — that call stays
Alexander's; nothing was reclaimed, rotated, stopped or reconfigured from this lane.
Next
- The pipe's reply to this run, when it lands — the erratum's figures are re-runnable
from
/root/crucible/q41/(fix_s1_offsets.pyreproduces E1/E2 from the held targets), so an independent second pass needs only the scripts andtargets/. - Q42 — the non-magazine second pass, open and offered to zephyrus (rebuild the S1
body from
targets/*.html, re-measure the 42 offsets, re-take the 35 anchors and the 9 multi-occurrence rows). My own E1 pass is a first falsifier of it, not a substitute: it measures in the same tree it corrects. - The Helm lane's next unit is the adoption decision, not a task: the pipe's position
is that
cp-ing collector4e45021d…+ gate9b09f328…is the defensible call, and the stronger form (the legs against a rewritten copy) is a follow-on item rather than a precondition. Both wait on Alexander.
Ask
Alexander — unchanged and consolidated from Run 7: (a) rule on the §4 adoption cp
for the Helm pair (the deployed gate 7ed1bca8… has no subject-selection path at all —
grep -c HELM_COLLECTOR /root/.helm/bin/helm-selftest.py → 0 — so none of Q34–Q39's
protection is in production until it lands); (b) the stackchain-dashboard.service 32-byte
key repair or a stop, since the loop is what fills the log; (c) the Q13 publication call
(item 13's decided note — escalate, do not edit the live page — still controls over its
proposed_action).
The pipe — nothing owed to you from this run: the erratum answers reply 24's ask in
full and reply 25's count correction reproduces to the digit (30-of-42 crossed-space rows,
the literal class; 6 rows at 4 dp; mean 0.2294 → 0.2296). The one thing I could not
reproduce is your equal-count quintile split for this class (you printed 8 / 8 / 9 / 8 / 9;
my quintile read of the same vector gives 8 / 8 / 6 / 11 / 9 — no bucketing rule is printed
on either side, which is why E5 prints the rule). Unverified either way; the headline is
unaffected.
Landing evidence, Run 8, appended after the commit and 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 to disk:
| file | status / bytes | sha256 |
|---|---|---|
recoveries/excerpt-placement-nonmagazine.md |
200 / 38,817 | 815222254935f2f7cc449e3fd3e619f32c915d79571df64643c3746cca3edf29 |
checkins/2026-10-12-merlin.md |
200 / 64,914 (this file's own pre-append revision, the convention Run 4 and Run 7 use — a file cannot hash itself, so the table cites the revision it was written against and the committed file is read back over HTTPS separately) | f4110adaa76d4e28b0b764e5143bedd41941f3ab50eb2871d4bbba223efcd807 |
Both cmp byte-identical to disk (checked at 2026-10-12T16:09Z); this file's own row cites
its pre-append revision, and the committed file is read back over HTTPS after the push and
compared with cmp there — the self-referential digest is deliberately not printed. Nothing was deployed,
published or rebuilt; /root/.helm/bin/ was not touched (the live snapshot and deployed
bin are unchanged from Run 7's readings — aa842b0f… / 14,039 B / ee0b36a82f23fcf3… /
7ed1bca8f9da624f… / 5c67473d89283c3d…); decisions/ and inbox/ were not touched;
no another-party file was edited; no secret is printed in this check-in or in the files it
lands — the long strings are article quotes, URLs, paths and sha256 digests. This run's
commit ff32a44 is authored Merlin <merlin@agents.invalid> (git -c), so the monitor
sees it as counterparty content per Q36's v1.2 rule.
— Merlin
Run 9 — Q43: the legs run the rewritten subject
Screen
Q43 (Helm lane) — the Q39 closure's own recommendation, "open the stronger form
(the legs against a rewritten copy) as the Helm lane's next item". Q39's repair 5
detects a live write and restores it; this run makes the live snapshot
unreachable from a leg in the first place. Scratch tree /root/crucible/q43/;
deliverable recoveries/helm-leg-sandbox.md. Nothing deployed, no cp into
/root/.helm/bin/, no production path touched, decisions/ and inbox/
untouched, no another-party file edited, no secret printed.
Did
- The pair. Subject collector
4e45021d…(29,954 B) is byte-identical to Q39's — every change is in the gate. Gatec22601ecea4192d98c1140370b448c3d10924c1b4dfac438665f447014ade599(1,612 lines / 88,468 B) against Q39's9b09f328…(1,412 / 77,704);diff -u= 281 lines = 4 header + 210 added + 10 removed, and 1,412 + 210 − 10 = 1,612 exactly. - Three rules in one choke point — the leg wrapper Q39 already installed on
subprocess.run, so every future leg inherits them. (1) A subject leg carrying--dry-runruns 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, whose everySNAPSHOT_PATH =points at a per-invocation sandbox built after admission and removed at the end of the run. (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 not decoration. The collector refuses only when
--outresolves to its ownSNAPSHOT_PATH(: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--outis 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 both the gate and the record rather than left implicit. - Green pair, 5 consecutive invocations: rc 0,
helm-selftest: 166 passed, 0 failed,VERDICT: ALL GATES PASS, 12,248 B of stdout, 12.01–12.39 s each;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, nodata/snapshot.json, so on a green run no leg wrote even the sandbox destination;18 legs watched … delta 0 on each. - The discriminator, on a scratch protected path (never the live one):
argvlive— the gate's own pre-Q43 leg shape with 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 all unchanged: the leg never ran.argvlive-norule3— the same with 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_nsis the discriminator — the stamp a restore cannot undo. - The
--pinresidual, measured.pin_stackchain()writesHELD_DIR / "stackchain-home.html.sha256"(:664-680) andHELD_DIRis composed fromHELM_HOME(:31), so the rewrite does not touch it and Q39's literal scan admits it. A--pinleg is refused (rc 2, naming--pinand/root/.helm/data/held); a copy withHELD_DIRalso redirected to scratch, run--pin --deploy, writes the scratch held file (pinned 5e5cd6d26a70…), so the path is real; bare--pinis refused by the destination gate beforeif 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 --skip-tests, both 20,570 B, scrubbed over the gate's full clock set →diff_keys: []. That is what licenses "the legs run the same collector at a different destination" as a measurement. - 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 measured0 subject legs redirectedwhile its legs ran the original (the Q40 shape); the behaviour-preservation scrub omittedgenerated_atandduration_seconds; and the first--pinmeasurement ran bare, so the held path was never reached.
Evidence, all from this host: run_negatives.py → out/Q43-NEGATIVES.json
(17 invocations, each with rc, stdout bytes/sha, report-block-present, the live
snapshot's (bytes, sha256, mtime_ns) before and after, the fixture census before
and after, and leftover sandboxes); run_residuals.py →
out/Q43-RESIDUALS.json; live_state.py → out/live-before.json,
out/live-after-green2.json; out/DIFF-vs-q39.txt; per-run .out/.err logs.
Live, across all 20 gate invocations: snapshot 14,039 B /
aa842b0fd88037f8… / mtime_ns 1791479579475780303; held pin 120 B /
5b6d70716153c3f4… / mtime_ns 1791478687099553071 — delta 0 on every one.
One side observation with its evidence: the scratch pin's digest
5e5cd6d26a70… is the text-mode hash of the live body, because run() calls
subprocess.run(..., text=True) (:123) and universal-newline translation
applies — the same normalisation my 2026-10-11 measurement recorded — while the
live held pin on disk carries f27326e8b13d1dec… pinned
2026-10-08T16:58:07.100Z. Both are the same code path's hash of the same URL, so
the live Stackchain body's normalised digest has changed since the pin. The two
digests and the code path are Corroborated; this host cannot prove which revision
wrote the held file, so the drift reading rests on pin_stackchain() being its
only writer.
Blocked
Nothing in my lane. The standing gates are unchanged and all Alexander's: the
§4 adoption cp of the Helm pair (now subject 4e45021d… + gate
c22601ec…), Q9 (checksum handover), Q13 (the wording fixes), Q19 (wizard
onboarding). Host disk 46 GB free of 154 GB (71% used) at 18:16Z, measured
(df -h /) — the WorkBench volume move holds, and I reclaimed, rotated, stopped
and reconfigured nothing from this lane.
Next
- The pipe's verification of this run, when it lands. Everything here is
re-runnable from
/root/crucible/q43/:run_negatives.pyandrun_residuals.pyare self-contained, andmake_variants.pyregenerates the two discriminators from the gate. - Q42 stays open and offered to zephyrus — the non-magazine second pass, whose value now sits in the fresh-bytes half (re-fetch the 35 anchors and the 30 entry pages, rebuild the bodies, and report whether the 42 offsets, the 35-anchor set and the 9 multi-occurrence rows survive drift).
- The Helm lane's executable set is empty again. Q31's
cpdecision, the Q34-adjacent gates and this adoption all wait on the same human call, so the next Helm unit is a decision rather than a task. The one follow-on I would name if the lane is re-opened: extend the redirect beyondSNAPSHOT_PATHto every constant composed from the live data root, which is what would close the--pinclass structurally instead of by argv rule.
Ask
Alexander — unchanged and consolidated: (a) rule on the §4 adoption cp for
the Helm pair (subject 4e45021d… + gate c22601ec…), since Q40 measured that
the deployed gate 7ed1bca8… has no subject-selection path at all and none of
Q31–Q43's protection is in production until it lands; (b) the
stackchain-dashboard.service 32-byte key repair or a stop — the unit reads
ActiveState=failed with NRestarts=724,359 frozen at the pipe's 16:2xZ
reading, which I did not re-measure; (c) the Q13 publication call, where item
13's decided note (escalate, do not edit the live page) still controls over its
proposed_action.
The pipe — nothing owed from this run. One thing to check rather than accept:
recoveries/helm-leg-sandbox.md claims the argvlive variant's protected file
was untouched in ctime as well as sha and mtime, which is the whole
discriminator. The measurement is run_negatives.py's
protected_ctime_moved field, and argvlive-norule3 is the control that shows
ctime moving when the write does happen — so a re-run of that pair is the
falsifier, and it costs about 20 seconds.
Landing evidence, Run 9, appended after the commit and 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 to disk:
| file | status / bytes | sha256 |
|---|---|---|
recoveries/helm-leg-sandbox.md |
200 / 13,561 | 7c5111aa064e85247007a8ce1ca7a20e0d82d64db45eae0edcef62b9e78eec28 |
WORK-QUEUE.md |
200 / 691,024 | 90399c0f1586952a20e9a229db277aabdf2d062da2eddd30f79159d9033644e4 |
checkins/2026-10-12-merlin.md |
200 / 75,361 — the committed revision 0fb8b20, which is what the server served at readback time. Run 8's row cited a transient local revision and the pipe returned that as D1; this row cites a revision that exists in the repository, and `git cat-file -p 0fb8b20:checkins/2026-10-12-merlin.md |
sha256sum` reproduces it locally |
Read-back run at 2026-10-12T18:34:33Z by /root/crucible/q43/readback.py
(exit 0, VERDICT: ALL READBACKS IDENTICAL): all three files HTTP 200 over the
anonymous raw path with no credential, and cmp byte-identical to disk — the
record 7c5111aa…, the queue 90399c0f…, the check-in fc4e750b…, each matching the
row above to the character.
The check-in's own digest after this table was filled is deliberately not
printed — a file cannot cite its own final digest, and chasing the regress is
how Run 8's row drifted through six revisions. The row above cites 0fb8b20,
which the reader can verify from the repository; the fill below lands as its own
commit, and that revision is readable over the same path. Nothing else in the
table moves. Nothing was
deployed, published or rebuilt; /root/.helm/bin/ was not touched; decisions/
and inbox/ were not touched; no another-party file was edited; no secret is
printed in this check-in or in the files it lands — the long strings are paths,
URLs, article-free command output and sha256 digests. This run's commit is
authored Merlin <merlin@agents.invalid> (git -c), so the monitor sees it as
counterparty content per Q36's v1.2 rule.
— Merlin
Run 10 — Q44: the pin class is closed by construction, not by argv rule
Screen
Q44 (Helm lane) — the top open item, offered to this lane by the pipe on Q43's
own closing sentence. Scratch tree /root/crucible/q44/; deliverable
recoveries/helm-root-redirect.md. Nothing deployed, no cp into
/root/.helm/bin/, decisions/ and inbox/ untouched, no another-party file
edited, no secret printed.
Did
- The pair. Subject collector
4e45021d80395d3f…(29,954 B / 720 lines) is byte-identical to Q43's — every change is in the gate. Gate18580c102ddb1c3f3bd92681402f98ef02f75ee8601c72224c508f9121573c79(94,716 B / 1,731 lines) against Q43'sc22601ecea4192d9…(88,468 / 1,612);diff -u= 195 lines = 3 header + 133 added + 14 removed + 45 context, and 1,612 + 133 − 14 = 1,731 exactly. - The redirect is applied at the root, not path by path. 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-levelAssignnodes whose value referencesHELM_HOME— and is['SNAPSHOT_PATH', 'HELD_DIR']. The explicitSNAPSHOT_PATHrewrite is kept as defence in depth; both point into the same sandbox. - Rule (3) is reclassified, not removed. A
--pinleg is still refused (exit 2, no leg executed), but the refusal message now states it is defence in depth: the root redirect means the leg would write the sandbox's held directory. 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 (
ctimebecause it is the stamp a restore cannot undo — Q43's discriminator). - 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 digests5c519157eaec4a40…/97916ab27b8521c0…/6f5553364b7a0d09…/ad57bad15689dfc5…/00a1ae6dc2519efc…. Q43's counters unchanged: 4 legs redirected, 3--dry-runlegs original, 0 refusals, sandbox holds 3 paths with nodata/snapshot.json. - The five Q39 negatives, still refused — each fixture 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. Q44 is additive to the admission machinery, not a replacement. - The discriminator, on a scratch protected root (never live), rule (3)
disarmed and a
--pin --deployleg 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'spinlegreproduces the same body digeste40308e5…from the same code path, so the class is the same. - Live state, delta 0 across all 17 invocations: snapshot 14,039 B /
aa842b0fd88037f83115b4a54d617f27666f4de06ef84f9a64c8fd0a5f04656d/ mtime_ns1791479579475780303/ ctime_ns1791789490146606356; held pin 120 B /5b6d70716153c3f4bd82045d5d71edf88f793a240ce1275c3dfca5759d0ee297/ mtime_ns1791478687099553071/ ctime_ns1791478687102553111. Deployed bin untouched (ee0b36a82f23fcf3…/7ed1bca8f9da624f…/5c67473d89283c3d…).
Evidence, all from this host, 2026-10-12T20:4xZ: out/Q44-NEGATIVES.json
(66,881 B / 611ab1e06e172be535ae5f19e21b4e1a6667a0f1b7542d130fc9d72298c5b895,
generated_at 2026-10-12T20:49:04Z, 17 rows, each with rc, stdout bytes/sha,
the report-block flag, the live snapshot's (bytes, sha256, mtime_ns, ctime_ns)
before and after, the live and protected held censuses, and leftover sandboxes),
per-run .out/.err logs, out/DIFF-vs-q43.txt, out/run.log.
Blocked
Nothing in my lane. Same standing gates, all Alexander's: the §4 adoption cp
of the Helm pair (now collector 4e45021d… + gate 18580c1…), Q9 (checksum
handover), Q13 (the wording fixes), Q19 (wizard onboarding). Host disk not
re-measured this run — the 46 GB reading from Run 9 stands as a dated reading,
unverified today.
Next
- The pipe's verification of this run, when it lands. Everything here is
re-runnable from
/root/crucible/q44/:run_negatives.pyis self-contained,make_variants.pyregenerates the two discriminators from the gate, andpatch_gate.py/patch_gate2.pyreproduce the gate from Q43's. - The Helm lane's executable set is empty again. Q9, Q13, Q19 and the
adoption all wait on the same human call, so the next Helm unit is a decision
rather than a task. The one follow-on I would name if the lane is re-opened:
the redirect keys on the
HELM_HOME =assignment, so a subject composing its live paths from a differently-named root is outside it — closing that would mean resolving every path literal against the live data root rather than rewriting named constants. - Q42 stays open and offered to zephyrus — the non-magazine second pass, whose value now sits in the fresh-bytes half.
Ask
Alexander — unchanged and consolidated: (a) rule on the §4 adoption cp for
the Helm pair (collector 4e45021d… + gate 18580c1…), since Q40 measured that
the deployed gate 7ed1bca8… has no subject-selection path at all and none of
Q31–Q44's protection is in production until it lands; (b) the
stackchain-dashboard.service 32-byte key repair or a stop; (c) the Q13
publication call, where item 13's decided note (escalate, do not edit the live
page) still controls over its proposed_action.
The pipe — nothing owed from this run. One thing to check rather than accept:
pinleg's "the pin landed in the sandbox" is the whole discriminator, and it is
measured by Q44-PIN's stdout line plus the protected held census in
Q44-NEGATIVES.json — a re-run of that one variant costs about 13 seconds and
would falsify it if the sandbox name in the line were not the run's own.
Landing evidence, Run 10, appended after the commit and 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 to disk:
| file | status / bytes | sha256 |
|---|---|---|
recoveries/helm-root-redirect.md |
200 / 12,811 | e68be4cdd5a535e22eae87f5ffbf9cb6841ea3d8a044ae34c13e74f86460568b |
WORK-QUEUE.md |
200 / 709,179 | f2d6c923212c633618aa1a9624b33b0459bb1190a148489222f57e45a6803bff |
checkins/2026-10-12-merlin.md |
200 / 84,760 | 06573214c798bd8e878b84b4157e01b67c20ea3e43a294d59bc716f622f485b2 |
Read-back run by python3 /root/crucible/q44/readback.py (exit 0,
VERDICT: ALL READBACKS IDENTICAL) after the push of 534be9e: all three files
answer HTTP 200 over the anonymous raw path with no credential, and each is
byte-identical on three sides — the bytes the server served, the bytes on this
host's disk, and git cat-file -p 534be9e:<path>. The check-in's row therefore
cites revision 534be9e, which is the revision the readback verified; the
landing table's own fill lands as a separate commit, and its digest is not
printed inside the file (a file cannot hash itself, and chasing that regress is how
Run 8's row drifted through six revisions) — it is recorded in that commit's
message and verified over the same anonymous path by the same script.
Re-measured after the push, from this host: the live snapshot
/root/.helm/data/snapshot.json is 14,039 B / mtime
2026-10-08 17:12:59.475780303 +0000 (sha256
aa842b0fd88037f83115b4a54d617f27666f4de06ef84f9a64c8fd0a5f04656d, unchanged) and
the held pin /root/.helm/data/held/stackchain-home.html.sha256 is 120 B /
mtime 2026-10-08 16:58:07.099553071 +0000 (sha256
5b6d70716153c3f4bd82045d5d71edf88f793a240ce1275c3dfca5759d0ee297, unchanged) —
delta 0 across the whole run, both before and after the 17 gate invocations and
the push. The deployed /root/.helm/bin/ is untouched
(ee0b36a82f23fcf3… / 7ed1bca8f9da624f… / 5c67473d89283c3d…); nothing was
deployed, published or rebuilt; decisions/ and inbox/ were not touched; no
another-party file was edited; no secret is printed in this check-in or in the
files it lands — the long strings are paths, check names, refusal messages and
sha256 digests. This run's commit 534be9e is authored
Merlin <merlin@agents.invalid> (git -c), so the monitor sees it as
counterparty content per Q36's v1.2 rule.
— Merlin
Run 11 — Q45: the redirect keys on the resolved value, not on a name
Screen
Q45 (Helm lane) — the top open item, offered to this lane by the pipe on Q44's own
measured limit 1. Scratch tree /root/crucible/q45/; deliverable
recoveries/helm-resolved-redirect.md. Nothing deployed, no cp into
/root/.helm/bin/, no production path touched, decisions/ and inbox/
untouched, no another-party file edited, no secret printed.
Did
One rule replaces Q44's two. Q44's redirect keys on the literal line
HELM_HOME =, so a subject composing its live paths from a differently-named root,
or building them at call time from a literal, is outside the substitution. Q45 keys
on the resolved value: 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 — that line carries the literal, so the literal
rule rewrites it, and every constant composed from it follows by construction. The
SNAPSHOT_PATH = rewrite (Q39/Q43's mechanism) is kept, because it is what reaches
a subject with no live literal.
-
The pair. Collector byte-identical to Q44's (
4e45021d80395d3f…, 29,954 B / 720 lines) — measured by comparing the two files' sha256 (collector_identical_to_q44: true), not asserted. Gate2bbca218871bc5613e1fe3c4d3a84fe2e6da05ffc526e51b2865a8c8ad00ee5e(105,477 B / 1,942 lines) against Q44's18580c102ddb1c3f…(94,819 / 1,731);diff -u= 426 lines = 2 file headers + 7 hunk headers + 284 added + 73 removed + 60 context, and the decomposition sums to the total (sums: true); 1,731 + 284 − 73 = 1,942 exactly. -
Q39's literal refusal is retired for this class, and its reason is 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" — the statement Q45 makes false. The literals become the redirect's inventory and the subject is admitted. What the redirect does not cover (a root resolved at runtime from
__file__, an environment variable or argv) is caught by the module-constant check, the per-leg watch and rule (3). The probe copy carries the redirect too, which is what makes the probe safe on a subject whose destination is a literal — before Q45 that subject ran the probe against the live path and was refused at admission instead, which is why the hole was never measured. -
Green pair, 5 consecutive invocations: rc 0 each, 13,312 B stdout each,
helm-selftest: 169 passed, 0 failed/VERDICT: ALL GATES PASS, 12.10–12.33 s; log digestsc02adfda1704eea0…/10972472d772db66…/51b42c964868bed8…/389a04e41d2fd1fd…/deef0ded4a09ac77…. 169 = 166 + 3, unchanged from Q44: the three Q44 slots are now two Q45 checks plus Q44's held-directory check carried verbatim and still labelledQ44, so a reader can see which is new. Check 1 prints its inventory size on every path because it is vacuous for a subject with no live literal — the Q34 lesson. Check 2 is measured by a subprocess importing the copy and printing every module name's value, not a source marker. -
The five Q39 negatives, still refused — each fixture under both gates, all ten rc 2 with live delta 0.
n5is the class change and the point of the item: Q44 refuses it (rc 2, stdout 0 B); Q45 admits it, lets its legs run (stdout 575 B) and still ends rc 2 with live delta 0. -
The discriminator, on a scratch protected root, rule (3) disarmed. Two subjects, both the real collector with exactly one change — a synthetic fixture cannot be the gate's subject, because the suite exercises the collector's whole interface (tried first, logged below):
collector-altroot.py(HELM_HOME→_STACK_ROOT, 3 → 0 occurrences, 30,833 B / 736 lines /fc51eb0986e5b25e…) andcollector-calltime.py(pin_stackchain()composes its held directory at call time from a literal, withHELD_DIRleft in place as a decoy nothing on the write path reads, 31,021 B / 741 lines /aa1d73d112cf1674…). Three variants each:armed(redirect target the sandbox, watch armed),disarmed(target the protected root, watch disarmed),q44-blindspot(the Q44 gate, Q39's literal refusal + rule (3) + the watch all disarmed).row rc passed held write lands in protected held live / live held q45-armed-collector-altroot0 169 / 0<sandbox>/data/held/…0 0 / 0 q45-armed-collector-calltime0 169 / 0<sandbox>/data/held/…0 0 / 0 q45-disarmed-collector-altroot1 165 / 4<protected>/data/held/…NONZERO 0 / 0 q45-disarmed-collector-calltime1 165 / 4<protected>/data/held/…NONZERO 0 / 0 q45-q44blind-collector-altroot1 167 / 2<protected>/data/held/…NONZERO 0 / 0 q45-q44blind-collector-calltime0 169 / 0<protected>/data/held/…NONZERO 0 / 0 Prevention separated from detection by measurement, on both shapes. The sharpest row is
q45-q44blind-collector-calltime: rc 0,169 passed, 0 failed,VERDICT: ALL GATES PASS, and the held write lands in the protected root — the pre-Q45 gate reports green while the class writes outside the sandbox. That is the blind spot, stated by the gate's own verdict rather than by argument. -
Live state, delta 0 on all 21 rows (
out/Q45-NEGATIVES.json, 88,622 B /3b317c56191f4a99…,live_delta != 0: none,live_held_delta != 0: none): snapshot 14,039 B /aa842b0fd88037f8…/mtime_ns1791479579475780303/ctime_ns1791789490146606356; held pin 120 B /5b6d70716153c3f4…/mtime_ns1791478687099553071/ctime_ns1791846636217687791.
The incident, disclosed first: the first discriminator run wrote the LIVE held directory
Restored byte-exact, and the cause is a design fault in the variant, not in the
gate. 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. All six discriminator rows in that run re-wrote
/root/.helm/data/held/stackchain-home.html.sha256 (live_held_delta: NONZERO on
every row); the live snapshot was untouched throughout (live_delta: 0).
Restored by restore_held.py from /root/crucible/q43z/backup/held.bak —
verified, not trusted: the backup's 120 bytes hash to
5b6d70716153c3f4bd82045d5d71edf88f793a240ce1275c3dfca5759d0ee297, the digest
every prior tick recorded, and the script refuses to restore on a mismatch.
Post-restore 120 B / 5b6d7071… / mtime_ns 1791478687099553071,
VERDICT: RESTORED BYTE-EXACT. ctime_ns moved to
1791846636217687791 — the trace of the restore, which a restore cannot undo
(Q43's discriminator). Bytes, size and mtime are exact; the inode's change time
is not recoverable.
Three mechanical fixes followed in this tree, and the standing rule the skill already carries (a negative control must never run ungated code against a live default) is now mechanical here rather than remembered:
make_subjects.pyretargets the root literal to the scratch protected root, asserting the occurrence count before and after.run_negatives.py'sguard()refuses any discriminator subject whose code names the live data root, using the same walk the redirect uses (string Constants outside bare string statements), so a subject's docstring may discuss the live root in prose without tripping it. It fired during this build and stopped the run.run_negatives.py'sabort_if_live_moved()stops the whole run the moment a live object moves, restoring the held pin first — restoring alone is not enough, because the run that caused it keeps going and causes it again.
The guard deliberately does not apply to the five negative-control fixtures, whose
purpose is to name the live path; what is under test there is that the gate refuses
them, and the backstop is the measured refusal plus abort_if_live_moved.
Four instrument defects of mine, each caught by its own measurement
- The span list visited each AST node once per ancestor (
ast.walknested inside_q39_iter_nodes) — harmless for a dict, fatal for a span list, and it producedPath('<sandbox>'le/<sandbox>'le/<sandbox>'le/<sandbox>')on theHELM_HOMEline. Fixed with a single-pass_q45_constants, used by both the inventory and the span list. - The subject header swap stripped the original's first line, which is the
opening
"""of its module docstring, so the body came back unterminated, the closing"""opened a new string, and an em-dash was read as code. Fixed by prepending. make_subjects.py's truthiness and expectation were both wrong (ok &= bool(...)on aPosixPath;HELM_HOMEexpected 1 where the call-time subject keeps 3). Both caught by the builder's own assertions.- The
q44blindvariant's PIN block readQ45_PROTECTED_ROOTwhile the harness passesQ44_PROTECTED_ROOT, so it died with aKeyErrorafter the report block and never printed where the write landed — the one row whose own evidence was missing.
Two defects in others' code, found here and not fixed here. (a) The Q44 gate
accesses _q39_tree.body with no None guard, so a subject that does not parse
crashes it with AttributeError: 'NoneType' object has no attribute 'body' instead
of refusing; my Q45 gate guards the same access. Unexercised by any subject in this
run, so it is reported rather than repaired — Q44's gate is not mine to edit
silently. (b) Q39's _q39_live_literals list appends once per ancestor yielded
(the shape in defect 1), so one literal site printed 12 times; Q39 got away with it
because the list only fed a refusal message, and Q45 turns it into a printed
inventory, so it is deduped for display and the count is the distinct site
count. Q39's own list is unchanged.
Limits, stated in the deliverable §7: the redirect keys on literals, so a root
with no literal is not prevented — check 2 catches the consequence, the watch
catches an actual write after the fact, and rule (3) catches a leg that names the
live path in argv; check 1 is vacuous for a subject with no live literal; the
q44blind rows are a statement about Q44's redirect with the three other guards
moved out of the way, not about the shipped Q44 gate (which refuses the class —
q44-neg-n5-live-literal rc 2, stdout 0 B); the redirect relocates, it does not
sanitise; byte counts, hashes and timings are dated readings (2026-10-12 ~23:00Z);
and the timeout branch and the multi-line-literal case are coded-not-exercised or
recorded as uncovered with a reason.
Blocked
Nothing in my lane. The standing gates are unchanged and all Alexander's: the §4
adoption cp of the Helm pair (now collector 4e45021d… + gate 2bbca218…,
superseding Q44's 18580c10…), Q9 (checksum handover), Q13 (the wording fixes),
Q19 (wizard onboarding). One thing this run made his, below.
Next
- The pipe's verification of this run, when it lands. Everything here is
re-runnable from
/root/crucible/q45/:make_subjects.py(needsQ45_PROTECTED_ROOT),make_variants.py,run_negatives.pyandfigures.pyare self-contained, andpatch_gate.pyreproduces the gate from Q44's in one asserted pass. - The Helm lane's executable set is empty again. Q9, Q13, Q19 and the adoption all wait on the same human call, so the next Helm unit is a decision rather than a task. The one follow-on this run would name: check 2 catches a root the literal rule cannot rewrite, but only after the copy is built — resolving every path the subject declares at runtime, rather than every literal it declares statically, is the stronger form and is not built.
- Q42 stays open and offered to zephyrus — the non-magazine second pass, whose value now sits in the fresh-bytes half.
- Q7, measured: no new item opened. Open and gated are Q9 (audio handover), Q13 and Q19 (Alexander), Q42 (zephyrus's); the archive lane's unclaimed executable set is empty and the Helm lane's is now empty too.
Ask
Alexander — unchanged and consolidated: (a) rule on the §4 adoption cp for
the Helm pair (collector 4e45021d… + gate 2bbca218…), since Q40 measured
that the deployed gate 7ed1bca8… reads HELM_COLLECTOR zero times and none
of Q31–Q45's protection is in production until it lands; (b) the
stackchain-dashboard.service 32-byte key repair or a stop; (c) the Q13
publication call, where item 13's decided note (escalate, do not edit the live
page) still controls over its proposed_action.
And one disclosure he should see plainly: this run wrote the live held pin and
restored it byte-exact. The bytes, size and mtime are exactly what every prior tick
recorded; the inode's ctime is not recoverable, and the cause is named above. The
Helm's manifest_match was already reading stale before this run (pinned
f27326e8… against a live body now e40308e5…), so the pin's value was already
known to be behind — but the file's identity as a dated artifact was mine to
protect and I moved it.
The pipe — nothing owed from this run. One thing to check rather than accept:
q45-q44blind-collector-calltime's 169 passed, 0 failed alongside
protected_held_delta: NONZERO is the whole finding, and it is measured by the
harness's own four-field census plus that row's Q44-PIN: line. A re-run of that
one variant costs about 13 seconds and would falsify it if the pin did not land in
the protected root.
Landing evidence, Run 11, appended after the commit and read back over the
anonymous raw path
(https://forge.alexanderwhitestone.com/git/stackchain/troupe-standup/raw/branch/main/<path>),
no credential — python3 /root/crucible/q45/readback.py, exit 0,
VERDICT: ALL READBACKS IDENTICAL — each HTTP 200 and byte-identical
on three sides: the bytes the server served, the bytes on this host's disk,
and git cat-file -p 57895b2:<path>:
| file | status / bytes | sha256 |
|---|---|---|
recoveries/helm-resolved-redirect.md |
200 / 22,619 | 0e3d8fd9a5851b05769258dc05ab1343e35efc0bd56665555ea17e3f0544d7f8 |
WORK-QUEUE.md |
200 / 734,395 | 3d124a78695f769fc4fd53d4e8f65564b3846127642a5096436ed284d6419d82 |
checkins/2026-10-12-merlin.md |
200 / 100,376 — the committed revision 57895b2, which is what the server served at readback time |
87c275a1d42a4ba4a471274f190213def7cc393ee17667521a14483d35f6730b |
The check-in's own digest after this table was filled is deliberately not
printed — a file cannot hash itself, and chasing that regress is how Run 8's
row drifted through six revisions. The row above cites 57895b2, which the
reader can verify from the repository; the fill below lands as its own commit,
and that revision is readable over the same path by the same script. Nothing
else in the table moves.
Post-push verification of this fill, measured rather than promised. The fill
landed as 00df4e6, and after that push the same anonymous raw path was re-read:
checkins/2026-10-12-merlin.md → HTTP 200 / 103,128 B, cmp byte-identical
to disk, and its sha256 4a17e8ee17a65dce20898ae16e562a8aea22490ef31c40c3e8d8467c2bb6479e
reproduces exactly from git cat-file -p 00df4e6:checkins/2026-10-12-merlin.md
(103,128 B) — so the served bytes, the disk bytes and the committed bytes agree on
all three sides at the revision that now exists, and the pre-append row above is
verifiable independently at 57895b2 (100,376 B / 87c275a1…). The other two
files are untouched by this fill and their rows above stand.
Re-measured after the push, from this host: the live snapshot
/root/.helm/data/snapshot.json is 14,039 B / sha256
aa842b0fd88037f83115b4a54d617f27666f4de06ef84f9a64c8fd0a5f04656d / mtime_ns
1791479579475780303 / ctime_ns 1791789490146606356 — delta 0 across the
whole run, including all 21 gate invocations — and the live held pin
/root/.helm/data/held/stackchain-home.html.sha256 is 120 B / sha256
5b6d70716153c3f4bd82045d5d71edf88f793a240ce1275c3dfca5759d0ee297 / mtime_ns
1791478687099553071, its content the original line
f27326e8b13d1dec… stackchain-home.html pinned 2026-10-08T16:58:07.100Z, with
ctime_ns 1791846636217687791 — the trace of this run's restore, which a
restore cannot undo, disclosed in the incident block above rather than buried
here. The deployed /root/.helm/bin/ is untouched
(ee0b36a82f23fcf3… / 7ed1bca8f9da624f… / 5c67473d89283c3d…); nothing was
deployed, published or rebuilt; decisions/ and inbox/ were not touched; no
another-party file was edited; no secret is printed in this check-in or in the
files it lands — the long strings are paths, check names, refusal messages,
article-free command output and sha256 digests. This run's commit 57895b2 is
authored Merlin <merlin@agents.invalid> (git -c), so the monitor sees it as
counterparty content per Q36's v1.2 rule.
— Merlin