Q35's precondition reads a source marker (--deploy and SNAPSHOT_PATH.resolve() in the subject's source), so a subject that documents the rule in a comment without implementing it is admitted — the pipe measured exactly that at 02:4xZ today. The gate now, at helm-selftest.py:117-211, after the Q35 check and before the subject import (:784) and G8's bare-refusal leg (:1079-1081, the Q34 incident's leg), runs a copy of the subject's source with its SNAPSHOT_PATH = assignment rewritten to a sentinel path as a subprocess bare, and refuses the subject (exit 2, no leg runs) unless that run exits non-zero, writes nothing, and leaves the live snapshot delta 0. The probe never imports the subject into the gate's process. Measured this run (scratch tree /root/crucible/q38/, nothing deployed): - 7 consecutive green invocations, exit 0 each, 164 passed / 0 failed and ALL GATES PASS in every log, the Q38 OK line present in all seven, live snapshot sha256+mtime read around every invocation with delta 0 throughout. - Discriminator on both sides: fake-documented-no-rule.py (both markers in its docstring, so the Q35 predicate over its source is true) exits 2 with the refusal naming probe rc=0 and the sentinel written (74 B / 817e3830...); fake-marker-only.py (the pipe's measured shape) exits 2 at the assignment step; the deployed collector exits 2 at the Q35 source check with zero Q38 lines — the ordering. The admission control: fake-marker-only.py under the Q35 gate runs G7 and dies at the subject import (AttributeError, helm-selftest.py:688) — the admission measured end to end. - Gate 1,044 -> 1,141 lines (+97/-0; DIFF.txt 108 lines / ac8e52bb...), sha256 643e5387... -> 74aba348...; collector byte-identical to the Q34/Q35 pair (4e45021d..., 720 lines). - Live snapshot /root/.helm/data/snapshot.json 14,039 B / aa842b0f... / mtime_ns 1791479579475780303, delta 0 across every leg; deployed bin untouched (ee0b36a8 / 7ed1bca8 / 5c67473d); decisions/ and inbox/ untouched; no another-party file edited. Section 4 consequence: the adoption pair is now collector 4e45021d... + gate 74aba348... (superseding 643e5387...); the cp into /root/.helm/bin/ remains Alexander's.
10 KiB
The behavioural destination guard — Q38 (Helm lane, 2026-10-12)
Q35 made the destination-rule guard a precondition, but the precondition reads a source
marker: the strings --deploy and SNAPSHOT_PATH.resolve() present in the subject collector's
source. A subject that documents the rule in a comment without implementing it passes that
check and is admitted. The pipe measured exactly that on 2026-10-12T02:4xZ: a fake collector
whose body is import sys; sys.exit(0) plus a comment carrying both strings ran the whole Q35
suite to completion with live delta 0. Q38 makes the guard behavioural: the subject is
executed against a sentinel destination and must refuse.
Nothing is published, nothing is deployed. The shared build tree, /root/.helm/bin/, the live
snapshot, decisions/ and inbox/ are untouched; every figure below came from a command run in
this pass. Scratch tree /root/crucible/q38/.
The guard
At helm-selftest.py:117–211 — after the Q35 source-marker check (:96–114) and before the
subject is imported (:784) and before 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 is written to the gate's own scratch tree, with its
SNAPSHOT_PATH =assignment rewritten to a sentinel path (<gate dir>/helm-q38-*/snapshot.json, created per invocation, removed at exit). - The copy is run as a subprocess, bare:
python3 <probe> --skip-tests— no--out, no--deploy, 180 s timeout. The subject's module-level code never executes inside the gate's process (the Q35 mechanism probe imported and rebound in-process; this one does not). - The subject passes iff the run exits non-zero and writes nothing to the sentinel and the live snapshot is byte- and mtime-identical before and after. Otherwise the gate exits 2 with a stderr message naming the subject, the probe path, the probe rc, the sentinel's byte count and sha256, and the live snapshot's before/after readings — and no leg runs. A subject that exits 0 and writes nothing is admitted: it cannot clobber the live snapshot bare, which is the property under test.
- A subject carrying no
SNAPSHOT_PATH =assignment at all is refused at step 1 — the destination the rule keys on does not exist, and such a subject crashes the gate at the import (:784) anyway.
The Q35 source check is kept as the fast first filter and fires first (measured below); the
behavioural check is the authority. The precondition registers no check(), so the green pair
still reports exactly 164 passed / 0 failed; the positive side prints one Q38 OK line on stderr
in every gate log so it is visible rather than implicit.
Gate: 1,044 → 1,141 lines, +97/−0 (git diff --no-index --numstat; DIFF.txt 108 lines /
sha256 ac8e52bb38090c66c8592a4aee9b39667d0cbbf6fefb8567043bf1e3ed0d588d), sha256
643e53874e42f7ae… → 74aba34860b34ac6aebd8661e3e46c01ed8c15dcec1cd958ec766114abfa1ef0.
The collector is byte-identical to the Q34/Q35 pair: 4e45021d80395d3f…, 720 lines, 29,954 B.
Both fixtures and the runner (run-green.sh) live in the same tree.
The subjects and the matrix
| subject | Q35 source check | Q38 behavioural | outcome |
|---|---|---|---|
pair 4e45021d… (/root/crucible/q38/helm-collect.py) |
pass | pass (probe rc=2, sentinel not written, live delta 0) | admitted — 7× green |
fixtures/fake-documented-no-rule.py |
pass (both markers in its docstring) | fail (rc=0, sentinel written) | exit 2, no leg |
fixtures/fake-marker-only.py (the pipe's measured shape) |
pass | fail (no SNAPSHOT_PATH = assignment) |
exit 2, no leg |
deployed /root/.helm/bin/helm-collect.py (ee0b36a8…) |
fail (no markers) | not reached | exit 2 at Q35 — the ordering |
Green pair — 7 consecutive invocations
HELM_COLLECTOR=/root/crucible/q38/helm-collect.py python3 helm-selftest.py, exit 0 each,
helm-selftest: 164 passed, 0 failed and VERDICT: ALL GATES PASS in every log, and the
Q38 OK: the subject refused its sentinel destination (probe rc=2, sentinel written=False, live delta 0) line present in all seven (run-green.sh reads the live snapshot's sha256 and mtime
around every invocation; delta 0 on all seven). Log digests:
f606bf3877cd6a96… / 4cd6456713f2e1ff… / 02ecbf989f2d162c… / 94781f8dfa0f4ac8… /
a2380b7ab4c61435… / fbd5e11713a6c048… / d0cae43b79b4f5f1…
The logs differ run to run in the temp paths only (the Q35 tick's C15 class); the verdict line is identical in all seven and is what the digests are quoted for.
fake-B — documents the rule, writes its default anyway
HELM_COLLECTOR=/root/crucible/q38/fixtures/fake-documented-no-rule.py python3 helm-selftest.py
→ exit 2, stdout 0 bytes (e3b0c442…, the empty-file digest — no helm-selftest: summary
and no PASS line exists, which is how "no leg executed" is measured), stderr
(q38-refuse-fakeB.err, sha256 38dcd0e40b05f9ba…):
Q38 REFUSAL: the subject collector … carries no WORKING destination rule — a bare run against the sentinel exited rc=0 (stderr: ); sentinel written=True (74 B /
817e38302aff70659ac8f49bd1607a7843748a917eec97dc01582d95c932f437); live delta 0 — so no leg is executed.
Its docstring carries both marker strings, so the Q35 predicate over its source is true
(measured: deploy ×2, SNAPSHOT_PATH.resolve() ×1 by line count on the fixture) — it is
admitted by the source check and refused by behaviour. Its unrewritten default destination is
deliberately not the live snapshot: the unrewritten file is never executed by this gate, but a
non-live default means even a false admission during measurement could not clobber live state.
The real-world subject this models — the deployed collector — has the live snapshot as its
default, and that is the shape Q34's incident ran.
fake-B was not run under the Q35 gate. Its G8 legs include a bare run (:982 in the Q35
file) and an explicit-live---out run (:994), either of which writes the live snapshot with an
ungated subject — the accident class itself. The admission was measured by the predicate
directly, and the end-to-end admission is shown by fake-C below.
fake-C — the pipe's measured shape, admitted by Q35, refused by Q38
HELM_COLLECTOR=/root/crucible/q38/fixtures/fake-marker-only.py python3 helm-selftest.py
→ exit 2, stdout 0 bytes, stderr (q38-refuse-fakeC.err, sha256
b3ab82a71d170998…): "carries no WORKING destination rule — the subject carries no
SNAPSHOT_PATH = assignment, so its default destination is not the module constant the rule
keys on — so no leg is executed."
The same subject under the Q35 gate
(HELM_COLLECTOR=… python3 /root/crucible/q35/helm-selftest.py, q35-admit-fakeC.log, 841 B /
sha256 691155d4b99dee0b…) ran G7 to completion and then died at the subject import —
AttributeError: module 'helm_collect_gate' has no attribute 'SNAPSHOT_PATH' at
helm-selftest.py:688 — which is the admission, measured: the Q35 precondition passed the
subject and the gate proceeded past it. Under Q38 the same subject never reaches the import.
Ordering — the deployed collector is still refused at Q35
HELM_COLLECTOR=/root/.helm/bin/helm-collect.py python3 helm-selftest.py → exit 2, stdout 0
bytes, stderr (q38-refuse-deployed.err, sha256 2aaa36c1f8277e90…, byte-identical to the Q35
tick's own q35-refuse-deployed.err) carrying the Q35 REFUSAL line and zero Q38 lines: the
source-marker check fires first, the behavioural check is not reached, live delta 0.
Live state, measured before and after 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 each of the 7 green runs, each of the 3 refusals, and the Q35 admission control. The
sentinel temp directories are removed at exit (0 left behind).
Deployed bin untouched: helm-collect.py ee0b36a82f23fcf3… (687 lines / 27,907 B),
helm-selftest.py 7ed1bca8f9da624f… (609 / 33,647), helm-render.py
5c67473d89283c3d… (496 / 21,766). Nothing deployed, nothing rebuilt, nothing published;
decisions/ and inbox/ untouched; no another-party file edited; no secret printed here or in
the files this lands.
Limits, stated
- The probe substitutes the module's
SNAPSHOT_PATH =assignment. A subject that resolves its destination by another route — a hardcoded path, an environment variable — is not covered by the substitution; the live-delta-0 assertion would catch an actual write only after the fact. The pair keys on the module's ownSNAPSHOT_PATH(Q31/Q35 measured that rebinding does not disable the rule), so the substitution is sound for this artifact family; it is not a general sandbox. - The refusal criterion is rc ≠ 0 and nothing written and delta 0. A subject that refuses with rc = 0 while writing nothing is admitted — correct, since it cannot clobber the live snapshot bare — but "refuses" is defined by the non-zero exit, not by a message.
- The timeout branch (rc
None) is coded and not exercised by a fixture. - The guard protects the gate's legs, not a direct CLI run of a pre-gate collector — the Q35 limit, unchanged.
- The bare-run leg now admits only behaviourally gated subjects, but the shipped gate is still
the pre-Q38 one: the §4
cpis Alexander's, and with this run the adoption pair becomes collector4e45021d…+ gate74aba348…(superseding643e5387…).
Artifacts
/root/crucible/q38/: helm-selftest.py (the patched gate), helm-collect.py (the pair,
unchanged), fixtures/fake-documented-no-rule.py, fixtures/fake-marker-only.py,
DIFF.txt, run-green.sh, q38-gate-run-1..7.log, q38-refuse-fakeB.{log,err},
q38-refuse-fakeC.{log,err}, q38-refuse-deployed.{log,err}, q35-admit-fakeC.log.
— Merlin, 2026-10-12