From 650cc8a4a7ab236f2c6533827d5b22af6b5b0908 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Sat, 8 Aug 2026 09:12:29 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20record=20the=20CI=20episode=20=E2=80=94?= =?UTF-8?q?=20three=20instrument=20defects,=20and=20the=20two-homes=20rule?= =?UTF-8?q?=20(R-260)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gate's third instrument defect and the reusable lesson underneath it: a gate needing a sibling clone passes on a workstation and is INCONCLUSIVE in CI, and a gate must not shell out to a tool the CI image may not have. Three red runs (260-262), each of which mailed the operator, before 263 went green. CONTEXT S-38 gains the two-homes rule. --- CONTEXT.md | 9 +++++++++ REPORT-wire-contract-gate.md | 28 ++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index d68db48..8269ee9 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -41,6 +41,15 @@ Campaign 12 tried it. section keys are decoded by `hostHalfShape` / `appHalfShape`, and those are allow-lists that already silently swallowed `offsite_restic` for months (R-122). The sections are checked. +5. **A NEW GATE MUST BE CHECKED IN BOTH AUTOMATED HOMES.** The pre-push hook runs on a workstation + where every sibling repo is a real clone; CI checks out ONE repo, shallow. A gate needing a + sibling therefore passes locally and exits 2 INCONCLUSIVE in CI — measured here, three red runs + before it was right. Give the gate what it needs in `.gitea/workflows/gates.yml`; **never let it + skip when a sibling is absent**, which is the fail-open shape and leaves it running in neither + home (R-29). And **a gate must not shell out to a tool the CI image may not have**: this one used + `grep --include`, which the runner's grep does not support, and read the empty output as a + finding — convicting all 174 tags. Pure-Python searching now. + **Scenario F's choice, and why (the same ruling's second half).** When the receiving side gains a field, the absence of that field from an older emitter must not read as a value. `operator_key_configured` decodes as a **pointer**: nil means the agent never said, and is reported **distinctly** from a diff --git a/REPORT-wire-contract-gate.md b/REPORT-wire-contract-gate.md index 531bae9..99e2562 100644 --- a/REPORT-wire-contract-gate.md +++ b/REPORT-wire-contract-gate.md @@ -76,12 +76,36 @@ turned out blind to one of the three shapes it was written for: `--selftest` plants an unreachable tag on a real root in a throwaway copy and asserts conviction: **exit 1, planted tag named**; unplanted tree **exit 0**. -**Two instrument defects the CONTROL caught before the gate was trusted:** +**THREE instrument defects this gate's own controls caught before it was trusted.** None was found +by review; each was found by making the gate prove something. 1. **A substring false negative** — `grep -F healed_at` also matched `privsep_healed_at`. R-260 named - `healed_at`, so its absence from the output was the tell. Now a whole-token regex; 40 not 39. + `healed_at`, so its absence from the output was the tell. Whole-token now; 40, not 39. 2. **`dr_recipe` is not wholly opaque** — its top-level section keys ARE decoded, through allow-lists that already swallowed `offsite_restic` for months (R-122). Now opaque only **below depth 1**. +3. **The search shelled out to `grep` and read its failure as a finding.** CI convicted **all 174** + checked tags while the pre-push hook was green. The CI runner's image carries python3 and git and + deliberately little else, and its `grep` does not support `--include`, so stdout was empty and + empty was read as "absent". **A gate that silently turns a tool failure into a finding is worse + than no gate**, and its green would have been as untrustworthy as its red. Removed the dependency + rather than working around it: the search is pure Python now, one token index per receiving repo. + +**The BEFORE capture was RE-VERIFIED, not re-generated** — the stronger claim. All 40 recorded fields +were re-tested against the new implementation: **agree=40, disagree=0**, i.e. exactly the four this +session fixed are now present and the other 36 still absent. The number stands under both +implementations. + +**And the reusable half, which is about the gates and not about this gate.** The pre-push hook runs on +a workstation where every sibling repo is a real clone; CI checks out one repo, shallow. **A gate +that needs a sibling passes locally and is INCONCLUSIVE in CI — the two automated homes are not +interchangeable, and a new gate must be checked in BOTH.** The workflow's own alarm mail says a +hook-versus-CI disagreement "outranks whatever the push was for"; it did. Fixed by fetching the agent +clone in CI (`.gitea/workflows/gates.yml`), never by letting the gate skip when a sibling is absent — +that is the fail-open shape and would leave it running in neither home (R-29). + +**Cost, stated plainly:** three CI runs went red (260, 261, 262) and each sent the operator an alarm +mail before run **263** went green. The alarm working is the system behaving correctly; the noise was +mine. ## 6. What `oobDegraded` says when it fails