9167cf53af
gates / gates (push) Successful in 23s
The hub has written every customer e-mail in Hungarian whatever the box was set to. The box has published its language since controller v0.247.0; nothing read it. Now it does. Nothing an operator reads changes. The Hungarian mails are byte-identical, and that is a diff rather than a reading: 56 goldens per language captured from v0.117.0 BEFORE any string moved, and all 56 Hungarian ones pass unchanged after every sentence was routed through the new bundle. - internal/i18n: flat bundle, 79 keys, hu authoritative + hu fallback, ceiling 0. - customerMessages/severityLabels are DERIVED from the bundle, so a sentence is written in one place and all 40+ tests that read those maps still work. - Language order: last reported -> created-with -> hu. reports.language defaults to EMPTY, never hu: "never told us" is not "chose Hungarian". - message_customer on POST /api/v1/event, additive and optional forever, for the sentences the box composes and the hub cannot translate. - The bind page is per-language, and its `expired` state stays Hungarian: it is the state an unknown token lands in, so rendering a real English customer's token in English would make the LANGUAGE answer what the TEXT refuses to. Two defects found inside the release: - R-581: the newest report was picked by received_at, which has SECOND granularity, so same-second reports tied and the winner was arbitrary. Ordered by the autoincrement id now. GetCustomers() still has the shape - row open. - R-582: the English copy-guard stems, ported word for word from Hungarian, convicted 141 honest sentences. The English claim is a phrase with a modal. R-555 closed: the language allowlist entry is out of wire_contract_gate.py. hub_copy_gate.py follows the sentences into the bundle - without that it would have scanned four files that no longer hold any customer text and reported success. Three new decoys incl. an innocent control. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
296 lines
15 KiB
Python
296 lines
15 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""test_gate_decoys.py — can this gate be fooled by a LABEL? (R-421)
|
|
|
|
WHY THIS FILE EXISTS. Four times in one week a gate turned out to be matching a name instead of the
|
|
thing it named — R-410 (a `mkdir` turned the release gate green), R-400 (seven debug controls that
|
|
answered nothing), R-378 (a status word inside a sentence), R-419 (a phrase inside prose, including
|
|
prose saying the marker was absent). **All four were found by accident.** The gates are the machinery
|
|
that enforces everything else in this project, and they were the one part nothing checked.
|
|
|
|
A DECOY IS THE LABEL WITHOUT THE FACT. Each test below constructs one, runs the real gate, and
|
|
asserts it CONVICTS. Where a decoy would pass, that is a live hole.
|
|
|
|
⚠ A DECOY MUST BE THE SHAPE A REAL SESSION WOULD PRODUCE. R-419 was not found by an absurd input —
|
|
it was found by a genuine note explaining that it carried no marker. That is the standard. A decoy
|
|
nobody would ever write proves nothing, and saying so is a result.
|
|
|
|
⚠ EVERY TEST ASSERTS BOTH DIRECTIONS where it can. A gate that rejects the decoy AND rejects the
|
|
genuine article is worse than the hole it replaced.
|
|
|
|
Fixtures are planted in the real tree and removed in a `finally`. The suite asserts the tree is
|
|
unchanged at the end.
|
|
|
|
Run from the repo root: python3 scripts/test_gate_decoys.py
|
|
Exit 0 all decoys rejected · 1 a decoy passed (a live hole).
|
|
"""
|
|
import io
|
|
import json
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
# ── WHAT THIS FILE COVERS ────────────────────────────────────────────────────────────────────────
|
|
# Read by scripts/decoy_coverage_gate.py, which AST-parses this literal rather than grepping for
|
|
# gate names — a substring search for coverage would be the very shape this sweep exists to find.
|
|
# A gate named here MUST have a decoy below that has been seen to fail.
|
|
COVERS = {
|
|
"hub-confirm": "a native confirm() in templates/partials/ (scope was os.listdir)",
|
|
"manifest-bearer": "a bearer literal in manifests/overlays/ (scope was os.listdir)",
|
|
"observations": "R-419: prose SAYING it carries no marker, plus both genuine markers",
|
|
"reuse-refs": "a cited .go path that does not exist; the .md hole is asserted as R-422",
|
|
"golden-currency": "R-410: an empty directory with a perfect name, checked by what it COUNTED",
|
|
"closed-register": "a verdict cell reading open, and a row with no state cell at all",
|
|
"decoy-coverage": "a gate registered in a runner with no decoy and no exemption (its red-proof)",
|
|
"hub-copy": ("R-558: an English retrieval promise in the NEW bundle (the sentences moved "
|
|
"out of templates.go, so the surface list had to move with them), the same "
|
|
"in Hungarian, and an INNOCENT control using the identical verbs without the "
|
|
"capability claim — the control is the half that matters, because the first "
|
|
"stem list convicted 141 honest sentences"),
|
|
}
|
|
fails = []
|
|
ran = 0
|
|
|
|
|
|
def gate(script, args=()):
|
|
p = subprocess.run([sys.executable, os.path.join("scripts", script)] + list(args),
|
|
cwd=ROOT, capture_output=True, text=True)
|
|
return p.returncode, p.stdout + p.stderr
|
|
|
|
|
|
def decoy(name, script, plant, args=(), expect="convict"):
|
|
"""plant() is a callable returning a cleanup callable."""
|
|
global ran
|
|
ran += 1
|
|
cleanup = plant()
|
|
try:
|
|
rc, out = gate(script, args)
|
|
finally:
|
|
cleanup()
|
|
want_nonzero = (expect == "convict")
|
|
if (rc != 0) != want_nonzero:
|
|
fails.append("%s: decoy %s (rc=%d)\n%s" %
|
|
(name, "PASSED - LIVE HOLE" if want_nonzero else "was wrongly convicted",
|
|
rc, out[-700:]))
|
|
else:
|
|
print(" ok %-20s %s" % (name, "decoy rejected" if want_nonzero else "genuine accepted"))
|
|
|
|
|
|
def plant_file(path, content):
|
|
def _plant():
|
|
made = []
|
|
d = os.path.dirname(path)
|
|
if d and not os.path.isdir(d):
|
|
os.makedirs(d)
|
|
made.append(d)
|
|
io.open(path, "w", encoding="utf-8").write(content)
|
|
|
|
def _clean():
|
|
if os.path.exists(path):
|
|
os.remove(path)
|
|
for m in reversed(made):
|
|
if os.path.isdir(m) and not os.listdir(m):
|
|
os.rmdir(m)
|
|
return _clean
|
|
return _plant
|
|
|
|
|
|
def append_to(path, extra):
|
|
def _plant():
|
|
backup = io.open(path, encoding="utf-8").read()
|
|
io.open(path, "w", encoding="utf-8").write(backup + extra)
|
|
return lambda: io.open(path, "w", encoding="utf-8").write(backup)
|
|
return _plant
|
|
|
|
|
|
T = os.path.join(ROOT, "hub", "internal", "web", "templates")
|
|
M = os.path.join(ROOT, "manifests")
|
|
REP = os.path.join(ROOT, "REPORT.md")
|
|
|
|
print("decoys — felhom.eu")
|
|
|
|
# --- hub-confirm: a native confirm() one directory down (R-421) -------------------------------
|
|
# Shape 1, name-for-fact: the gate used os.listdir, so its SCOPE was a directory listing rather
|
|
# than the set of templates. There are no subdirectories today; adding templates/partials/ is an
|
|
# ordinary act and the gate would have stayed green.
|
|
decoy("hub-confirm/subdir", "hub_confirm_gate.py",
|
|
plant_file(os.path.join(T, "partials", "decoy.html"),
|
|
u'<button onclick="confirm(\'biztos?\')">x</button>\n'))
|
|
|
|
# --- manifest-bearer: a bearer literal one directory down --------------------------------------
|
|
decoy("manifest-bearer/subdir", "manifest_bearer_gate.py",
|
|
plant_file(os.path.join(M, "overlays", "decoy.yaml"),
|
|
u"apiVersion: v1\ndata:\n token: %s\n" % ("a1b2c3d4" * 8)))
|
|
|
|
# --- observations: R-419 itself, and the genuine markers beside it -----------------------------
|
|
# Shape 2, substring-for-field. THE decoy that found this class: an honest note SAYING it has no
|
|
# marker satisfied the marker test.
|
|
decoy("observations/R-419", "observations_gate.py",
|
|
append_to(REP, u"\n## Observations\n\n1. **A real finding.** It carries no `FILED:` marker "
|
|
u"and no `NOT-A-FINDING:` marker, deliberately.\n"), args=(ROOT,))
|
|
decoy("observations/genuine-FILED", "observations_gate.py",
|
|
append_to(REP, u"\n## Observations\n\n1. **A real finding.** Something broke. "
|
|
u"**FILED: R-419**\n"), args=(ROOT,), expect="accept")
|
|
decoy("observations/genuine-NAF", "observations_gate.py",
|
|
append_to(REP, u"\n## Observations\n\n1. **A real finding.** Odd. **NOT-A-FINDING: my own "
|
|
u"typo, corrected in the same minute.**\n"), args=(ROOT,), expect="accept")
|
|
|
|
# --- reuse-refs: a cited path that does not exist ----------------------------------------------
|
|
# The .go case is REJECTED. The .md case is a KNOWN HOLE (R-422) and is asserted as such below, so
|
|
# this file records the hole rather than pretending it is covered.
|
|
decoy("reuse-refs/missing-go", "reuse_refs_check.py",
|
|
append_to(os.path.join(ROOT, "REUSE.md"),
|
|
u"\n- see `hub/internal/api/does_not_exist.go`\n"), args=(ROOT,))
|
|
|
|
# --- KNOWN HOLE, asserted so it cannot be forgotten (R-422) ------------------------------------
|
|
# reuse_refs_check.py's PATH_RE matches only go|py|html|css|yml|yaml|sh. A rotted .md citation is
|
|
# invisible. This asserts the CURRENT behaviour so the day it is fixed, this test fails and is
|
|
# updated deliberately — a hole that nothing asserts is a hole nobody remembers.
|
|
decoy("reuse-refs/missing-md (KNOWN HOLE R-422)", "reuse_refs_check.py",
|
|
append_to(os.path.join(ROOT, "REUSE.md"),
|
|
u"\n- see `documentation/architecture/99-does-not-exist.md`\n"),
|
|
args=(ROOT,), expect="accept")
|
|
|
|
# --- golden-currency: R-410's own decoy, re-run here so the sweep owns it too ------------------
|
|
def _mkdir_decoy():
|
|
d = os.path.join(ROOT, "documentation", "tests", "golden-9.9.9-2026-01-01")
|
|
os.makedirs(d)
|
|
return lambda: os.path.isdir(d) and os.rmdir(d)
|
|
|
|
|
|
def check_golden_names_the_fake():
|
|
"""golden-currency exits 0 either way when currency is fine — the QUESTION is what it counted."""
|
|
global ran
|
|
ran += 1
|
|
cleanup = _mkdir_decoy()
|
|
try:
|
|
_rc, out = gate("golden_currency_gate.py")
|
|
finally:
|
|
cleanup()
|
|
if "newest golden baked : 9.9.9" in out:
|
|
fails.append("golden-currency: an EMPTY directory was counted as a bake — R-410 has regressed")
|
|
elif "NOT counted as bakes" not in out:
|
|
fails.append("golden-currency: the empty directory was neither counted nor REPORTED; a "
|
|
"half-finished bake must be visible, not silently ignored")
|
|
else:
|
|
print(" ok %-20s empty dir rejected AND named" % "golden-currency")
|
|
|
|
|
|
check_golden_names_the_fake()
|
|
|
|
# --- closed-register: the verdict cell is the predicate, deliberately (R-378) -------------------
|
|
# NOT a hole: R-378's whole lesson is that an open word ANYWHERE in a row convicts rows that are
|
|
# 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"),
|
|
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"))
|
|
|
|
# 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
|
|
# sweep — so they were exempt from the only check that reads this file. An unreadable row is now a
|
|
# 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"))
|
|
|
|
# --- hub-copy: an ENGLISH retrieval promise, planted in the bundle (R-558) -----------------------
|
|
#
|
|
# TWO SHAPES AT ONCE, and the second is why this decoy exists at all.
|
|
#
|
|
# Shape "scope is a fact": until hub v0.118.0 every customer sentence was a Go literal in
|
|
# templates.go. Slice 3 moved all of them into locales/*.json. If the gate's CUSTOMER_SURFACES had
|
|
# not moved with them, all four declared files would still exist, the gate would still report
|
|
# success, and it would be scanning a file with no customer sentences left in it.
|
|
#
|
|
# Shape "half a guard": the stems were Hungarian only. A promise written in the new English bundle
|
|
# would have been invisible to a gate that had just been taught to read the file it sits in.
|
|
#
|
|
# The planted sentence is the shape a real translator would write — the English of a claim this
|
|
# product genuinely cannot keep (R-304: nothing reads a retained key, and a customer's correct old
|
|
# code is reported as wrong).
|
|
_EN_BUNDLE = os.path.join(ROOT, "hub", "internal", "i18n", "locales", "en.json")
|
|
|
|
|
|
def _plant_en_promise():
|
|
backup = io.open(_EN_BUNDLE, encoding="utf-8").read()
|
|
d = json.loads(backup)
|
|
d["mail.event.offbox_repo_reset"] = ("The remote backup store has been reset. Your earlier "
|
|
"backups can still be restored with your old recovery code.")
|
|
io.open(_EN_BUNDLE, "w", encoding="utf-8").write(json.dumps(d, ensure_ascii=False, indent=2) + "\n")
|
|
return lambda: io.open(_EN_BUNDLE, "w", encoding="utf-8").write(backup)
|
|
|
|
|
|
decoy("hub-copy/en-promise", "hub_copy_gate.py", _plant_en_promise)
|
|
|
|
|
|
# ...and the CONTROL, which is the half that makes the decoy mean something: an ordinary English
|
|
# sentence using the very same words WITHOUT the capability claim must NOT be convicted. The first
|
|
# version of these stems matched the bare verbs and convicted 141 honest sentences, including
|
|
# "Disaster recovery has started" and the name of the Restore page.
|
|
def _plant_en_innocent():
|
|
backup = io.open(_EN_BUNDLE, encoding="utf-8").read()
|
|
d = json.loads(backup)
|
|
d["mail.event.offbox_repo_reset"] = ("The remote backup store has been reset. Disaster recovery "
|
|
"has finished and the Restore page is available again.")
|
|
io.open(_EN_BUNDLE, "w", encoding="utf-8").write(json.dumps(d, ensure_ascii=False, indent=2) + "\n")
|
|
return lambda: io.open(_EN_BUNDLE, "w", encoding="utf-8").write(backup)
|
|
|
|
|
|
decoy("hub-copy/en-innocent", "hub_copy_gate.py", _plant_en_innocent, expect="accept")
|
|
|
|
|
|
# And the scope half, stated as its own decoy: a HUNGARIAN promise in the bundle. Before v0.118.0
|
|
# this text lived in templates.go and was scanned; if the bundle were not a declared surface, moving
|
|
# the sentence would have moved it out of reach of a gate that was already watching it.
|
|
def _plant_hu_promise():
|
|
hu = os.path.join(ROOT, "hub", "internal", "i18n", "locales", "hu.json")
|
|
backup = io.open(hu, encoding="utf-8").read()
|
|
d = json.loads(backup)
|
|
d["mail.event.offbox_repo_reset"] = ("A tárolót visszaállítottuk. A régi mentéseidet a korábbi "
|
|
"helyreállítási kóddal visszaállíthatod.")
|
|
io.open(hu, "w", encoding="utf-8").write(json.dumps(d, ensure_ascii=False, indent=2) + "\n")
|
|
return lambda: io.open(hu, "w", encoding="utf-8").write(backup)
|
|
|
|
|
|
decoy("hub-copy/hu-in-bundle", "hub_copy_gate.py", _plant_hu_promise)
|
|
|
|
|
|
# --- decoy-coverage: the meta-gate's own red-proof, kept as a test --------------------------------
|
|
# It must convict a gate registered in a runner with no decoy and no exemption. Without this the
|
|
# meta-gate is itself an unchecked instrument, which is the joke this whole sweep exists to avoid.
|
|
# It also convicted ITSELF the moment it was registered, which is how this decoy came to be written.
|
|
ran += 1
|
|
_RUNNER = os.path.join(ROOT, "scripts", "repo_gates.py")
|
|
_b = io.open(_RUNNER, encoding="utf-8").read()
|
|
_anchor = ' ("observations", os.path.join(SCRIPTS, "observations_gate.py"), [ROOT], True, False),'
|
|
try:
|
|
assert _anchor in _b, "the runner's shape changed — this decoy can no longer be built"
|
|
io.open(_RUNNER, "w", encoding="utf-8").write(_b.replace(
|
|
_anchor, _anchor + '\n ("decoy-red-proof", os.path.join(SCRIPTS, "nope.py"), [], True, False),', 1))
|
|
_rc, _out = gate("decoy_coverage_gate.py", (ROOT,))
|
|
finally:
|
|
io.open(_RUNNER, "w", encoding="utf-8").write(_b)
|
|
if _rc == 0:
|
|
fails.append("decoy-coverage: a NEW gate with no decoy and no exemption was ACCEPTED — the "
|
|
"meta-gate cannot see the thing it exists for\n%s" % _out[-500:])
|
|
elif "decoy-red-proof" not in _out:
|
|
fails.append("decoy-coverage: it convicted, but did not NAME the uncovered gate")
|
|
else:
|
|
print(" ok %-20s a new gate with no decoy is convicted BY NAME" % "decoy-coverage")
|
|
|
|
print()
|
|
if fails:
|
|
for f in fails:
|
|
print("FAIL: %s" % f)
|
|
print("\n%d decoy(s) of %d exposed a hole" % (len(fails), ran))
|
|
sys.exit(1)
|
|
print("all %d felhom.eu decoys behaved — labels do not satisfy these gates" % ran)
|