960d29b061
gates / gates (push) Successful in 12s
Adds section 11. The sharpest result of the whole sweep is in it: my own decoy-coverage gate identified a repository by its DIRECTORY NAME and went blind the first time CI ran it, because the act-runner checks out into a folder called hostexecutor. The gate written that morning to catch name-for-fact was matching a name, in the first ten lines of its own main loop (R-428). The other cause was my push ordering - two repos citing R-421 pushed before felhom.eu carried the row - which instructions_gate convicted exactly as designed.
217 lines
15 KiB
Markdown
217 lines
15 KiB
Markdown
# REPORT — the decoy sweep: can a gate be fooled by a label? (2026-09-01, R-421)
|
||
|
||
## The survey — every gate, its shape, and whether a decoy passed BEFORE this session
|
||
|
||
| 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** |
|
||
|
||
## 1. Gate count
|
||
|
||
**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 |
|
||
|---|---|
|
||
| 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 |
|
||
|
||
**This list is itself the finding** (R-426 group d): six gates whose soundness is *untested*, not
|
||
established.
|
||
|
||
## 5. Files and commits
|
||
|
||
| 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 |
|
||
|
||
## 6. The meta-gate's exemption list — 20 names, owned by R-426
|
||
|
||
**(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`.
|
||
|
||
**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.
|
||
|
||
## 7. Which of the five defining rows closed
|
||
|
||
| row | outcome |
|
||
|---|---|
|
||
| **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 |
|
||
|
||
## 8. All four runners, final
|
||
|
||
```
|
||
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)
|
||
```
|
||
|
||
## 9. No version, no image, no golden
|
||
|
||
**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.
|
||
|
||
## 10. Register
|
||
|
||
**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.
|
||
|
||
## 11. CI — four red runs after the first push, all mine, all now green
|
||
|
||
**The first push of every repo except the catalog went RED**, while all four runners were green
|
||
locally. The workflow's own alarm text calls that *"a finding about the gates themselves"*, and it
|
||
was right twice.
|
||
|
||
| repo | final job | sha | result |
|
||
|---|---|---|---|
|
||
| felhom.eu | run 300 | `2d88776` | **success** |
|
||
| felhom-controller | job 493 | `2298388` | **success** |
|
||
| felhom-agent | job 492 | `4586f0f` | **success** |
|
||
| app-catalog-felhom.eu | job 486 | `29edad9` | **success** (green first time) |
|
||
|
||
**Cause 1 — R-428, and it is the sweep's sharpest result.** `decoy_coverage_gate.py` identified a
|
||
repository by `os.path.basename(root)`. Gitea's act-runner checks the repo out into a directory named
|
||
`hostexecutor`, so the gate reported *"unknown repo 'hostexecutor'"* and went INCONCLUSIVE. **The
|
||
gate written that morning to catch instruments matching a NAME instead of a FACT was matching a name,
|
||
in the first ten lines of its own main loop.** It now identifies a repo by which registered runner
|
||
FILE exists under the root. Verified under a renamed directory: 14 gates found where the name-based
|
||
version found none.
|
||
|
||
**Cause 2 — my push ordering.** `felhom-agent` and `felhom-controller` cite R-421, and I pushed them
|
||
**before** `felhom.eu` carried that row, so `instructions_gate` correctly convicted *"cites R-421,
|
||
which appears in neither register"*. The register lives in `felhom.eu`; **a repo citing a new row must
|
||
be pushed after it.** Not a gate defect — the gate did exactly its job, on me.
|
||
|
||
**Cause 3 — a missing input.** CI fetches two sibling clones; the meta-gate walks four runners. The
|
||
catalog fetch was added rather than letting the gate skip, which is the reasoning already written into
|
||
the two fetch steps beside it.
|
||
|
||
## 12. Observations, and my own mistakes by name
|
||
|
||
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. **My own meta-gate identified a repository by its DIRECTORY NAME** and went blind on its first CI
|
||
run. **FILED: R-428** — kept as the class's best example: it was written that morning, for exactly
|
||
this, by a session with the four shapes on screen.
|
||
10. **My mistake — I pushed two repos citing R-421 before the register carried the row**, so CI
|
||
convicted them. The register lives in `felhom.eu`. **NOT-A-FINDING: an ordering error of mine that
|
||
the instructions gate caught precisely as designed; the rule now stated in the commit is that a
|
||
repo citing a new row is pushed after felhom.eu.**
|
||
11. **`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.**
|
||
|
||
12. **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.**
|