v0.214.0 — the recovery screen stops hedging about a code it can now check (R-311)
gates / gates (push) Successful in 13s
gates / gates (push) Successful in 13s
MinAgent: 0.129.0 What was already right: the screen did not bluntly accuse. R-222/R-226 hedged, naming both causes and the kept package, and saying it could not tell them apart. That was honest - and it could not tell them apart because nothing ever looked. Agent v0.129.0 looks, so the hedge becomes an answer. New class RecoveryCodeOpensRetained on HTTP 422, gated by FeatureRetainedRecoveryClass (MinAgent 0.129.0). The gate is SEPARATE from the R-224 one because the two name different agent versions and a box can sit between them, where a 422 is a shape we did not design. ClassifyRecoveryFailure therefore takes both flags; the compiler found every call site. The message says the code is correct, names the supersession date, says the earlier package is kept, and says the CURRENT backups are unaffected - the half a customer will otherwise assume wrong. It promises NO restore: there is no in-product route to a set-aside store (R-312) and the retained package may itself predate the repository-password field. It routes to support, which can do it. The claim guard grew a surface and immediately convicted something. It scanned templates only, while every recovery message is a Go string in a handler - the highest-stakes copy in the product, never scanned. It now scans recovery_handlers.go too, and found a PRE-EXISTING unregistered claim on its first run. Six handler tests asserting which SENTENCE the customer sees; red-proofs asserted applied, including: 422 unconditional makes an agent that never looked read as having looked, and routing 400 to the new class congratulates a mistype.
This commit is contained in:
@@ -27,10 +27,28 @@ import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
TEMPLATES = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "internal", "web", "templates")
|
||||
_HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
TEMPLATES = os.path.join(_HERE, "..", "internal", "web", "templates")
|
||||
|
||||
# R-311 — THE GATE HAD A BLIND SPOT THE SIZE OF THE RECOVERY SCREEN.
|
||||
#
|
||||
# It scanned `internal/web/templates` only. But every one of the recovery screen's messages is a Go
|
||||
# STRING in a handler, not template text — including the four R-224 messages and the R-222/R-226 one,
|
||||
# i.e. the highest-stakes customer copy in the product, on the one screen whose whole purpose is to be
|
||||
# believed about someone's backups. None of it had ever been scanned.
|
||||
#
|
||||
# Go `//` and `/* */` comments are stripped for the same reason template comments are: they never
|
||||
# reach a customer. (A `//` inside a Hungarian string literal would be stripped too — there are none,
|
||||
# and a false NEGATIVE there is the safe direction for a guard that convicts on presence.)
|
||||
GO_SOURCES = [
|
||||
os.path.join(_HERE, "..", "internal", "web", "recovery_handlers.py".replace(".py", ".go")),
|
||||
]
|
||||
|
||||
# The verbs that carry the claim "your old backups can be got back".
|
||||
STEMS = ["visszaállíthat", "visszaszerezhet", "visszahozhat"]
|
||||
# R-311 adds `visszanyit`: the honest new message says a customer needs support's help „a régebbi
|
||||
# előzményed visszanyitásához". That is the SAME claim in a fourth verb, and the docstring above
|
||||
# records what happens when the guard chases words instead of claims — it misses the next one.
|
||||
STEMS = ["visszaállíthat", "visszaszerezhet", "visszahozhat", "visszanyit"]
|
||||
|
||||
# (template, substring that identifies the occurrence) -> why it is allowed.
|
||||
# The substring must be specific enough that a DIFFERENT claim in the same file does not match it.
|
||||
@@ -53,20 +71,38 @@ ALLOWLIST = {
|
||||
"the banner's promise — R-302 made it conditional on RecoveryAbandonRetrievalOffered; TRUE branch.",
|
||||
("layout.html", "Hogy ezek még visszaszerezhetők-e"):
|
||||
"R-302's cautious branch on the banner. As above.",
|
||||
("recovery_handlers.go", "A régi előzmény visszanyitása felülírná azt"):
|
||||
"PRE-EXISTING and never scanned until R-311 extended this gate to Go handlers — which is the "
|
||||
"point of extending it. It is NOT a promise: it is the reason for a REFUSAL (RecoverRefused, "
|
||||
"a different repository password is already present), i.e. the sentence says the reopening "
|
||||
"would overwrite and was therefore not done. Registered as an explanation, not a claim.",
|
||||
("recovery_handlers.go", "A régebbi előzményed visszanyitásához a Felhom ügyfélszolgálatának segítsége kell"):
|
||||
"R-311's truthful message for a code that opens a RETAINED package. It is a claim, and it is "
|
||||
"TRUE: the drill of 2026-08-12 recovered exactly this by hand (unsealed the retained package, "
|
||||
"opened the set-aside store, restored planted files byte-identical). It routes to SUPPORT "
|
||||
"rather than to a button precisely because there is no in-product route yet — the restore "
|
||||
"machinery resolves its repository from settings and its password from one file. If that route "
|
||||
"is ever built, this entry changes; if support ever cannot do it, this sentence must go.",
|
||||
("recovery.html", "a mentéseid visszaszerezhetők, és a törlés elmarad;"):
|
||||
"the abandon CONFIRMATION screen, shown at the moment of the decision. True by construction "
|
||||
"there: the package the hub holds right now is the one about to be pinned. Left alone.",
|
||||
}
|
||||
|
||||
TEMPLATE_COMMENT = re.compile(r"\{\{/\*.*?\*/\}\}", re.S)
|
||||
GO_COMMENT = re.compile(r"//[^\n]*|/\*.*?\*/", re.S)
|
||||
|
||||
|
||||
def scan():
|
||||
convictions, seen_keys = [], set()
|
||||
files = sorted(f for f in os.listdir(TEMPLATES) if f.endswith(".html"))
|
||||
for name in files:
|
||||
path = os.path.join(TEMPLATES, name)
|
||||
text = TEMPLATE_COMMENT.sub("", open(path, encoding="utf-8").read())
|
||||
sources = [(f, os.path.join(TEMPLATES, f), TEMPLATE_COMMENT) for f in files]
|
||||
for gp in GO_SOURCES:
|
||||
if not os.path.exists(gp):
|
||||
raise SystemExit(f"retrieval-promise gate: declared Go source is missing: {gp}")
|
||||
sources.append((os.path.basename(gp), gp, GO_COMMENT))
|
||||
files = files + [os.path.basename(gp)]
|
||||
for name, path, stripper in sources:
|
||||
text = stripper.sub("", open(path, encoding="utf-8").read())
|
||||
for stem in STEMS:
|
||||
for m in re.finditer(re.escape(stem) + r"[a-záéíóöőúüű]*", text):
|
||||
line = text[: m.start()].count("\n") + 1
|
||||
@@ -105,8 +141,8 @@ def main():
|
||||
print("claim, make it CONDITIONAL on what the box can see; if it is a question about")
|
||||
print("knowability, or an unrelated local-backup sentence, add it to ALLOWLIST with the reason.")
|
||||
return 1
|
||||
print(f"retrieval-promise gate OK — {len(files)} templates, {len(ALLOWLIST)} registered claim(s), "
|
||||
f"none unregistered")
|
||||
print(f"retrieval-promise gate OK — {len(files)} surface(s) incl. {len(GO_SOURCES)} Go handler file(s), "
|
||||
f"{len(ALLOWLIST)} registered claim(s), none unregistered")
|
||||
print(" (BLIND SPOT: it registers WHERE the claim is made, not whether each conditional is wired")
|
||||
print(" to a true predicate — that is what the R-302 render tests are for.)")
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user