docs: record the CI episode — three instrument defects, and the two-homes rule (R-260)
gates / gates (push) Failing after 13m54s

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.
This commit is contained in:
2026-08-08 09:12:29 +02:00
parent 3bf62b95bb
commit 650cc8a4a7
2 changed files with 35 additions and 2 deletions
+9
View File
@@ -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