burn-down round 2: R-593 papra field copy, R-760 vikunja healthcheck reason, R-594 English allow-list, R-605 refusal exit 3, R-781 onboarding decoy clone, R-806 scheme; stale runner test fixed
gates / gates (push) Successful in 4s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-05 19:15:02 +02:00
parent 29ac711d26
commit 4828dc754d
16 changed files with 536 additions and 50 deletions
+52 -1
View File
@@ -57,7 +57,7 @@ COVERS = {
"probe-measured": "the measurement written in the TAGLINE or another comment block, not directly above setup_done_probe:; a date with no before/after; before/after with no date; 'read upstream' instead of 'measured' - vs a genuine measured comment (R-715)",
"family-gate": "family_gate written only in a COMMENT (not gated, no min_controller owed); min_controller only in a comment; a golden DIRECTORY named 0.287.0 with no bake log (the mkdir shape, R-410); a sibling with no golden (stated NOT CHECKED, never a pass of rule 3) - vs the facts: an unanchorable exception (regex, '/', '..'), an exception list with no gate, min_controller below 0.287.0, the newest baked golden below 0.287.0; and a genuine family app passes (decisions 63/64, finding F1)",
"onboarding": "a NEW template with no record; a record missing an id, or carrying it only inside an HTML comment; a `done` whose path does not exist, is an EMPTY directory (the mkdir shape, R-410) or names an absent sibling-repo file; an `n/a` with an empty or two-word reason; an `open` row; `opened:` backdated before the checklist; the template a new app copies lacking a new id - vs a complete record, an id added after `opened:`, and an exempt app's record with open rows (NEW-APP-CHECKLIST.md)",
"copy-i18n": "Hungarian edited in a COMMENT/README/display_name (label, not copy) vs a real frozen string changed; an English block that is not English, is not matched to a Hungarian twin, or rewrites a credential (R-560). Also the DEGRADED mode CI actually runs — PyYAML shadowed out, freeze only (R-595)",
"copy-i18n": "Hungarian edited in a COMMENT/README/display_name (label, not copy) vs a real frozen string changed; an English block that is not English, is not matched to a Hungarian twin, or rewrites a credential (R-560). A retrieval promise REGISTERED in ALLOWLIST_EN passes only for its own app+path+sentence with a real reason; an entry for another app, a rewritten sentence, a stale entry or a two-word reason convicts (R-594). Also the DEGRADED mode CI actually runs — PyYAML shadowed out, freeze only (R-595)",
}
fails = []
@@ -488,6 +488,26 @@ def test_record_cases(clone):
[(NF, tr_append(rt(TR_D1, TR_D1, box_evidence="x")))] + with_steps, 1, ("no new digest",), {old: TR_D1})
def isolate_onboarding_clone(cat):
"""R-781: the cases judge ONLY the records they build. The clone carries the REAL published records (the new apps',
and the exempt apps' shape-checked ones), and those cite evidence in the real felhom.eu — which the stand-in
sibling does not hold — so a genuine case read incomplete on an untouched tree (4 FAILs). Remove every real record
from the scratch clone, and every non-exempt template directory with it (without its record a real new app would
itself convict). The real tree is never touched; the real records stay judged by the real gate run."""
src = io.open(os.path.join(cat, "scripts", "check-onboarding.py"), encoding="utf-8").read()
exempt = set(re.search(r'EXEMPT = frozenset\("""(.*?)"""', src, re.S).group(1).split())
if len(exempt) < 40:
raise SystemExit("check-onboarding.py's EXEMPT list read as %d apps — the fixture drifted" % len(exempt))
onb = os.path.join(cat, "onboarding")
for name in os.listdir(onb):
if name.endswith(".md") and name != "_TEMPLATE.md":
os.remove(os.path.join(onb, name))
tdir = os.path.join(cat, "templates")
for name in os.listdir(tdir):
if os.path.isdir(os.path.join(tdir, name)) and name not in exempt:
shutil.rmtree(os.path.join(tdir, name))
def onboarding_cases():
"""The onboarding gate reads FILES — the checklist, the template, the records, and evidence paths that may live
in a SIBLING repository. So each case runs in its own scratch WORKSPACE: <ws>/app-catalog-felhom.eu (a clone,
@@ -500,6 +520,7 @@ def onboarding_cases():
for rel in ("NEW-APP-CHECKLIST.md", os.path.join("onboarding", "_TEMPLATE.md")):
os.makedirs(os.path.dirname(os.path.join(cat, rel)) or cat, exist_ok=True)
shutil.copy(os.path.join(ROOT, rel), os.path.join(cat, rel))
isolate_onboarding_clone(cat)
sib = os.path.join(ws, "felhom.eu", "documentation", "audits", "onb")
os.makedirs(sib)
with io.open(os.path.join(sib, "proof.txt"), "w", encoding="utf-8") as fh:
@@ -889,6 +910,36 @@ i18n:
[(PB, en_with("Password protection for extra safety",
"Deleted notes can still be restored later"))],
expect_rc=1, must_contain=("retrieval promise",))
# ALLOWLIST_EN (R-594): a TRUE retrieval promise can be REGISTERED with a reason; the label without the fact
# must not pass. The register lives beside the freeze so the clone's own copy is the one judged.
AL = "scripts/copy_freeze/allowlist_en.json"
PROMISE = ("Password protection for extra safety", "Deleted notes can still be restored later")
WHY = "privatebin keeps a burned paste for its expiry window, so it is true here (decoy)"
def allow(*entries):
return lambda t: json.dumps({"_what": "decoy", "entries": [
dict(zip(("app", "path", "match", "reason"), e)) for e in entries]}, indent=1) + "\n"
case_copy("GENUINE: a REGISTERED true retrieval promise passes", clone,
[(PB, en_with(*PROMISE)),
(AL, allow(("privatebin", "app_info.use_cases[4]", "can still be restored", WHY)))],
expect_rc=0, must_contain=("copy-i18n: OK", "1 registered retrieval promise(s) in ALLOWLIST_EN, 1 used"))
case_copy("FACT: registered for ANOTHER app - the promise still convicts, the entry is stale", clone,
[(PB, en_with(*PROMISE)),
(AL, allow(("vaultwarden", "app_info.use_cases[4]", "can still be restored", WHY)))],
expect_rc=1, must_contain=("retrieval promise the Hungarian does not make", "STALE entry vaultwarden"))
case_copy("FACT: registered on the path, but the sentence was rewritten (match gone)", clone,
[(PB, en_with(PROMISE[0], "Deleted notes can be recovered at any time")),
(AL, allow(("privatebin", "app_info.use_cases[4]", "can still be restored", WHY)))],
expect_rc=1, must_contain=("retrieval promise the Hungarian does not make", "STALE entry privatebin"))
case_copy("FACT: a STALE entry - nothing in the English promises it any more", clone,
[(AL, allow(("privatebin", "app_info.use_cases[4]", "can still be restored", WHY)))],
expect_rc=1, must_contain=("STALE entry privatebin",))
case_copy("FACT: a registered promise with a two-word reason", clone,
[(PB, en_with(*PROMISE)),
(AL, allow(("privatebin", "app_info.use_cases[4]", "can still be restored", "it's fine")))],
expect_rc=1, must_contain=("no reason worth the name", "retrieval promise the Hungarian does not make"))
# A credential is a LOGIN, not prose: gokapi's default_creds carries admin / adminadmin.
GK = "templates/gokapi/.felhom.yml"
case_copy("FACT: a credential token rewritten in translation", clone,