decoy suite: fake register ids R-903..907 -> R-99903..99907 (the real register reached R-903; the hook refused the push)
gates / gates (push) Successful in 3m55s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
This commit is contained in:
2026-10-08 09:25:26 +02:00
parent 42a4a4634b
commit aa843a2fa0
2 changed files with 19 additions and 12 deletions
+7
View File
@@ -57,6 +57,13 @@ The decoy suite (`scripts/test_gate_decoys.py`) runs all five with `must=` on th
- Earlier, `67b3fd23` (website refresh): CI job 1535 failed with no log and every step failing in the same second
(R-887's shape); `663421dd` on top passed (job 1536).
## Fixed without a row
The decoy suite's fake register ids were R-903…R-907 — the real register reached R-903 today, so the
`closed-register/body-word` decoy saw R-903 in BOTH registers and the pre-push hook refused 42a4a463. The fakes are now
R-99903…R-99907 (`scripts/test_gate_decoys.py`, 12 lines); suite 73/73 behaved, all gates OK. (My command chain ran
the commit after the red gate; the hook held the push. Each later step ran gates first, read rc, then committed.)
## Instruction-file edits
`.claude/rules/website.md`: one bullet added — the switch is a globe; a new language = one `LANGS` entry + its twins.
+12 -12
View File
@@ -267,12 +267,12 @@ check_golden_names_the_fake()
# genuinely closed. This asserts the deliberate behaviour so a future "fix" has to argue with it.
decoy("closed-register/body-word (BY DESIGN)", "closed_register_gate.py",
append_to(os.path.join(ROOT, "documentation", "backlog", "CLOSED-ITEMS.md"),
u"\n| **R-903** | Work continues and it is still READY in the body. | CLOSED 2026-09-01 | none |\n"),
u"\n| **R-99903** | Work continues and it is still READY in the body. | CLOSED 2026-09-01 | none |\n"),
expect="accept")
# A 4-column row (| ID | Title | Shipped | Evidence |) whose VERDICT cell reads open.
decoy("closed-register/verdict-word", "closed_register_gate.py",
append_to(os.path.join(ROOT, "documentation", "backlog", "CLOSED-ITEMS.md"),
u"\n| **R-904** | A finished thing. | READY - still being worked on | none |\n"))
u"\n| **R-99904** | A finished thing. | READY - still being worked on | none |\n"))
# R-421: a row this gate cannot PARSE used to be a warning, and the gate then printed OK. Four rows
# were in that state — two of them written by the session that closed them the day before this
@@ -280,7 +280,7 @@ decoy("closed-register/verdict-word", "closed_register_gate.py",
# conviction. This is the sweep's own shape one level up and it is why the decoy is kept.
decoy("closed-register/unreadable-row", "closed_register_gate.py",
append_to(os.path.join(ROOT, "documentation", "backlog", "CLOSED-ITEMS.md"),
u"\n| **R-905** | A row with no state cell at all. |\n"))
u"\n| **R-99905** | A row with no state cell at all. |\n"))
# --- closed-register RULE 4 (2026-10-05, R-416): the same id twice in CLOSED-ITEMS.md ---------------
# The id is read from the file's FIRST closed row at run time, so the decoy duplicates a real id rather than an
@@ -317,9 +317,9 @@ def _open_row(rid, state):
decoy("closed-register/finished-row-in-open", "closed_register_gate.py",
append_to(_OPEN_REG, _open_row("R-906", "**CLOSED 2026-10-03 — shipped in v9.9.9**")))
append_to(_OPEN_REG, _open_row("R-99906", "**CLOSED 2026-10-03 — shipped in v9.9.9**")))
decoy("closed-register/open-row-closed-word (BY DESIGN)", "closed_register_gate.py",
append_to(_OPEN_REG, _open_row("R-907", "**READY — RE-RANKED UP 2026-08-03 (R-86 closed)**")),
append_to(_OPEN_REG, _open_row("R-99907", "**READY — RE-RANKED UP 2026-08-03 (R-86 closed)**")),
expect="accept")
# --- one-register (2026-10-03): an open ROADMAP row whose id carries a LETTER SUFFIX ------------
@@ -327,7 +327,7 @@ decoy("closed-register/open-row-closed-word (BY DESIGN)", "closed_register_gate.
# R-50b at once — a finding that had lived only in ROADMAP.md. This decoy is that shape.
_ROADMAP = os.path.join(ROOT, "documentation", "backlog", "ROADMAP.md")
decoy("one-register/suffix-id-row", "one_register_gate.py",
append_to(_ROADMAP, u"\n| R-905b | **A finding filed only here.** | S | READY — 2026-10-03 | none |\n"))
append_to(_ROADMAP, u"\n| R-99905b | **A finding filed only here.** | S | READY — 2026-10-03 | none |\n"))
# R-426 (2026-10-06): the rest of the gate's decoys, which took it off the EXEMPT list.
# * done-word-about-another-row PASSED before 2026-10-06 (a live hole): DONE was searched over the whole
@@ -338,16 +338,16 @@ decoy("one-register/suffix-id-row", "one_register_gate.py",
# passes BY DESIGN — that is R-424's hole, which the operator accepted on 2026-10-05 (no mechanical
# fix; a person reads the roadmap). Asserted so a future change to it is a decision, not a drift.
decoy("one-register/done-word-about-another-row", "one_register_gate.py",
append_to(_ROADMAP, u"\n| R-905c | **A finding filed only here.** | S | READY — split out after R-86 CLOSED | none |\n"))
append_to(_ROADMAP, u"\n| R-99905c | **A finding filed only here.** | S | READY — split out after R-86 CLOSED | none |\n"))
decoy("one-register/id-only-in-register-prose", "one_register_gate.py",
_two_appends(_ROADMAP, u"\n| R-905d | **A finding filed only here.** | S | READY — 2026-10-06 | none |\n",
_OPEN_REG, u"\nNote: R-905d is tracked on the roadmap.\n"))
_two_appends(_ROADMAP, u"\n| R-99905d | **A finding filed only here.** | S | READY — 2026-10-06 | none |\n",
_OPEN_REG, u"\nNote: R-99905d is tracked on the roadmap.\n"))
decoy("one-register/genuine-counterpart", "one_register_gate.py",
lambda: append_to(_ROADMAP, u"\n| %s | **The roadmap half of a registered finding.** | S | READY — 2026-10-06 | none |\n"
% _open_reg_first_id())(),
expect="accept")
decoy("one-register/idea-row (BY DESIGN, R-424 accepted)", "one_register_gate.py",
append_to(_ROADMAP, u"\n| R-905e | **A proposal.** | S | idea — 2026-10-06 | none |\n"),
append_to(_ROADMAP, u"\n| R-99905e | **A proposal.** | S | idea — 2026-10-06 | none |\n"),
expect="accept")
# --- hub-copy: an ENGLISH retrieval promise, planted in the bundle (R-558) -----------------------
@@ -723,7 +723,7 @@ _rsg_case("blank-splits-table",
# decoy 4 (2026-09-30) — a row whose id carries a LETTER SUFFIX (R-88a, R-88b, R-209a are real) with its state cell
# eaten: the old `R-\d+` pattern did not read it as a row at all, so it passed unchecked
_rsg_case("suffix-row-eaten-state",
_HEALTHY.rstrip() + "\n| **R-903a** | **A split finding.** |\n".replace(" |\n", "\n"),
_HEALTHY.rstrip() + "\n| **R-99903a** | **A split finding.** |\n".replace(" |\n", "\n"),
must_convict=True, expect_rule="RULE 1")
# 2026-10-03 — RULES 5–8: the columns, the category, the severity and the state word. Each decoy is a
@@ -731,7 +731,7 @@ _rsg_case("suffix-row-eaten-state",
_R901 = "| **R-901** | Backup & restore | P3 |"
# decoy 5 — a row filed with no category (the old 3-column habit, under the new header)
_rsg_case("old-shape-row-under-new-header",
_HEALTHY.rstrip() + "\n| **R-903** | **A finding filed the old way.** | **READY** |\n",
_HEALTHY.rstrip() + "\n| **R-99903** | **A finding filed the old way.** | **READY** |\n",
must_convict=True, expect_rule="RULE 5")
# decoy 6 — a near-miss category (the brief's own short name, not the defined one)
_rsg_case("unknown-category",