R-502: the ISO first-boot harness is a gate, full runs only, "not checked" without docker
iso_bootstrap_gate.py runs scripts/iso/test/bootstrap-modes.sh in felhom-iso-assistant:trixie (staged copy, read-only mount, --network none), registered fast=False in repo_gates.py so the pre-push hook and CI (both --fast) never run it (decision 147). No docker / no image / docker error -> exit 2 NOT CHECKED. Every green run is followed by a built-in decoy: the harness must FAIL a bootstrap whose pairing banner never paints (R-496 shape), or the gate convicts the instrument as blind. Docker-free decoys in test_iso_bootstrap_gate.py (fake docker on a one-directory PATH), run from test_gate_decoys.py (COVERS). 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:
@@ -0,0 +1,157 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""iso_bootstrap_gate.py — the ISO first-boot harness, run as a gate (R-502, decision 147).
|
||||
|
||||
Usage: python3 scripts/iso_bootstrap_gate.py
|
||||
Exit 0 harness green AND its planted-broken copy convicted · 1 a harness check failed, or the harness
|
||||
could not see a planted broken banner · 2 NOT CHECKED (no docker, no image, docker itself failed).
|
||||
|
||||
WHAT IT RUNS. scripts/iso/test/bootstrap-modes.sh — felhom-bootstrap.sh's two modes, the network gate,
|
||||
the console banners against their goldens and the postinst — inside `felhom-iso-assistant:trixie`.
|
||||
Until this gate it ran only by hand, and it was RED for two days before anyone saw (R-586).
|
||||
|
||||
WHERE IT RUNS. Full runs only — `fast=False` in repo_gates.py, so never in --fast, never in the
|
||||
pre-push hook, never in CI (both call --fast; CI has no docker). Operator ruling 2026-10-06, `09` §3
|
||||
decision 147. Pinned by scripts/test_iso_bootstrap_gate.py (`test_registered_full_runs_only`).
|
||||
|
||||
NOT CHECKED IS NEVER A PASS. No docker binary, no image, or a docker error (exit 125-127, a timeout)
|
||||
is exit 2 with the words "not checked". The image is built by hand:
|
||||
docker build -f scripts/iso/Dockerfile.assistant -t felhom-iso-assistant:trixie scripts/iso
|
||||
|
||||
THE TREE STAYS CLEAN. The harness writes into /work (fakes, logs, /etc/felhom). It never sees the repo:
|
||||
the four inputs are copied to a temp dir, mounted READ-ONLY at /src, and copied to /work INSIDE the
|
||||
container. `--network none`: the harness fakes curl and needs no network.
|
||||
|
||||
THE BUILT-IN DECOY (the positive control, every run). After a green run the gate runs the harness a
|
||||
second time against a copy of felhom-bootstrap.sh whose print_pairing_banner returns at once — the
|
||||
exact shape that left the household's first screen untested until ISO 1.27.0 (R-496). The harness
|
||||
MUST fail it, naming the banner. If it passes, the instrument is blind and the green run proved
|
||||
nothing: exit 1. A green run is also required to show its own pass line AND at least MIN_OK `ok:`
|
||||
lines — a harness that printed the pass line and checked nothing is not green.
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import uuid
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
ISO = os.path.join(ROOT, "scripts", "iso")
|
||||
IMAGE = "felhom-iso-assistant:trixie"
|
||||
TIMEOUT_S = 300
|
||||
# The harness carried 76 `ok:` checks on 2026-10-06. Far below that, deliberately: this is the floor
|
||||
# of "it ran", not a count to keep in step.
|
||||
MIN_OK = 40
|
||||
PASS_LINE = "ALL BOOTSTRAP-MODE TESTS PASSED"
|
||||
|
||||
# The planted decoy: the banner function returns before painting.
|
||||
MUTANT_ANCHOR = "print_pairing_banner() {\n"
|
||||
MUTANT_TEXT = MUTANT_ANCHOR + " return 0 # R-502 planted decoy: the banner never paints\n"
|
||||
# A FAIL line the mutant must produce (ASCII fragment of the harness's own check name).
|
||||
MUTANT_MUST_FAIL = re.compile(r"^\s*FAIL: R-496: banner painted", re.M)
|
||||
|
||||
INPUTS = [ # (repo path, path under /work) — what the harness reads, per its own header
|
||||
(os.path.join(ISO, "felhom-bootstrap.sh"), "felhom-bootstrap.sh"),
|
||||
(os.path.join(ISO, "pkg", "debian", "postinst"), "postinst"),
|
||||
(os.path.join(ISO, "test", "golden"), "golden"),
|
||||
(os.path.join(ISO, "test", "bootstrap-modes.sh"), os.path.join("test", "bootstrap-modes.sh")),
|
||||
]
|
||||
|
||||
|
||||
def not_checked(why):
|
||||
print("iso-bootstrap: NOT CHECKED — %s" % why)
|
||||
print("iso-bootstrap: INCONCLUSIVE (exit 2) — an unrun harness is never a pass")
|
||||
return 2
|
||||
|
||||
|
||||
def stage(dest, mutate=False):
|
||||
"""Copy the harness inputs into dest. Returns None, or a reason the staging failed."""
|
||||
for src, rel in INPUTS:
|
||||
if not os.path.exists(src):
|
||||
return "harness input missing: %s" % src
|
||||
out = os.path.join(dest, rel)
|
||||
os.makedirs(os.path.dirname(out), exist_ok=True)
|
||||
if os.path.isdir(src):
|
||||
shutil.copytree(src, out)
|
||||
else:
|
||||
shutil.copy2(src, out)
|
||||
if mutate:
|
||||
p = os.path.join(dest, "felhom-bootstrap.sh")
|
||||
with open(p, encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
if text.count(MUTANT_ANCHOR) != 1:
|
||||
return ("the decoy anchor %r occurs %d time(s) in felhom-bootstrap.sh (want 1) — the "
|
||||
"built-in decoy cannot be planted" % (MUTANT_ANCHOR.strip(), text.count(MUTANT_ANCHOR)))
|
||||
with open(p, "w", encoding="utf-8") as f:
|
||||
f.write(text.replace(MUTANT_ANCHOR, MUTANT_TEXT))
|
||||
return None
|
||||
|
||||
|
||||
def run_harness(docker, mutate):
|
||||
"""Return (rc, output) of one harness run; rc None means docker itself failed (output says why)."""
|
||||
tmp = tempfile.mkdtemp(prefix="felhom-iso-gate-")
|
||||
name = "felhom-iso-gate-%s" % uuid.uuid4().hex[:12]
|
||||
try:
|
||||
err = stage(tmp, mutate)
|
||||
if err:
|
||||
return None, err
|
||||
cmd = [docker, "run", "--rm", "--name", name, "--network", "none",
|
||||
"-v", "%s:/src:ro" % tmp, IMAGE, "bash", "-c",
|
||||
"mkdir -p /work && cp -a /src/. /work/ && exec bash /work/test/bootstrap-modes.sh"]
|
||||
try:
|
||||
p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, timeout=TIMEOUT_S)
|
||||
except subprocess.TimeoutExpired:
|
||||
subprocess.run([docker, "rm", "-f", name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
return None, "the harness did not finish in %d s (container %s removed)" % (TIMEOUT_S, name)
|
||||
out = p.stdout.decode("utf-8", "replace")
|
||||
if p.returncode in (125, 126, 127):
|
||||
return None, "docker run exited %d:\n%s" % (p.returncode, out[-600:])
|
||||
return p.returncode, out
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
|
||||
|
||||
def main():
|
||||
docker = shutil.which("docker")
|
||||
if not docker:
|
||||
return not_checked("no docker on PATH")
|
||||
p = subprocess.run([docker, "image", "inspect", IMAGE],
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
|
||||
if p.returncode != 0:
|
||||
return not_checked("image %s is absent or docker is unreachable (%s). Build it with: docker build "
|
||||
"-f scripts/iso/Dockerfile.assistant -t %s scripts/iso"
|
||||
% (IMAGE, p.stderr.decode("utf-8", "replace").strip()[:200], IMAGE))
|
||||
|
||||
rc, out = run_harness(docker, mutate=False)
|
||||
if rc is None:
|
||||
return not_checked(out)
|
||||
print(out.rstrip())
|
||||
oks = len(re.findall(r"^\s*ok: ", out, re.M))
|
||||
fails = re.findall(r"^\s*FAIL: .*$", out, re.M)
|
||||
if rc != 0 or fails:
|
||||
print("\niso-bootstrap: FAILED — the harness exited %d with %d failing check(s):" % (rc, len(fails)))
|
||||
for f in fails:
|
||||
print(" " + f.strip())
|
||||
return 1
|
||||
if PASS_LINE not in out or oks < MIN_OK:
|
||||
print("\niso-bootstrap: FAILED — exit 0 but %s (pass line %s, %d ok lines, want >= %d)"
|
||||
% ("the harness proved nothing", "present" if PASS_LINE in out else "ABSENT", oks, MIN_OK))
|
||||
return 1
|
||||
|
||||
mrc, mout = run_harness(docker, mutate=True)
|
||||
if mrc is None:
|
||||
return not_checked("the built-in decoy could not run: %s" % mout)
|
||||
if mrc == 0 or not MUTANT_MUST_FAIL.search(mout):
|
||||
print("\niso-bootstrap: FAILED — the harness PASSED a bootstrap whose pairing banner never paints "
|
||||
"(exit %d). The instrument is blind; the green run above proves nothing." % mrc)
|
||||
print(mout[-800:])
|
||||
return 1
|
||||
print("\niso-bootstrap: built-in decoy convicted (a banner that never paints -> harness exit %d)" % mrc)
|
||||
print("iso-bootstrap gate OK — %d harness checks green in %s" % (oks, IMAGE))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -27,6 +27,8 @@ Gates, in order (all must pass; **non-zero exit on any failure**):
|
||||
14b. stands where-felhom-stands.yaml: every claim cites a source that resolves, and every
|
||||
'walked' cites a walk (R-819; it was red and ran in no runner)
|
||||
15. script-tests every Python test suite under scripts/ (found by a walk), by exit code (R-885)
|
||||
15b. iso-bootstrap the ISO first-boot harness in felhom-iso-assistant:trixie, with a built-in
|
||||
decoy — FULL RUNS ONLY, "not checked" (exit 2) without docker (R-502, decision 147)
|
||||
16. decoy-coverage every registered gate in all four repos has a decoy, or a named exemption (R-421)
|
||||
|
||||
**THE `GATES` TABLE BELOW IS THE LIST; THIS IS A POINTER TO IT.** It drifted once already —
|
||||
@@ -162,6 +164,11 @@ GATES = [
|
||||
# R-885 — the Python test suites under scripts/ (found by a walk) ran only by hand; a change that broke the
|
||||
# 15 tests of the DooPlex hub-DB scripts, or the 73 of the instructions gate, reached main green. ~20 s.
|
||||
("script-tests", os.path.join(SCRIPTS, "script_tests_gate.py"), [ROOT], True, False),
|
||||
# R-502 — the ISO first-boot harness (scripts/iso/test/bootstrap-modes.sh) was run by no gate and was RED
|
||||
# for two days unseen (R-586). It needs docker and an image, so it is fast=False: FULL RUNS ONLY, never
|
||||
# the pre-push hook, never CI (operator ruling 2026-10-06, `09` §3 decision 147). Without docker or the
|
||||
# image it says "not checked" and exits 2 — never a pass. Pinned by test_iso_bootstrap_gate.py.
|
||||
("iso-bootstrap", os.path.join(SCRIPTS, "iso_bootstrap_gate.py"), [], False, False),
|
||||
# R-421 — every registered gate across all four repos ships with a decoy test, or is
|
||||
# named in the exemption list with its row. Registered LAST, after every runner was green:
|
||||
# a failing gate refuses every push, which is what instructions_gate learned the hard way.
|
||||
|
||||
@@ -53,6 +53,11 @@ COVERS = {
|
||||
"(2026-10-03) an old-shape row under the new header, a near-miss category, an "
|
||||
"old rank tag as Sev, an undefined state word, and a pipe outside backticks"),
|
||||
"decoy-coverage": "a gate registered in a runner with no decoy and no exemption (its red-proof)",
|
||||
"iso-bootstrap": ("R-502: SIX decoys + the genuine article in scripts/test_iso_bootstrap_gate.py, run from here, docker-free (a "
|
||||
"fake docker on a one-directory PATH): a BLIND harness that passes a bootstrap whose "
|
||||
"pairing banner never paints (the R-496 shape), a failing harness, the pass line with no "
|
||||
"checks behind it, and no docker / no image / a docker error, each 'not checked' (exit 2); "
|
||||
"the gate also plants that broken banner itself in the real container on every full run"),
|
||||
"instructions": "R-426: a version literal in a CLAUDE.md's effective text; the same in an HTML comment must pass",
|
||||
"wire-contract": ("R-555: an emitted tag whose name the receiver carries ONLY in a // and a /* */ comment "
|
||||
"must convict (it passed for months as `language` did); the genuine article — the same "
|
||||
@@ -455,6 +460,19 @@ else:
|
||||
_n = _gq.stdout.strip().splitlines()[-1] if _gq.stdout.strip() else "?"
|
||||
print(" ok %-20s %s" % ("guide-quote", _n))
|
||||
|
||||
# ── iso-bootstrap (R-502) ────────────────────────────────────────────────────────────────────────
|
||||
#
|
||||
# Its decoys need a fake docker on a one-directory PATH, so they live in their own file and are RUN from
|
||||
# here (guide-quote's shape). The suite never reaches the real docker, so this stays CI-safe.
|
||||
ran += 1
|
||||
_ib = subprocess.run([sys.executable, os.path.join("scripts", "test_iso_bootstrap_gate.py")],
|
||||
cwd=ROOT, capture_output=True, text=True)
|
||||
if _ib.returncode != 0:
|
||||
fails.append("iso-bootstrap: its decoy suite FAILED — a decoy did not convict\n%s"
|
||||
% (_ib.stdout + _ib.stderr)[-800:])
|
||||
else:
|
||||
print(" ok %-20s %s" % ("iso-bootstrap", (_ib.stderr.strip().splitlines() or ["?"])[-1]))
|
||||
|
||||
# ── site (R-423) ─────────────────────────────────────────────────────────────────────────────────
|
||||
# A page that exists but is not in PAGES. The content is a perfectly valid page, so only the scope rule can convict.
|
||||
decoy("site/unlisted-page", "site_gates.py",
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Decoys for scripts/iso_bootstrap_gate.py (R-502). DOCKER-FREE — runs on the CI runner (BusyBox +
|
||||
python3 + git) and on DooPlex alike, and never reaches the real docker.
|
||||
|
||||
HOW. The gate finds docker with shutil.which, so every case runs the REAL gate as a subprocess with
|
||||
PATH set to ONE temp directory: empty (no docker), or holding a FAKE `docker` written in python with
|
||||
an absolute shebang (so it needs nothing else on PATH — /usr/bin, where the real docker lives on
|
||||
DooPlex, is never on it). The fake answers `image inspect` and `run` per FAKE_DOCKER_MODE, and for
|
||||
`run` reads the STAGED felhom-bootstrap.sh from the `-v <dir>:/src:ro` mount, so it can tell the
|
||||
genuine run from the gate's built-in banner decoy.
|
||||
|
||||
What the fake cannot do is run the harness — that needs root and the image. The harness's power to
|
||||
see a broken banner is proven by the gate itself, in the real container, on every full run (its
|
||||
built-in decoy). This file proves the gate's verdicts: that a blind harness, a failing harness, a
|
||||
harness that checked nothing, and an unrun harness can never read as green.
|
||||
|
||||
Run: python3 scripts/test_iso_bootstrap_gate.py
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
ROOT = os.path.dirname(HERE)
|
||||
GATE = os.path.join(HERE, "iso_bootstrap_gate.py")
|
||||
sys.path.insert(0, HERE)
|
||||
import iso_bootstrap_gate as g # noqa: E402
|
||||
import repo_gates # noqa: E402
|
||||
|
||||
FAKE = r'''#!%(py)s
|
||||
import os, re, sys
|
||||
mode = os.environ.get("FAKE_DOCKER_MODE", "genuine")
|
||||
a = sys.argv[1:]
|
||||
log = os.environ.get("FAKE_DOCKER_LOG")
|
||||
if log:
|
||||
open(log, "a").write(" ".join(a) + "\n")
|
||||
if a[:2] == ["image", "inspect"]:
|
||||
sys.exit(1 if mode == "no-image" else 0)
|
||||
if a[:1] == ["rm"]:
|
||||
sys.exit(0)
|
||||
if a[:1] != ["run"]:
|
||||
sys.exit(3)
|
||||
if mode == "daemon-error":
|
||||
print("docker: Error response from daemon: something broke."); sys.exit(125)
|
||||
src = [x for x in a if x.endswith(":/src:ro")][0][:-len(":/src:ro")]
|
||||
mutant = "R-502 planted decoy" in open(os.path.join(src, "felhom-bootstrap.sh"), encoding="utf-8").read()
|
||||
oks = "".join(" ok: check %%d\n" %% i for i in range(60))
|
||||
if mode == "fails" or (mutant and mode != "blind"):
|
||||
print(oks + " FAIL: R-496: banner painted to the console seam\nSOME TESTS FAILED"); sys.exit(1)
|
||||
if mode == "hollow":
|
||||
print("ALL BOOTSTRAP-MODE TESTS PASSED"); sys.exit(0)
|
||||
print(oks + "ALL BOOTSTRAP-MODE TESTS PASSED"); sys.exit(0)
|
||||
'''
|
||||
|
||||
|
||||
def run_gate(mode=None):
|
||||
"""Run the real gate. mode None = no docker on PATH at all."""
|
||||
d = tempfile.mkdtemp(prefix="iso-gate-test-")
|
||||
try:
|
||||
log = os.path.join(d, "calls.log")
|
||||
if mode is not None:
|
||||
p = os.path.join(d, "docker")
|
||||
with open(p, "w") as f:
|
||||
f.write(FAKE % {"py": sys.executable})
|
||||
os.chmod(p, 0o755)
|
||||
env = {"PATH": d, "FAKE_DOCKER_MODE": mode or "", "FAKE_DOCKER_LOG": log,
|
||||
"HOME": d, "PYTHONDONTWRITEBYTECODE": "1"}
|
||||
r = subprocess.run([sys.executable, GATE], cwd=ROOT, env=env,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
calls = ""
|
||||
if os.path.exists(log):
|
||||
with open(log) as f:
|
||||
calls = f.read()
|
||||
return r.returncode, r.stdout.decode("utf-8", "replace"), calls
|
||||
finally:
|
||||
shutil.rmtree(d, ignore_errors=True)
|
||||
|
||||
|
||||
class IsoBootstrapGateTest(unittest.TestCase):
|
||||
|
||||
def test_genuine_passes_and_runs_the_decoy_too(self):
|
||||
rc, out, calls = run_gate("genuine")
|
||||
self.assertEqual(rc, 0, out)
|
||||
self.assertIn("built-in decoy convicted", out)
|
||||
self.assertEqual(calls.count("run "), 2, "want the genuine run AND the decoy run:\n" + calls)
|
||||
self.assertIn("--network none", calls)
|
||||
|
||||
def test_blind_harness_convicts(self):
|
||||
# The harness passes a bootstrap whose banner never paints: the R-496 shape. Must be 1.
|
||||
rc, out, _ = run_gate("blind")
|
||||
self.assertEqual(rc, 1, out)
|
||||
self.assertIn("instrument is blind", out)
|
||||
|
||||
def test_failing_harness_convicts(self):
|
||||
rc, out, calls = run_gate("fails")
|
||||
self.assertEqual(rc, 1, out)
|
||||
self.assertIn("FAIL: R-496: banner painted", out)
|
||||
self.assertEqual(calls.count("run "), 1)
|
||||
|
||||
def test_pass_line_without_checks_convicts(self):
|
||||
rc, out, _ = run_gate("hollow")
|
||||
self.assertEqual(rc, 1, out)
|
||||
self.assertIn("proved nothing", out)
|
||||
|
||||
def test_no_docker_is_not_checked(self):
|
||||
rc, out, _ = run_gate(None)
|
||||
self.assertEqual(rc, 2, out)
|
||||
self.assertIn("NOT CHECKED", out)
|
||||
|
||||
def test_no_image_is_not_checked(self):
|
||||
rc, out, calls = run_gate("no-image")
|
||||
self.assertEqual(rc, 2, out)
|
||||
self.assertIn("NOT CHECKED", out)
|
||||
self.assertNotIn("run ", calls)
|
||||
|
||||
def test_docker_error_is_not_checked(self):
|
||||
rc, out, _ = run_gate("daemon-error")
|
||||
self.assertEqual(rc, 2, out)
|
||||
|
||||
def test_decoy_plants_on_the_real_bootstrap(self):
|
||||
# The built-in decoy's anchor must exist ONCE in the real script, and the plant must apply.
|
||||
d = tempfile.mkdtemp()
|
||||
try:
|
||||
self.assertIsNone(g.stage(d, mutate=True))
|
||||
with open(os.path.join(d, "felhom-bootstrap.sh"), encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
self.assertIn("R-502 planted decoy", text)
|
||||
for _src, rel in g.INPUTS:
|
||||
self.assertTrue(os.path.exists(os.path.join(d, rel)), rel)
|
||||
finally:
|
||||
shutil.rmtree(d, ignore_errors=True)
|
||||
|
||||
def test_registered_full_runs_only(self):
|
||||
# Decision 147: never in --fast (pre-push hook and CI both run --fast; CI has no docker).
|
||||
rows = [r for r in repo_gates.GATES if r[0] == "iso-bootstrap"]
|
||||
self.assertEqual(len(rows), 1, "iso-bootstrap must be registered once in repo_gates.GATES")
|
||||
self.assertTrue(rows[0][1].endswith("iso_bootstrap_gate.py"))
|
||||
self.assertIs(rows[0][3], False, "iso-bootstrap must be fast=False (full runs only)")
|
||||
self.assertIs(rows[0][4], False, "iso-bootstrap must not be exemptible")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
Reference in New Issue
Block a user