Files
felhom.eu/documentation/audits/AUDIT-gate-decoys-2026-09-01.md
T
admin 574f5df107
gates / gates (push) Failing after 17s
the decoy sweep: 29 gates read, 16 fooled, 10 fixed - and a gate that refuses the next one (R-421)
THE CLASS, now a row: an instrument that matches a LABEL rather than the fact it names. Five
instances - R-410, R-400, R-378, R-419, R-94 - and EVERY ONE was found by accident, by someone
looking at something else. The gates enforce every other rule in this project, including the rule
that findings must be written down rather than left in prose. Nothing had ever checked the gates.

METHOD, and it is the transferable part: for each gate, construct the label WITHOUT the fact - a
directory with the right name and no bake log, a handler case that exists only in a comment, a note
whose prose mentions the marker it lacks - run the gate, record what it says. No verdict was reached
by reading. Reading is how all five hid.

RESULT: 29 distinct scripts (35 registrations; three are shared across three runners). 19 sound, 4
holes left OPEN with rows, 6 that no plausible decoy could be built for and are named UNTESTED rather
than called sound. A gate nobody tried to fool is UNKNOWN.

SCOPE IS A FACT TOO - the largest single cause, and mundane. Eight gates decided what to look at with
os.listdir, one level. Every one was green AND CORRECT today, and every one would have gone blind the
moment anyone added a subdirectory. mojibake and docker-v already used os.walk, caught the identical
planted file, and are the control that proves the cause was the listing and not the decoy.

IN THIS REPO: hub-confirm and manifest-bearer now walk. observations_gate (R-419, CLOSED) requires a
marker at a line start or after a sentence boundary and strips inline code spans - a note SAYING it
carries no marker no longer satisfies the marker test. closed-register now CONVICTS on a row it
cannot parse instead of warning: FOUR rows were in that state, TWO of them written by the session
that closed them the day before, and every one was exempt from the only check that reads that file.
The rows were repaired first and the conviction added second - registering a failing gate refuses
every push.

THE META-GATE: decoy_coverage_gate.py refuses a gate registered without a decoy or a named exemption.
It convicted ITSELF the moment it was registered, which is how it came to have one. Coverage is a
DECLARATION the gate AST-parses, never a grep - searching a test file for a gate's name would be the
very shape this sweep exists to find. The 20 uncovered gates are listed by name (R-426).

NOT FIXED, each with a row and a decoy asserting TODAY's behaviour so the fix must be deliberate:
R-422 reuse-refs (only 7 extensions; a rotted .md citation is invisible), R-423 site (PAGES is a
hardcoded list of 7), R-424 one-register (a defect parked as `idea`), R-425 offbox-rename (fixed
FILES list). R-427: closed_register_gate checks ONE direction - twelve open rows carry a closed
verdict and were NOT moved, because telling finished from partly-finished is a judgement and R-378
is the record of a machine getting it wrong.

FIVE DECOYS WITHDRAWN AS ILLEGITIMATE, mine, named in the audit. A decoy nobody would write proves
nothing, and manufacturing a finding to fill a row is worse than an honest NO.

No product code. No version bump. No image. No golden owed. All four runners green.
Register: OPEN 172 -> 178, CLOSED 160 -> 161.
2026-09-01 12:39:45 +02:00

5.7 KiB
Raw Blame History

AUDIT — can this check be fooled by a label? The decoy sweep (2026-09-01, R-421)

Question asked of every gate: could I satisfy this with a convincing label instead of the real thing? Answered by construction — a decoy is written, the gate is run, and the verdict recorded. No row's last column was answered from reading.

The survey

29 distinct scripts, 35 registrations (reuse-refs, instructions, observations are one script each, registered in three runners). This matches the task's count of 29.

