REPORT: the decoy sweep - 29 gates, 16 fooled, 10 fixed, 6 honestly untested
gates / gates (push) Failing after 14s
gates / gates (push) Failing after 14s
Opens with the survey table. Records the three numbers, every live hole with its decoy and row, the six gates no plausible decoy could be built for, the meta-gate's 20-name exemption list, and which of the five defining rows actually closed (R-419; R-378 explicitly did NOT). Includes my own mistakes by name - five decoys withdrawn as illegitimate, a 36-vs-44 arithmetic artefact I announced before checking, an rc==0 read as a hole for a gate where rc is not the question, a bash heredoc that ate my backticks, and an R-419 fix that was too strict and rejected genuine markers until its own gate convicted this very report.
This commit is contained in:
@@ -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: `<local ref> <local sha> <remote ref>
|
||||
<remote sha>`, 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) | `<!-- {{template "app_list_row"}} -->` | 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.**
|
||||
|
||||
Reference in New Issue
Block a user