diff --git a/REPORT.md b/REPORT.md index efaa77f..d454fd3 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,283 +1,179 @@ -# REPORT — R-404 / R-417: block the push that can act, notify the one that cannot (2026-09-01) +# REPORT — the decoy sweep: can a gate be fooled by a label? (2026-09-01, R-421) -**No version bumped, no image built, no golden owed.** This changes no Go code. Creating a release -here would have created the exact debt the task is about. +## The survey — every gate, its shape, and whether a decoy passed BEFORE this session -## 1. The git stdin format, measured +| gate | runner(s) | meant to prove | actually matched | shape | decoy passed? | +|---|---|---|---|---|---| +| emoji | ctrl | no emoji in UI copy | codepoints, `listdir` scope | 1 | **YES → fixed** | +| native-confirm | ctrl | no OS-modal dialogs | JS regex, `listdir` scope | 1 | **YES → fixed** | +| app-row-dedup | ctrl | one row markup | regex + `not in src`, `listdir` | 1, 2 | **YES ×2 → fixed** | +| template-id | ctrl | JS ids resolve | id sets, `listdir` scope | 1 | **YES → fixed** | +| secret-markup | ctrl | no secret in markup | template actions, `listdir` | 1 | **YES → fixed** | +| retrieval-promise | ctrl | promises registered | stems, `listdir` scope | 1 | **YES → fixed** | +| hub-confirm | eu | no OS-modal dialogs | JS regex, `listdir` scope | 1 | **YES → fixed** | +| manifest-bearer | eu | no bearer literals | 64-hex, `listdir` scope | 1 | **YES → fixed** | +| observations | eu, ctrl, agent | a finding is filed | `FILED:` anywhere in body | 2 | **YES → fixed (R-419)** | +| debug-routes | ctrl | controls resolve | raw text, comments included | 2, 3 | **YES → fixed** | +| closed-register | eu | closed rows are closed | verdict cell; **skipped unparseable rows** | 2 | **YES → fixed** | +| site | eu | pages well-formed | a 7-entry `PAGES` list | 1 | **YES → R-423** | +| one-register | eu | open work registered | state cell; `idea` escapes | 2 | **YES → R-424** | +| offbox-rename | ctrl | branding retired | a fixed 3-entry `FILES` list | 1 | **YES → R-425** | +| reuse-refs | eu, ctrl, agent | citations resolve | only 7 extensions | 1 | **YES → R-422** | +| mojibake | ctrl | no mojibake | bytes, `os.walk` | 5 | NO — **the control** | +| docker-v | ctrl | `-v` mounts safe | argv, `os.walk` | 5 | NO | +| image-pins | catalog | no floating tags | real `image:` refs | 5 | NO | +| golden-currency | eu | a golden was baked | `GOLDEN_SHA256` in the log | 5 | NO (R-410's fix holds) | +| golden-notice | ctrl | ditto, mirrored | imports the gate above | 5 | NO | +| instructions | eu, ctrl, agent | instruction files sane | effective text | 5 | NO | +| hub-copy | eu | retired names gone | `os.walk` over hub/internal | 5 | NO | +| release-complete | agent | a release is complete | tag + ancestry + HTTP HEAD | 5 | NO | +| hostinstall | eu | installer invariants | — | ? | **UNKNOWN** | +| wire-contract | eu | emitted fields decode | — | ? | **UNKNOWN** | +| due-checks | eu | dated checks fire | — | ? | **UNKNOWN** | +| published | agent | versions published | — | ? | **UNKNOWN** | +| image-resolvable | catalog | images exist | `docker manifest inspect` | 5 | **UNKNOWN** | +| volume-persistence | catalog | data survives | runs containers, diffs | 5 | **UNKNOWN** | -Against **git 2.47.3** on DooPlex, with a throwaway bare remote (removed; its removal is recorded in -§12). A pre-push hook receives, on **stdin**, one line per ref: ` -`, four whitespace-separated fields. Observed directly, not read from documentation: +## 1. Gate count -| case | line | +**29 distinct scripts, 35 registrations** — `reuse-refs`, `instructions` and `observations` are one +script each registered in three runners (35 − 6 = 29). **Agrees with the task's 29.** Runner counts +13 / 14 / 5 / 3 also match. + +## 2. Three numbers + +**19 sound · 16 holes · 6 unknown.** (Sound + holes exceeds 29 because 6 of the 16 were fixed and are +now counted sound; the after-state is 29 = 19 sound + 4 open holes + 6 unknown.) + +- **Fooled: 16.** **Fixed this session: 10.** **Left open with a row: 4** (+2: R-427, R-426). +- **UNKNOWN: 6** — no plausible decoy was constructed. Named in §4. **Not called sound.** + +## 3. Every live hole, its decoy, its fix, its row + +| gate | decoy (the label without the fact) | fix | row | +|---|---|---|---| +| emoji, native-confirm, app-row-dedup, template-id, secret-markup, retrieval-promise, hub-confirm, manifest-bearer | one file planted in a new `partials/` (or `overlays/`) subdirectory, carrying exactly what each gate hunts | `os.listdir` → `os.walk` | R-421 | +| observations | *"it carries no `FILED:` and no `NOT-A-FINDING:` marker"* — prose about the markers | marker must start a line or follow a sentence boundary; inline code spans stripped | **R-419 CLOSED** | +| debug-routes | a live dispatcher case commented out; the button survives | strip Go and HTML comments before matching | R-421 | +| app-row-dedup (2nd) | `` | strip HTML comments in the MUST_USE check | R-421 | +| closed-register | a row with no state cell — **four existed**, two written the day before | unreadable row now CONVICTS, was a warning | R-421 | +| reuse-refs | a non-existent `.md` citation | **not fixed** — needs a false-positive pass over 4 repos | **R-422** | +| site | a new `website/*.html` absent from `PAGES` | **not fixed** — needs the exemptions rethought | **R-423** | +| one-register | a defect parked under state `idea` | **not fixed** — declared in its own docstring | **R-424** | +| offbox-rename | banned branding in a new offbox template | **not fixed** — fixed `FILES` list | **R-425** | + +**The one cause behind eight:** scope set by `os.listdir`, one level. Green *and correct* today — +blind the moment anyone adds a subdirectory. **`mojibake` and `docker-v` already walked, caught the +identical planted file, and are the control that proves the cause was the listing, not the decoy.** + +## 4. Gates with no plausible decoy — the honest unknown + +| gate | why not | |---|---| -| ordinary push | `refs/heads/master 0bb77614… refs/heads/master bb88be35…` | -| **first push of a ref** | `refs/heads/master bb88be35… refs/heads/master 0000000000000000000000000000000000000000` | -| two refs at once | two lines, one per ref | -| **deletion** | `(delete) 0000000000000000000000000000000000000000 refs/heads/side 0bb77614…` | +| hostinstall | asserts installer invariants against a shell script; a legitimate decoy needs a shape a real edit would produce | +| wire-contract | 607 lines comparing emitted fields to receiver structs across two repos; needs a Go edit, forbidden here | +| due-checks | my attempt was a no-op; its verdict was **withdrawn**, not reported | +| published | network gate; needs a fake registry | +| image-resolvable | needs a container runtime and the network | +| volume-persistence | 877 lines that actually run containers and diff them | -Both all-zero cases classify as **code**, fail-closed: a first push has no range to diff and a -deletion has no content. +**This list is itself the finding** (R-426 group d): six gates whose soundness is *untested*, not +established. -**My instrument failed first and I nearly believed it.** The initial probe printed nothing at all. I -had pushed `main` while `git init` had created `master`, so the hook never ran and my grep matched an -empty stream. An empty grep is not evidence — the raw output said `src refspec main does not match -any`. Re-run on the real branch, all four cases above appeared. +## 5. Files and commits -## 2. The classifier against real history — 18/6, exact agreement +| repo | commit | what | +|---|---|---| +| `felhom-agent` | `205e22b` | CHANGELOG + CLAUDE.md pointer. **No gate changed.** | +| `app-catalog-felhom.eu` | `29edad9` | CHANGELOG + CLAUDE.md pointer. **No gate changed.** | +| `felhom-controller` | `681cc66` | 7 gates fixed, `test_gate_decoys.py` (10 decoys), CHANGELOG, `.claude/rules/gates.md` | +| `felhom.eu` | `574f5df` | 4 gates fixed, `test_gate_decoys.py` (12 decoys), `decoy_coverage_gate.py`, audit, register, CONTEXT, CLAUDE.md | -Last 24 commits ending at the task's baseline `a91c058`: +## 6. The meta-gate's exemption list — 20 names, owned by R-426 -**docs = 18 · code = 6.** The task's §2 measurement was 18/6. **No disagreement.** +**(a) covered in the sweep, not yet in a suite:** `hub-copy`, `instructions` (eu), `docker-v`, +`image-pins`. +**(b) blocked by an open hole, so cannot be asserted as rejecting:** `site` (R-423), `one-register` +(R-424), `offbox-rename` (R-425). +**(c) shared scripts, counted in `felhom.eu`:** `reuse-refs` ×2, `instructions` ×2, `observations` ×2 +(controller + agent). +**(d) no plausible decoy yet:** `hostinstall`, `wire-contract`, `due-checks`, `published`, +`image-resolvable`, `volume-persistence`. -The six code pushes: `22e1c95` (the R-410 gate fix), `1aeaa30` (hub v0.110.0), `6e550ae` -(closed_register_gate), `66156c6` (R-403 evidence + a credential reader), `77a5a11`, `99af997`. +**Red-proof run:** a fake gate registered with no decoy → the meta-gate exits 1 and names +`felhom.eu / brand-new-gate`. Reverted byte-identical. -One refinement to §2's prose: it says **five** of the documents-only pushes were bake records. I -count **six** — `4f87517`, `db0812b`, `1623a4d`, `83ff9e8`, `2263245`, `63eff21`. The point is -strengthened, not weakened: the push that pays the debt is documents-only, and it happened six times -in twenty-four. +## 7. Which of the five defining rows closed -## 3. Files created / modified - -**felhom.eu** — `1c00af6` (code), `1f74427` (docs), plus the register/docs commit below. - -| file | what | +| row | outcome | |---|---| -| `scripts/push_scope.py` | NEW — the classifier | -| `scripts/test_push_scope.py` | NEW — P1–P5 | -| `scripts/test_repo_gates_scope.py` | NEW — R1–R6, Scenario C | -| `scripts/repo_gates.py` | fifth `exemptible` field, `--scope=`, `ADVISORY`, advisory block, tee'd `run_gate`, docstring drift fixed | -| `.githooks/pre-push` | reads stdin, passes `--scope=`, honest-limits header extended | -| `.gitea/workflows/gates.yml` | same rule in CI from the push event payload | -| `documentation/runbooks/target-selection.md` | the drill-night line | -| `CONTEXT.md`, `STATUS.md`, `scripts/CHANGELOG.md`, register | the ruling | +| **R-419** | **CLOSED** — fixed and pinned, verified in both directions | +| R-410 | already closed; its fix **re-verified** by decoy (the empty dir is rejected *and named*) | +| R-400 | already closed; but its gate had a **second door** — a commented-out case — now shut | +| R-378 | **stays open.** Its row sits in `OPEN-ITEMS.md` with a verdict reading `CLOSED 2026-08-22`; it is one of the twelve in **R-427** and moving it is a judgement I did not make | +| R-94 | already closed; **not re-verified** — it is a test, not a registered gate, and outside this sweep's denominator | -**felhom-controller** - -| file | what | -|---|---| -| `controller/scripts/golden_notice.py` | NEW — advisory, imports the sibling gate | -| `controller/scripts/test_golden_notice.py` | NEW — N1–N4 | -| `controller/scripts/controller_gates.py` | fifth `blocking` field; the notice registered non-blocking | -| `CHANGELOG.md` | an entry with **no version heading** | -| `REUSE.md` | how to register a reporting-only gate | - -## 4. Test results, and the three red-proofs by name - -All pass. - -| test | cases | -|---|---| -| `test_push_scope.py` | P1 (11 doc paths) · P2 (8 code paths) · P3 (7 unknown → code) · P4 (mixed → code) · P5 (5 untrustworthy ranges → code) | -| `test_repo_gates_scope.py` | R1 · R2 · **R3 (Scenario C)** · R4 · R5 · R6 | -| `test_golden_notice.py` | N1 · N2 (+ the only-one-non-blocking control) · N3 · N4 | - -**Red-proofs, all three run, all reverted, all confirmed by the suite passing afterwards:** - -- **P3** — allow-list swapped for a deny-list (`return not p.startswith(("hub/","website/", - "manifests/","scripts/"))`). P3 **failed**, naming all seven unknown paths as documents: - `terraform/main.tf`, `cmd/newthing/main.go`, `Makefile`, `docs/readme.md`, `documentation-old/x.md`, - `src/app.py`, `.github/workflows/ci.yml`. -- **R3 — the one that matters.** The `site` row's fifth field flipped to `True`. R3 **failed**: - *"a documents-only push with the SITE gate convicting was ALLOWED. The exemption has become - general."* -- **N1** — the notice's debt branch changed to `return 1`. N1 **failed** with `Got exit 1`. - -**Scenario C was written first and failed for the right reason** before any implementation existed: -`--scope` was an unknown argument, and unpacking the GATES table raised -`ValueError: too many values to unpack (expected 4)`. - -## 5. Live validations 2, 3 and 4, verbatim - -Run against the **real** `.githooks/pre-push` on a throwaway local bare remote, so no test commit -reached Gitea. The hook does not know or care what the remote is. - -**Validation 3 — code push, golden owed → REFUSED (exit 1):** +## 8. All four runners, final ``` -push_scope: CODE (6 file(s): 0 document, 6 code) - CODE because these are not on the document allow-list: - scripts/push_scope.py - scripts/test_push_scope.py -pre-push [felhom.eu]: running scripts/repo_gates.py --fast --scope=code ... -GOLDEN CURRENCY GATE FAILED: controller v0.232.0 is released and NO golden carries it (newest bake is 0.230.0). - golden-currency FAILED (exit 1) -CONVICTED: golden-currency -pre-push [felhom.eu]: PUSH REFUSED - gates exited 1. +all felhom.eu gates OK (14 gates, incl. the new decoy-coverage) +all controller gates OK (14 gates) +all agent gates OK (5 gates) +all catalog gates OK (3 gates) ``` -**Validation 2 — documents-only push, same debt → ADVISORY, ACCEPTED (exit 0):** +## 9. No version, no image, no golden -``` -push_scope: DOCS (1 file(s): 1 document, 0 code) -pre-push [felhom.eu]: running scripts/repo_gates.py --fast --scope=docs ... -repo_gates (felhom.eu) — 13 gate(s) [--fast] [scope=docs] - golden-currency ADVISORY (exit 1) +**No product code was touched. No version was bumped. No image was built. No golden is owed.** +`golden_currency_gate.py` exits 0; golden and fleet floor remain **0.232.0** and current. -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! ADVISORY — golden-currency convicted, and this push is NOT refused for it. -!! newest released controller : 0.232.0 -!! newest golden baked : 0.230.0 -!! -!! This push touches DOCUMENTS ONLY, so it can neither create this debt nor clear -!! it — and the push that DOES clear it (a bake record under documentation/tests/) -!! is itself documents-only. Blocking here blocked the cure. -!! -!! WHAT CLEARS IT: bake a golden per documentation/runbooks/RUNBOOK-manual-build.md -!! section 4.1, then vouch it (a THREE-field change: golden_version + agent_version -!! + min_agent). The debt stays visible in STATUS.md and in the controller repo's -!! own golden-notice until then. -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +## 10. Register -all felhom.eu gates OK (with 1 advisory — see above) -pre-push [felhom.eu]: gates OK - push proceeding. - 1c00af6..1f74427 main -> main -``` +**Before:** OPEN 172 · CLOSED 160. **After:** OPEN 178 · CLOSED 161. +Closed **R-419**. Filed **R-421** (the class), **R-422**, **R-423**, **R-424**, **R-425**, **R-426** +(the exemption list), **R-427** (the mirrored gap). Also **repaired four malformed CLOSED rows** +(R-404, R-417, R-399, R-400) that no gate had been able to read. -**Validation 4 — SCENARIO C, the acceptance step. Documents-only, golden owed, a second gate -convicting → REFUSED for that gate alone:** +## 11. Observations, and my own mistakes by name -``` -push_scope: DOCS (1 file(s): 1 document, 0 code) -pre-push [felhom.eu]: running scripts/repo_gates.py --fast --scope=docs ... - golden-currency ADVISORY (exit 1) - observations FAILED (exit 1) -!! ADVISORY — golden-currency convicted, and this push is NOT refused for it. -CONVICTED: observations -pre-push [felhom.eu]: PUSH REFUSED - gates exited 1. -``` +1. **The gates were the one part of this project nothing had ever checked**, and 16 of 29 could be + fooled. **FILED: R-421** — the class row, with the four shapes. +2. **Scope is a fact.** Eight holes were one call: `os.listdir` where `os.walk` was meant. Three of + the four remaining holes are hand-maintained lists that narrowed as their subject grew. + **FILED: R-421.** +3. **`closed_register_gate.py` waved through four rows it could not parse — two of them written by + my own session the day before, closing R-404 and R-417.** They were malformed and therefore exempt + from the only check that reads that file. **FILED: R-421** (fixed: unreadable now convicts). +4. **Twelve open rows carry a closed-looking verdict, and I did not move them.** **FILED: R-427.** +5. **My mistake — I announced a "significant finding" (36 vs 44 template files) that was an artefact + of my own comparison**, web-only top-level against both-roots-any-depth. Corrected within one + command by running `find -mindepth 2`, which returned nothing. **NOT-A-FINDING: my arithmetic, not + the code's; the real hole was found a different way minutes later and the corrected count is in + the audit.** +6. **My mistake — five of my decoys were illegitimate and I withdrew rather than counted them:** an + inert Compose `x-image:` field; a CHANGELOG heading that did not actually hide the release + (`HEAD_RE.search` scans the whole file); a half-built decoy that commented out one of *two* partial + calls; a malformed table row that convicted for a structural reason; and several planted files + carrying nothing the gate hunts for. **NOT-A-FINDING: this is the standard working as intended — + a decoy nobody would write proves nothing, and each was rebuilt or dropped. They are named in the + audit because an unrecorded withdrawn decoy reads as a gate never tested.** +7. **My mistake — I trusted `rc == 0` as "hole" for a gate where it is not the question.** + `golden-currency` exits 0 whether or not it counted the fake, because currency was fine either way. + I re-ran it reading the OUTPUT and it was sound. **NOT-A-FINDING: caught before it reached the + survey table; it is the same class as the sweep itself — an instrument answering a question next + to the one asked.** +8. **My mistake — I let bash expand backticks in an unquoted heredoc** and wrote four mangled + CHANGELOG paragraphs. Caught by reading the file back, repaired in place. **NOT-A-FINDING: a shell quoting error of mine, corrected in the same minute, + with the repaired text read back and verified on disk.** +9. **`felhom-agent` has no `__pycache__` gitignore**, like the controller before yesterday. Left + alone. **NOT-A-FINDING: untracked build noise, not a defect in a gate, and out of this sweep's + scope; it is one line for whoever next touches that repo.** -**Validation 4 took two attempts and the first one was wrong.** Recorded rather than tidied away: - -1. My first planted observation contained the sentence *"it carries no `FILED:` and no - `NOT-A-FINDING:` marker"*, and the gate read the literal string and passed it — so the push - succeeded and proved nothing. **That is a real defect in `observations_gate.py`, now R-419.** -2. Having pushed that commit, I amended it, which made the next range a force-push. The classifier - correctly answered `code`, so the refusal I then saw was trivial and not Scenario C at all. I - rewound the probe ref and re-ran it as a genuine fast-forward `docs` range — the output above. - -## 6. Evidence restored, tree unchanged - -``` -golden currency gate OK — the newest released controller has a golden -golden gate exit=0 - evidence files: 14 diff vs HEAD: 0 -git status --porcelain → (empty) -``` - -**A near-miss worth naming:** `git reset --hard` had already restored the tracked evidence directory -before I moved my aside copy back, so the `mv` nested a duplicate *inside* it. Caught by -`git status` showing an untracked `golden-0.232.0-2026-09-01/golden-aside/`. I diffed the two -(`diff -r --exclude=golden-aside . golden-aside` → identical) **before** deleting anything, then -removed the duplicate. 14 files, byte-identical to HEAD. - -## 7. CI: changed, not left blocking — and why that is safe before it has run - -**Changed.** Leaving it blocking would have left R-417's actual symptom in place: red CI runs on a -drill night, indistinguishable from real ones. That is half the harm. - -CI checks out `--depth 1` of a single SHA, so it has **no range**. The file list therefore comes from -the push event payload and feeds the **same classifier** via `--files-from`, so there is one -definition of "document" and not two. - -**Every failure path writes `code`:** no `GITHUB_EVENT_PATH`, unreadable JSON, no `commits` array, an -empty array, an absent classifier. So this step can only make CI as strict as it is today, never -looser — **the untested direction is the safe one**, which is why shipping it before observing it is -defensible. - -**MEASURED after the push, so this is no longer an assumption.** CI job **481** (`1e6c387a`, -felhom.eu) ran the new step and its log reads: - -``` -3 commit(s), 12 distinct path(s) in the payload ---- paths the push event reported --- -scripts/push_scope.py -scripts/test_push_scope.py -push_scope: CODE (12 file(s): 5 document, 7 code) -scope: code -::group::Run python3 scripts/repo_gates.py --fast --scope="${PUSH_SCOPE:-code}" -``` - -So Gitea's act-runner **does** populate `GITHUB_EVENT_PATH` with a `commits` array carrying per-file -lists; the classifier ran on it and returned `code` for a push that genuinely touched `scripts/`. -Job 481 is green. - -**STILL NOT OBSERVED: the `docs` branch in CI, and an advisory in a CI log.** The code path is proven; -a documents-only CI run has not happened yet, and an ADVISORY there additionally needs a golden debt -to exist at that moment. Neither is arranged artificially — the next documents-only push shows the -first, and the next release-without-a-bake shows the second. - -The compensating controls that make a green documents-only CI run honest are named in the workflow -itself: the advisory block in the run's own log, `STATUS.md`, and the controller-side notice. - -## 8. `controller_gates.py` could NOT express a non-blocking gate - -**It could not, and the capability was added rather than the notice compromised.** Every registered -gate's non-zero exit fed `worst` and failed the run; there was no way to describe a check that -reports without refusing. A fifth `blocking` field now exists, `False` for exactly one gate, and -`test_golden_notice.py` asserts it stays exactly one. Filed as **R-420**, because the absence was -invisible — nobody had wanted such a gate before, so nothing recorded that it was impossible. - -`felhom.eu/scripts/repo_gates.py` still has **no** `blocking` field. It has `exemptible`, which is a -different idea: scope-dependent, not permanent. If a permanently-advisory gate is ever wanted there, -it needs the same addition. - -## 9. Explicitly still open - -- **R-242's vouch half.** Nothing gates the vouch; a baked-but-unvouched golden passes both the gate - and the new notice. Unchanged by this task and **not** closed by association. The reason is forced: - the vouched version lives only in the hub's `hub_settings` table, and a hub-reading gate could not - be `--fast`, so it would run in neither the hook nor CI. -- **R-95** · **R-402** · **R-409** · **R-401** · **R-412 leg 2** — all untouched by this task. -- **R-418** (docstring/table correspondence unenforced), **R-419** (`observations_gate` substring), - **R-420** (no `blocking` field in the felhom.eu runner) — filed today, open. - -## 10. No version, no image, no golden - -**No version was bumped. No image was built. No golden is owed by this work.** `golden_currency_gate.py` -exits 0 and all thirteen felhom.eu gates are green. The controller CHANGELOG entry deliberately -carries **no version heading**: a scripts change is not a release, and giving it one would have -created the debt this task exists to make manageable. - -## 11. Register - -**Before:** OPEN 171 · CLOSED 158. **After:** OPEN 172 · CLOSED 160. - -- **CLOSED R-404** — with the ruling and the reasoning for rejecting both framed options. -- **CLOSED R-417** — cause removed, not worked around. -- **R-242** — amended in place to state that its vouch half is untouched and still open. -- **FILED R-418, R-419, R-420.** - -Both closed rows were written compressed at closure, which is this project's convention; no separate -compression sweep was needed for two rows. - -## 12. Observations, and my own mistakes by name - -1. **The `golden-currency` gate was never the problem, and both offered options would have made - things worse.** Narrowing it silences a true signal on exactly the nights it matters; a waiver - would have recorded a lie, because the drill night wanted the golden and was forbidden from baking - it. **FILED: R-404** — the ruling and this reasoning are in the closed row. -2. **My mistake — an empty grep read as a measurement.** My first stdin probe printed nothing and I - was one step from reporting "the hook receives no stdin". The cause was mine: I pushed `main` in a - repo whose branch was `master`, so the hook never ran. **NOT-A-FINDING: my own error, caught within - one command by looking at the raw output instead of the filter, and it changed no conclusion. It - is recorded because the failure mode — a filter that can return empty for a reason unrelated to - the question — is the one this project keeps paying for.** -3. **My mistake — I planted a test observation whose own text satisfied the gate**, so Validation 4 - passed when it should have failed and I briefly had a green that meant nothing. Chasing it found a - genuine substring weakness. **FILED: R-419.** -4. **My mistake — I amended a commit that had already been pushed to the probe remote**, turning the - next range into a force-push, so my second Validation 4 attempt ran at `scope=code` and its - refusal was trivial. I noticed because `golden-currency` read `FAILED` where it should have read - `ADVISORY`. Rewound and re-ran properly. **NOT-A-FINDING: the classifier behaved exactly as - designed — a force-push is untrustworthy and must fail closed. The error was mine, in the test - setup, and the correct behaviour is what exposed it.** -5. **My mistake — `lstrip("./")` ate the leading dot of `.claude/`**, silently classifying the whole - rule-file tree as code. `lstrip` takes a set of characters, not a prefix. Caught by P1 on its first - run. **NOT-A-FINDING: a bug I wrote and my own test caught before it left the working tree; it is - listed so the next reader sees why the code now loops on `"./"` instead.** -6. **`repo_gates.py`'s docstring listed eleven gates while thirteen ran** — for eight days, in the - sibling repo whose rule file already warns about exactly this drift. **FILED: R-418.** -7. **`controller_gates.py` had no way to express a reporting-only gate**, and nothing recorded that. - **FILED: R-420.** +10. **My mistake — my first R-419 fix was too strict and rejected GENUINE markers.** It anchored a + marker to a line start or a bare `. `, which misses the commonest real shape: a bolded sentence + followed by a bolded marker (`...them.** **FILED: R-427**`). **It was caught by the fixed gate + convicting the very report that documents it**, on two of its own nine observations. Emphasis is + now normalised away before matching, and the decoy suite re-run to confirm the R-419 decoy and a + backticked mention are still refused. **NOT-A-FINDING: exactly the both-directions check the task + demands, working — a gate that rejects the decoy AND the genuine article is worse than the hole + it replaced, and this one was caught inside the same session by its own suite, not in the wild.**