main
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
560f0d4451 |
G-1: a gate for the dropped field — built first, and seen failing on 40
Campaign 12 ranked this first of eight gating candidates. It is built BEFORE the fixes it finds, because last night an off-the-shelf tool for a neighbouring class (deadcode, for C6) was made to prove itself first and found NEITHER of the two defects it was meant for. A gate nobody has watched fail has not been shown to work. scripts/wire_contract_gate.py, registered in repo_gates.py as --fast (no network, no container, so it runs in BOTH the pre-push hook and CI — the R-29 constraint). THE TEST. For every json tag reachable from a declared wire ROOT, does that literal tag occur anywhere in the receiving repo's production Go or templates? A tag occurring nowhere cannot be decoded by any struct, named OR anonymous. That last clause is why a string test is used instead of comparing struct to struct: Campaign 12's first attempt paired types by shape and false-positived badly, because the hub decodes one report through several ad-hoc anonymous structs. RESULT ON TODAY'S TREE: 210 tags checked across 3 declared wires, 51 skipped (generic / opaque / allowlisted), 40 CONVICTED. Captured verbatim in documentation/tests/wire-contract-gate-2026-08-08/ BEFORE.md, which is deliverable 1 of this session. The prompt for this session said "465 emitted tags, eight unreachable". Checked against the repo rather than quoted: R-260's wording was "at least eight DECISION-BEARING facts", not eight tags in total. The real count on the three declared wires is 40, and R-260's own census already listed more than eight. Recorded because this prompt's own rule 6 says not to quote a document as source. TWO THINGS THE CONTROL CAUGHT, both before the gate was trusted: 1. A SUBSTRING FALSE NEGATIVE. `grep -F healed_at` also matches `privsep_healed_at`, so a genuinely dropped field read as received — and R-260 named healed_at, so its absence from the output was the tell. Now a whole-token regex; healed_at is convicted. 2. dr_recipe IS NOT WHOLLY OPAQUE. The hub stores each half as json.RawMessage and re-emits nested shapes verbatim, so the LEAVES are genuinely not on this wire. But the TOP-LEVEL SECTION KEYS are decoded by hostHalfShape/appHalfShape, and those are ALLOW-LISTS: a section an emitter adds is silently dropped until named in both. That already cost `offsite_restic` (R-122). So the gate is opaque BELOW depth 1, not opaque — the sections are checked and pass. Self-test: `--selftest` plants an unreachable tag on a real root in a throwaway copy and asserts conviction. Verified: exit 1, planted tag named. Blind spots are in the module docstring AND in the gate's own output, because Campaign 12's C1 guard turned out blind to one of the three shapes it was written for: generic tag names are not checked; reachability of a NAME is not use of a VALUE; only declared ROOTS are covered, and the hub's desired-state (served as raw stored JSON, no typed emitter) and the agent local API are NOT. Allowlist entries carry a stated reason. A quiet exclusion is a dropped field with paperwork. Not pushed alone: the fixes follow in the next commit so main is never red on this check. |
||
|
|
3ca9a7bbe6 |
R-242: build the gate the rule described — a release without a golden now fails the push
gates / gates (push) Failing after 13s
R-242 was filed 2026-08-07 as a mechanism-less rule and RECURRED WITHIN A DAY: controller v0.206.0 shipped the R-241 fixes while the vouched golden still carried 0.205.0, so a machine installed this morning would have received neither. Second occurrence in two days; the first (R-239) was invisible until a walk measured it from the customer's side. SHOWN FAILING FIRST, against today's state, before anything was baked - that is the gate's red-proof and the whole point of building it before the bake: newest released controller : 0.206.0 newest golden baked : 0.205.0 GOLDEN CURRENCY GATE FAILED ... A machine installed right now would receive v0.205.0 - the release is written, tested and pushed, and NOT delivered. Entry point exits 1; summary reports CONVICTED: golden-currency. *** THIS PUSH USED --no-verify, to push past the gate's OWN conviction. *** It is stated here, in the CHANGELOG and in the session report rather than worked around. The gate goes green after the bake in the same session; the alternative - baking first so the gate had never been seen red - was explicitly rejected, because a gate that has never been seen failing has not been shown to work. IT IS --fast, AND THAT FORCED THE DESIGN. Both the pre-push hook and CI run repo_gates.py --fast, which by contract selects only gates touching no network. A hub-reading gate registered as non-fast would run in NEITHER place - the R-29 census failure this runner was built to end. SO IT CHECKS THE BAKE, NOT THE VOUCH. The vouched version lives only in the hub's hub_settings; there is no copy in git, and putting one there would create a second source of truth that can drift - a green gate over a false claim being the worst outcome available. A bake without a vouch still passes. That gap is real, is stated in the docstring, and stays on R-242 rather than being hidden. The recurrence this gate exists for was a missing BAKE. IT COMPARES VERSIONS, NOT BEHAVIOUR, so a release that changed nothing customer-visible also trips it. Accepted deliberately: judging "customer-visible" by hand is what failed twice, and the cost of a false trip is one bake. A waiver belongs in the register, never in a habit of bypassing. Inconclusive (exit 2) on an absent controller clone or an unparseable header: not knowing is never a pass. |
||
|
|
3a9dd81e18 |
docs+gate: felhom.eu/CLAUDE.md becomes core + path-scoped rules; instructions gate registered (R-229)
gates / gates (push) Successful in 8s
227 -> 115 effective lines, split into .claude/rules/{hub,website,manifests,docs}.md, and
repo_gates.py gains gate 6. Trim first, register second: a registered-but-failing gate refuses
every push through the pre-push hook, which is why this repo -- the one that OWNS the gate --
was the only one not running it.
Register discipline and the R-110 installer fence deliberately stayed in the core; both have
triggers no fixed glob covers, and scoping them would have rebuilt the failure class they exist
to prevent.
Scoping proven from the InstructionsLoaded hook log in two fresh sessions, not from frontmatter.
|
||
|
|
9bd1a54d71 |
gates: one entry point (scripts/repo_gates.py) + pre-push hook
A census of all thirteen gate scripts across the four felhom repos on 2026-08-02 found one clean correlation: every check a CLAUDE.md tells a person to run was passing, and two of the four nobody is told to run were failing — one since 14 July. Neither failure was harmful in effect (checked line by line); nothing would have said so if they had been. The fix is not more gates, it is one place to run them from. repo_gates.py runs site + hostinstall + hub-confirm + manifest-bearer + reuse-refs, streams each gate's own output, and exits worst-wins non-zero. A missing gate script is a FAILURE and prints the path tried — fail-closed, because a runner that quietly skips a gate is the inert-seam failure this project has shipped four times. It copies catalog_gates.py (R-161), NOT site_gates.py, which is a gate and not a runner. .githooks/pre-push runs it with --fast and refuses the push. Honest limits are written into the hook itself: per-clone (core.hooksPath is local config), and --no-verify bypasses it on purpose. Any manual run WARNS when the clone is unarmed. Measured on git 2.47.3: a relative core.hooksPath resolves correctly and the hook's cwd is the repo root from any subdirectory. test_repo_gates.py is a SEAM test — it asserts each member gate's own distinctive stdout, not the runner's summary line, which an inert runner prints while calling nothing. Red-proofed: replacing run_gate's body with 'return 0' still prints 'all felhom.eu gates OK' and exits 0, and turns the seam test red. |