- reproduced the defect first: python3 helm-render.py exits 1 with
NameError: name 'build_page' is not defined (guard at :90-91 above
def build_page at :232), same with --out, no file written
- moved the __main__ guard to the end of helm-render.py (:495 of 495);
--out/--as-of semantics unchanged; fixed renderer 21,766 B sha256
5c67473d89283c3d4434928dc96d45ff48a41d8f443ecc3c98e9236be8b597f9
- destination gate per the 09:21Z control-plane addendum: OUT_DEFAULT is
production, so main() resolves the destination and refuses without
--deploy (exit 2, message naming the path and flag); parse_args now
returns (out, now, explicit_out, deploy)
- gate G7: the CLI executed in a subprocess, asserting emitted bytes
(exit 0, file exists, bytes equal build_page()'s at the pinned clock,
byte count matches, --as-of honoured, live root untouched); production
destination refused by default and by explicit --out at the prod path
(resolve(), not string equality); --deploy branch exercised against a
patched OUT_DEFAULT; source-order guard so the defect cannot return
- gates re-run: 119 passed, 0 failed (102 before; G7 adds 17); the gate
discriminates - the same command against the pre-fix file exits 1
- CONTRACT.md appended append-only: transcripts, digests, gate, and a
revision note on :213-214 (deploy is now --deploy)
- nothing deployed: live page 9,865 B / 427b7b54c69b18d2... and snapshot
aa842b0fd88037f8... unchanged before and after every run; --deploy
alone never executed
recoveries/helm-render-aging.md — the served Helm page (9,865 B / sha256
427b7b54c69b18d270288910fc810732463129f48ec4bdd7dbc75cdb22997ce4, read
2026-10-11T08:53:51Z) carried five false FRESH badges and a "5 green" pill on a
snapshot 2d 15h 40m old, against its own rule "green = measured OK and fresh".
The honest reading was 0 green / 6 not green. The same page served
manifest_match: true while a fresh collect returns False (live
5e5cd6d26a700423... vs held f27326e8b13d1dec...). No Helm cron job exists.
helm-render.py now re-derives every badge from each source's own observed_at
against the collector's threshold table (imported, not copied), recomputes the
verdict, and stamps the page with its snapshot age, a STALE READING/CURRENT
banner naming the stalest source and its ratio, and a per-card evidence line
carrying the limit each reading aged past. --as-of pins the render clock.
Roster identity was filename_body.split("-")[0], which registers
zephyrus-sparkborn as two members; it is now the longest roster prefix, with
unrecognised bodies reported in troupe_standup.unrecognised_bodies rather than
silently attributed. Fresh collect: 4 members, unrecognised_bodies: [].
The site digest was unreproducible from outside: run() uses text=True, so
live_body_sha256 is over the CRLF->LF-normalised body. 217,162 wire bytes /
9a5ee1328d4a3fedd... vs 217,149 normalised / 5e5cd6d26a700423..., delta 13 = the
13 CRLF pairs, lone CR 0, raw.replace(CRLF,LF) == normalised. The snapshot now
records live_body_sha256_wire and names the rule; both reproduce via
curl | sha256sum and curl | tr -d '\r' | sha256sum.
helm-selftest: 102 passed, 0 failed, six gates. Three defects of mine are logged
first in the deliverable: (1) the first renderer ran at module scope, so
importing it overwrote the production page — reverted byte-exact from a pre-run
copy and re-verified; (2) build_page() referenced an undefined SNAPSHOT and
78/78 passed because nothing called it; (3) card() was passed the evidence
sub-dict instead of the source block, so no card printed its limit. A gate that
does not execute the path it protects is not a gate — G5 renders real HTML at a
pinned clock and asserts on the emitted bytes.
Nothing deployed: /var/www/alexander-whitestone/helm/index.html unchanged at
9,865 B / 427b7b54c69b18d2... throughout; staged pages live in
/root/crucible/helm/. Deploy is one command and Alexander's under section 4.
/root/.helm/data/snapshot.json not clobbered (aa842b0fd88037f8... before and
after). No secret printed; decisions/ and inbox/ untouched; no other party's
file edited; no X page fetched.
32 emit sites in build_blocks.py enumerated from the source (28 esc calls plus the 4
prose panels): 28 covered by fixture, 4 stated structural exclusions. 35 fixtures,
every negative exit 1 with 0 files written (4 valid-panel builds expand, 12
malformed-panel dies, 4 uncited dies, 10 non-panel field dies, 2 URL-field dies, 3
class-field dies, head/index/pager/gap/genesis, 1 no-token control).
URL-field scope decided: a token in a URL must die - safe_url rejects the non-https
body_source_url value, the pre-write sentinel scan catches the https-with-token and
excerpt cases, all exit 1 with 0 files.
The queue's scope clause closed with option (a): check_uncited() runs above OUT.mkdir
so all four token classes are pre-write (the uncited fixture wrote 20 files before the
patch, 0 after, same message).
No-token build still byte-identical at 91ecf6bf (125 files, diff -rq vs the shared
site-ledger silent). Shared tree untouched at 97fedb5d with blocks.json 63c9cee6; live
index 200 / 34,488 B / 4f764ef4dfb9. Patched builder c4127c6c is scratch-local,
nothing staged; decisions/ and inbox/ untouched.