14 KiB
Q33 — Split the newest-file pointer out of evidence.source_path
Screen. Every claim below carries a path, a digest, a URL, or a command with its output, or the word
unverified. Nothing secret is printed here or in the files this lands.
Merlin, 2026-10-11 ≈22:0x–22:3x UTC. Scratch tree /root/crucible/q31/; re-runnable with
python3 q33-paired.py then python3 q33-before-after.py (the gate:
HELM_COLLECTOR=/root/crucible/q31/helm-collect.py python3 helm-selftest.py).
Did
Claimed and finished Q33 — split the newest-file pointer out of evidence.source_path (the queue's
only OPEN item, opened 2026-10-11 by the pipe on Q32's residue; named to the Helm lane. Q20 is
zephyrus-sparkborn's, Q9 blocked on the audio handover, Q13/Q19 gated on Alexander).
The change, three edits in the collector:
profiles[]:evidence.source_pathwaslatest_path or str(entry)— the newest file under the profile'ssessions//cron/output//logs/, mutable by construction. Now"source_path": str(entry)(the profile directory, a stable citation) with the pointer beside it as"latest_active_path": latest_path(Nonewhen the profile has no files).members[]:evidence.source_pathwasinfo["file"]— the member's own newest check-in, i.e. the pointer. Now"source_path": str(d)(the check-ins directory the roster is enumerated from) with the pointer as"newest_checkin_file": info["file"].- The module docstring states the rule:
evidence.source_pathis a stable citation; a newest-file pointer is never smuggled into it.
Design decision, stated because the queue's parenthetical is loose. The queue writes that
source_path becomes "a stable citation for every block — profile directory or the member's own
check-in file". The member's own check-in file is the newest file — that is the pointer, so it cannot
also be the citation. The stable citation for a member block is therefore the check-ins directory (the
exact analogue of the profile directory), and the pointer is preserved in full as
evidence.newest_checkin_file. No information is lost: the before/after dump below shows the member's
check-in file moving from source_path to newest_checkin_file, value intact.
The pointer lives inside evidence (with observed_at and source_path) rather than as a sibling
of the profile/member dict: it qualifies the evidence block, and the gate's scrub nulls by key name at
any depth, so the declared-key mechanism works unchanged.
The gate change: the Q32 pointer carve-out — which nulled evidence.source_path inside any dict
carrying a live mtime reading — is deleted. SCRUB_KEYS gains the two named pointer fields as
ordinary declared keys, and eight new behavioural G8 checks plant a newer file between two collects of
scratch trees and assert the pointer field moves while evidence.source_path does not.
Identities (sha256, this host):
| artifact | before | after | lines | net |
|---|---|---|---|---|
helm-collect.py |
8a477376f72c578b… (29,085 B) |
4e45021d80395d3f03fc58d7e99610e3c1c0d2fcf9c85f1db50b16cfd2043c6c (29,954 B) |
705 → 720 | +18 / −3 |
helm-selftest.py |
9ce6a4e971525fdd… (48,468 B) |
4a442f625961a1308302d5606b6b9eb64409cbfd472aeb1293caa7f580ad7fa3 |
868 → 929 | +107 / −47 |
Diffs: DIFF-Q33-COLLECTOR.txt, DIFF-Q33-SELFTEST.txt (scratch). Pre-change copies preserved as
helm-collect-q33-before.py / helm-selftest-q33-before.py (verified byte-identical to the identities
above before any edit).
Consumer walk, done in the same run (the queue's Status note required it):
| consumer | what it reads | verdict |
|---|---|---|
/root/.helm/bin/helm-render.py:264 |
top-level evidence.source_path per source card |
unaffected — the six top-level citations (dirs, files, command strings) are unchanged; the per-profile rows render last_active_at (:291) and the member table renders last_checkin_date / last_checkin_at (:354-356), never the nested source_path |
/root/.helm/bin/helm-validate.py:73-74 |
requires evidence.source_path present in every block |
passes — python3 /root/.helm/bin/helm-validate.py q33-after.json → OK: q33-after.json — valid, all green-rule checks passed, no secrets detected, exit 0 |
/root/.helm/schema/helm-snapshot.schema.json |
$defs.evidence — required observed_at/source_path, no additionalProperties: false |
no change needed — the two new keys are admitted by the open object; required keys unchanged |
/root/.helm/bin/helm-selftest.py (deployed) |
fixture snapshots with literal source_path values |
unaffected — the fixtures name top-level citations, not per-profile pointers |
/root/troupe-harness/mercury-q31-summary.py, mercury_standup.py, /root/troupe-verify/q32/timmy-verify-q32.sh |
read the snapshot for verdict/harness purposes | no read of the changed fields (grep for `source_path |
No other consumer of helm.snapshot/1 exists on the host: grep -rln 'helm.snapshot' over /root
(--include py/js/sh/json, excluding node_modules/.git/crucible/pycache/sessions) returns exactly
the schema, the live snapshot, helm-validate.py, helm-selftest.py, helm-collect.py and one verify
fixture.
Evidence (measured, not accepted)
The gate discriminates — the pre-Q33 collector fails all eight new checks.
HELM_COLLECTOR=/root/crucible/q31/helm-collect-q33-before.py python3 helm-selftest.py → exit 1,
helm-selftest: 151 passed, 9 failed (log q33-gate-old-collector.log). The nine failures: the eight
Q33 behavioural checks plus the declared-key source check naming the two absent literals. Three of the
eight, verbatim from the log:
FAIL G8 Q33: a profile's evidence.source_path is the profile directory
-> '/tmp/tmp86csye9n/q33-split/profiles/alpha/sessions/new.jsonl'
FAIL G8 Q33: evidence.source_path does not move when a newer file lands
'/tmp/tmp86csye9n/q33-split/profiles/alpha/sessions/new.jsonl' -> '.../newer.jsonl'
FAIL G8 Q33: the member's own newest check-in is emitted as evidence.newest_checkin_file -> None
That is the defect, live: source_path is the newest file, and it moves when a newer file lands.
Six consecutive green invocations, each 160 passed / 0 failed.
HELM_COLLECTOR=/root/crucible/q31/helm-collect.py python3 helm-selftest.py, runs 1–6, all exit 0,
helm-selftest: 160 passed, 0 failed, VERDICT: ALL GATES PASS. Log digests:
| run | log | sha256 (first 16) |
|---|---|---|
| 1 | q33-gate-run-1.log |
0e5dedcd1df85867 |
| 2 | q33-gate-run-2.log |
c5cdfb2ca21a0528 |
| 3 | q33-gate-run-3.log |
788ad40353a1a1302 |
| 4 | q33-gate-run-4.log |
a0409de02a9da27e |
| 5 | q33-gate-run-5.log |
fd49ec1a538c143a |
| 6 | q33-gate-run-6.log |
7319abcb2dea80ae |
All eight Q33 checks PASS in run 1 (names in the log): source_path-is-the-directory (profile, member), pointer-emitted-as-its-own-field (both), source_path-does-not-move (both), pointer-moves (both).
Eight paired runs, zero differing leaf paths, no pointer carve-out.
python3 q33-paired.py (Q33-PAIRED.txt): the gate's own _scrub is extracted from the selftest
source by AST, so the paired script cannot drift from the gate; the script asserts the carve-out is
gone ('source_path=None' not in src) and both pointer keys are declared. 8 pairs, 0 differing leaf
paths under the gate's scrub. Controls: the pre-Q32 scrub set reds 1 of 8 pairs on
sources.cron_jobs.age_seconds / sources.hermes_profiles.age_seconds (the age-boundary class is
still live without the declared keys — the defect the Q32/Q33 keys close); the pre-Q33 collector under
the same new scrub showed 0 differing leaf paths in 3 pairs, which is stated honestly: the
pointer's volatility is rare (no file landed mid-pair), so the paired-level control did not fire —
the non-vacuity proof is the gate's planted-file checks, which are red on the old collector by
construction.
Before/after on the same live data (python3 q33-before-after.py → Q33-BEFORE-AFTER.txt): the
pre-Q33 collector and the patched one both run to scratch --out paths, never the live default.
pre-Q33 collector helm-collect-q33-before.py: 29085 B, sha256 8a477376f72c578b…
patched collector helm-collect.py: 29954 B, sha256 4e45021d80395d3f…
pre-Q33 snapshot q33-before.json: 18523 B, sha256 b050b356bd08a131…
patched snapshot q33-after.json: 20141 B, sha256 4f736f5498c08f75…
live snapshot UNCHANGED: 14039 B, sha256 aa842b0fd88037f8…, mtime_ns 1791479579475780303
One profile block's evidence, before → after (names/values from the dump; the full block is in the log):
BEFORE: {"observed_at": "2026-10-11T22:17:26.503Z", "source_path": "<profile>/logs/gateway.log"}
AFTER: {"latest_active_path": "<profile>/logs/gateway.log",
"observed_at": "2026-10-11T22:17:28.341Z", "source_path": "<profile>"}
One member block's evidence, before → after:
BEFORE: {"observed_at": "…", "source_path": "/root/troupe-standup/checkins/2026-10-07-cassian-q5-census.md"}
AFTER: {"newest_checkin_file": "/root/troupe-standup/checkins/2026-10-07-cassian-q5-census.md",
"observed_at": "…", "source_path": "/root/troupe-standup/checkins"}
Key delta: +24 keys / −0 — 20 × profiles[].evidence.latest_active_path + 4 ×
members[].evidence.newest_checkin_file. Live counts in the patched fresh collect: 20/20 profiles
carry a non-None latest_active_path and a directory source_path; 4/4 members carry a non-None
newest_checkin_file and the check-ins directory as source_path.
Live snapshot delta 0 across the whole run (before and after every leg):
14,039 B / sha256 aa842b0fd88037f83115b4a54d617f27666f4de06ef84f9a64c8fd0a5f04656d / mtime_ns
1791479579475780303. Deployed bin unchanged: helm-collect.py ee0b36a82f23fcf3…,
helm-selftest.py 7ed1bca8f9da624f…, helm-render.py 5c67473d89283c3d…. Nothing deployed;
decisions/ and inbox/ untouched; no another-party file edited; no X page fetched.
Blocked
Nothing blocked the run. Limits, stated not hidden:
- Adoption is Alexander's, and the deployed tree still carries the old shape. Until the
cp, production emitssource_pathas a pointer and the deployed gate keeps the Q32 carve-out. The scratch pair is complete and gated; the publish gate is §4. - The renderer does not display the pointer. A deliberate minimal-change decision: the renderer's evidence line shows the stable citation, which is the honest thing to print; showing the newest file beside it is a renderer change nobody asked for. Named as an option, not done.
- The pointer's
Nonecase is untested against a live empty profile (a profile with no files undersessions//cron/output//logs/emitslatest_active_path: null). The gate's planted tree always has files; the live collect has 20/20 non-None.unverifiedagainst a real empty profile. - No fuzzer. The behavioural checks plant two deterministic file shapes; the guarantee is stated over those shapes plus the two-run agreement check, not over an exhaustive input census.
- The gate runs with
--skip-tests(the standing Q31 caveat).
Defects of mine, logged first
- The first version of the four pointer checks read the wrong nesting level. They queried
_pr1.get("latest_active_path")— the profile dict's top level — while the collector emits the pointer insideevidence. The checks failed-> Nonewhile the field was present one level down (found by instrumenting the gate:_pr1printed with the field set while the comparison returned None). Same class as the 2026-10-11card()incident: the check read the wrong object, the gate looked broken, and the failure message was misleading. Fixed by moving the reads to["evidence"], and the five green-run logs produced by the buggy gate were discarded and regenerated — the six logs in the table above are all from the fixed file, identity4a442f62…. The debug prints were removed before the final runs. - The paired-level before-control did not fire (0 differing leaf paths in 3 pairs on the pre-Q33 collector). Stated above rather than re-rolled until it went red: the non-vacuity proof is the gate's planted-file checks, which fail deterministically on the old collector.
Next
- Alexander's call, §4: adopt the pair into
/root/.helm/bin/— collector4e45021d…+ selftest4a442f62…(superseding8a477376…+9ce6a4e9…). This is the artifact Q31's adoption ask has been waiting on; after it, collect+render scheduling is safe to take on its own evidence. Scratch until he says so. - Q7, measured: Q20 open and unclaimed (zephyrus-sparkborn's named lane); Q9 blocked on the audio
handover; Q13/Q19 gated on Alexander. The unclaimed executable set is empty again, so a successor is
owed; this record opens none — the same standing pattern as Q26–Q32, and the natural candidate would
be this run's own residue (the renderer's pointer display, or the
latest_active_path: nullcase), which the successor should open on a fresh measurement.
Ask
- Alexander — adopt
4e45021d…+4a442f62…into/root/.helm/bin/? It is the difference between a snapshot whosesource_pathmeans two different things and one whose citations are stable, with a gate that proves the split behaviourally. Scratch until you say so. - Nothing asked of zephyrus-sparkborn this tick. His lane is Q20.
— Merlin, 2026-10-11