catalog gates: one entry point, mandated in CLAUDE.md (R-161 ruling)
scripts/catalog_gates.py runs all three gates - image-pins, image-resolvable, volume-persistence - and exits non-zero if any fails. Mandated in CLAUDE.md the way felhom.eu/scripts/site_gates.py is: run it after any template change, naming the app(s) you touched. Operator ruling, recorded because both alternatives were rejected for measured reasons. Controller-side enforcement at template load was rejected because such a check can only read the file, and a static audit of all 53 templates reports the catalog clean INCLUDING papra - it would pass on the exact defect it exists to catch; the property is decidable only at runtime. CI was rejected for now: neither repo has any, and there are no users yet. What was chosen copies the shape that demonstrably works here - of this project's gates, the only ones that ever get run are the ones with a single entry point named in a CLAUDE.md; site_gates.py is run, and R-29's three orphans are named nowhere and have stopped nothing. Behaviour: 0 all clean / 1 convicted / 2 UNDETERMINED, never a pass; a conviction outranks an undetermined result so the reader knows which they have. Gate output is streamed, not captured. App names scope the two gates that accept scoping; with no names the runtime gate deploys every template and belongs on a scratch host. Adding a fourth gate means one line in GATES. R-161 stays OPEN at reduced scope: this is convention, run by a person. Real automatic enforcement is owed when a second person touches templates. Verified: image-pins passes standalone (53 templates, 0 unpinned), the unknown-option path exits 2, and the aggregation was unit-checked over five gate-code combinations. The runtime leg was deliberately NOT executed - it deploys templates via docker compose and DooPlex is the recovery chain - so the runner's end-to-end invocation of that third gate is inferred, not measured, and is flagged in REPORT.md to be closed on a scratch host at the next campaign. REPORT.md overwritten per convention; the persistence sweep's report is preserved at audits/persistence-sweep-2026-08-02/ and pointed to from the new one.
This commit is contained in:
@@ -1,5 +1,33 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-08-02 — one entry point for the catalog's gates (R-161 ruling)
|
||||
|
||||
`scripts/catalog_gates.py` runs all three gates — image-pins, image-resolvable, volume-persistence —
|
||||
and exits non-zero if any fails. Mandated in `CLAUDE.md` the way `felhom.eu/scripts/site_gates.py` is:
|
||||
**run it after any template change**, naming the app(s) you touched.
|
||||
|
||||
**Operator ruling, recorded because the alternatives were rejected for measured reasons.**
|
||||
Controller-side enforcement at template load was rejected: such a check can only read the file, and a
|
||||
static audit of all 53 templates reports the catalog clean **including papra** — it would pass on the
|
||||
exact defect it exists to catch. CI was rejected for now: neither repo has any, and there are no users
|
||||
yet. What was chosen copies the shape that demonstrably works here — of this project's gates, the only
|
||||
ones that ever get run are the ones with a single entry point named in a CLAUDE.md; `site_gates.py` is
|
||||
run, and R-29's three orphaned gates are named nowhere and have stopped nothing.
|
||||
|
||||
Behaviour: `0` all clean · `1` convicted · `2` UNDETERMINED, **never a pass**; a conviction outranks an
|
||||
undetermined result in the summary so the reader knows which they have. Gate output is streamed, not
|
||||
captured — a runner that swallows diagnostics makes a conviction unreadable. Scoping passes app names
|
||||
through to the two gates that accept them; with no names the runtime gate deploys every template and
|
||||
belongs on a scratch host.
|
||||
|
||||
**R-161 stays OPEN at reduced scope:** this is convention, run by a person. Real automatic enforcement
|
||||
is owed when a second person touches templates.
|
||||
|
||||
Verified: `image-pins` passes standalone (53 templates, 0 unpinned); the unknown-option path exits 2;
|
||||
the aggregation was unit-checked over five gate-code combinations. **The runtime leg was deliberately
|
||||
NOT executed on DooPlex** — it deploys templates via `docker compose`, and DooPlex is the recovery
|
||||
chain; it belongs on a scratch host.
|
||||
|
||||
## 2026-08-02 — persistence sweep: does every app's data land in a folder the template preserves?
|
||||
|
||||
Campaign 10's R-156 found papra writing its database into the container's writable layer while the
|
||||
|
||||
Reference in New Issue
Block a user