R-426: hostinstall gate gets decoys; two live holes closed, exemption removed

The R-185 check counted the bare word `felhom-backup-target-apply grant`, so the
dry-run echo stood in for a deleted real grant (2 resolutions, 2 "grants"); now
only path-qualified invocations at a command start count. The age check matched
a commented-out install; now comment lines are excluded. Decoys (plus a version
const in a new hub sub-package, and a comment naming the identifier that must
pass) live in test_gate_decoys.py; all three convicting decoys were seen to PASS
against the pre-fix gate. EXEMPT drops `felhom.eu/hostinstall`.

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-06 01:04:56 +02:00
parent 70304a53df
commit c6c6cb7676
3 changed files with 54 additions and 5 deletions
+44
View File
@@ -72,6 +72,8 @@ COVERS = {
"their sentences. Also: a prefix of the real sentence, the OLD Hungarian "
"quote, a reworded bundle, a deleted key, and the scope case — an absent "
"controller clone must be INCONCLUSIVE, never a pass"),
"hostinstall": ("R-426: R-185's regression with only its dry-run echo left, the age install commented "
"out, and a version const in a new hub sub-package; a comment naming the identifier passes"),
"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 "
@@ -393,6 +395,48 @@ decoy("site/unlisted-page", "site_gates.py",
plant_file(os.path.join(ROOT, "website", "zz-r423-decoy.html"),
u"\ufeff<!DOCTYPE html><html><body><nav></nav><footer></footer></body></html>\n"))
def replace_in(path, old, new):
"""Mutate one exact span of a real file; restore it byte-for-byte. A span that is no longer there makes
the decoy UNBUILDABLE, which is a failure of this suite — never a quietly skipped case."""
def _plant():
backup = io.open(path, encoding="utf-8").read()
assert backup.count(old) == 1, "%s: the decoy's anchor is gone or ambiguous — rebuild the decoy" % path
io.open(path, "w", encoding="utf-8").write(backup.replace(old, new, 1))
return lambda: io.open(path, "w", encoding="utf-8").write(backup)
return _plant
# ── hostinstall (R-426, 2026-10-06) ──────────────────────────────────────────────────────────────
#
# Each decoy is a one-line edit a real session would make to felhom-host-install.sh, leaving the LABEL
# the gate used to read while removing the fact. The first two PASSED before 2026-10-06 (live holes):
# * R-185's own regression — the Scenario-F arm loses its real grant but keeps its dry-run echo. The
# gate counted the bare word, so the echo stood in for the grant (2 resolutions, 2 "grants").
# * the `age` install commented out while debugging — the bare search matched the comment.
# * a host-install version const in a NEW hub sub-package — the scope is a walk, not a file list.
# The genuine article is the unmodified tree, which the runner's own `hostinstall` row passes on every push.
_HI = os.path.join(ROOT, "scripts", "felhom-host-install.sh")
decoy("hostinstall/R-185-dry-run-echo-only", "hostinstall_gates.py",
replace_in(_HI,
' /usr/local/sbin/felhom-backup-target-apply grant "$BACKUP_TARGET_ID" \\\n'
' || die "backup target grant failed on the pre-existing target',
' true \\\n'
' || die "backup target grant failed on the pre-existing target'))
_AGE_CMD = (" DEBIAN_FRONTEND=noninteractive apt-get install -y -q age >/dev/null 2>&1 \\\n"
" || { apt-get update -q >/dev/null 2>&1; DEBIAN_FRONTEND=noninteractive apt-get install -y -q age >/dev/null 2>&1; } \\\n"
" || die \"failed to install the 'age' package (the escrow ceremony needs it)\"\n")
decoy("hostinstall/age-commented-out", "hostinstall_gates.py",
replace_in(_HI, _AGE_CMD, "".join("#" + l[1:] + "\n" for l in _AGE_CMD.splitlines())))
decoy("hostinstall/hub-version-const-subpkg", "hostinstall_gates.py",
plant_file(os.path.join(ROOT, "hub", "internal", "web", "zzr426decoy", "v.go"),
u'package zzr426decoy\n\nconst hostInstallVersion = "1.32.0"\n'))
# the genuine article beside the third: a comment that merely NAMES the identifier must pass (configs.go
# carries exactly such a note), so the scope widening is not bought with false convictions.
decoy("hostinstall/comment-names-identifier", "hostinstall_gates.py",
plant_file(os.path.join(ROOT, "hub", "internal", "web", "zzr426decoy", "v.go"),
u'package zzr426decoy\n\n// hostInstallVersion was deleted on purpose (R-94).\n'),
expect="accept")
# ── script-tests (R-885) ─────────────────────────────────────────────────────────────────────────
#
# Its decoys are whole fake repos, so they live in their own file and are RUN from here (guide-quote's shape).