gate runner(s) meant to prove actually matches shape decoy passed before?
emoji ctrl no emoji in UI copy codepoints, in listdir scope 1 YES
native-confirm ctrl no OS-modal dialogs JS call regex, listdir scope 1 YES
app-row-dedup ctrl one row markup regex + '…' not in src, listdir 1, 2 YES (×2)
template-id ctrl JS ids resolve id sets, listdir scope 1 YES
secret-markup ctrl no secret in markup template actions, listdir 1 YES
retrieval-promise ctrl promises are registered stems, listdir scope 1 YES
hub-confirm eu no OS-modal dialogs JS call regex, listdir scope 1 YES
manifest-bearer eu no bearer literals 64-hex regex, listdir scope 1 YES
observations eu, ctrl, agent a finding is filed FILED: anywhere in the body 2 YES (R-419)
debug-routes ctrl controls resolve raw text, comments included 2, 3 YES
closed-register eu closed rows are closed verdict cell — but skipped unparseable rows 2 YES
site eu pages are well-formed a 7-entry PAGES list 1 YES → R-423
one-register eu open work is registered the state cell; idea escapes 2 YES → R-424
offbox-rename ctrl branding is retired a fixed 3-entry FILES list 1 YES → R-425
reuse-refs eu, ctrl, agent citations resolve paths — only 7 extensions 1 YES → R-422
mojibake ctrl no double-encoded UTF-8 bytes, via os.walk 5 NO — control
docker-v ctrl -v mounts are safe argv, via 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 bake log 5 NO (R-410 fixed)
golden-notice ctrl ditto, other direction imports the gate above 5 NO
instructions eu, ctrl, agent instruction files stay sane effective text 5 NO
hub-copy eu retired names are gone os.walk over hub/internal 5 NO
release-complete agent a release is complete git 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 are published — ? UNKNOWN
image-resolvable catalog images exist docker manifest inspect 5 UNKNOWN
volume-persistence catalog data survives runs containers, diffs 5 UNKNOWN

19 sound · 4 holes left open with rows · 6 unknown. Ten holes were fixed in this session.

The one cause behind eight of them

Eight gates decided their scope with os.listdir, one directory level. No template or manifest subdirectory exists today, so every one was green and correct — and would have stayed green the moment anyone added templates/partials/, which is an ordinary act. mojibake and docker-v already used os.walk, caught the identical planted file, and are the control that proves the cause was the listing and not the decoy.

Holes left open, with rows

row gate why not fixed here
R-422 reuse-refs PATH_RE matches 7 extensions; a rotted .md citation is invisible. Widening it needs a false-positive pass over 4 repos.
R-423 site PAGES is a hardcoded list of 7. Fix is to glob website/*.html, which needs the per-page exemptions rethought.
R-424 one-register a defect parked under state idea escapes. Declared in the gate's own docstring as residual hole 1.
R-425 offbox-rename fixed 3-entry FILES list; a new offbox file is unscanned.
R-426 — owns the meta-gate's 20-name exemption list.

Each is asserted as it behaves today where a test could hold it, so the day it is fixed the assertion fails and is updated deliberately. A hole nothing asserts is a hole nobody remembers.

Decoys withdrawn as illegitimate — mine, and named

§2.1 sets the standard: a decoy nobody would write proves nothing. These were withdrawn rather than counted, because counting them would have manufactured findings.

  1. image-pins / x-image: nginx:latest — x- fields are inert in Compose. The label had no fact behind it either way. Replaced with a positive control (a real untagged image: line → convicted).
  2. release-complete / a non-version heading on top of CHANGELOG.md — HEAD_RE.search scans the whole file, so the real release is still found and named v0.130.0. The gate is sound.
  3. app-row-dedup / one commented-out partial call — dashboard.html has two; replacing one left the other live. The gate was right and my decoy was half-built.
  4. one-register / a 3-column decoy row — it convicted for a structural reason, not the one under test. Rebuilt at the real 5-column shape, and the hole then reproduced.
  5. template-id, secret-markup, retrieval-promise, hub-copy / wrong content — my first planted files carried nothing those gates hunt for, so "passed" meant nothing. Rebuilt with real triggers.