Files
felhom.eu/documentation/audits/r890-instructions-2026-10-06/tools/vwstep.py
T
admin a29ee9dd33
gates / gates (push) Successful in 2m38s
Decisions 149-150 recorded; instruction files kept true; R-890, R-891, R-644 closed, R-469 narrowed (142 -> 139)
Decision 150 (operator 13:25): sessions correct stale facts in instruction files themselves,
naming each edit; never loosen a rule. Added to unprompted-work.md §5 (all copies) and
PROMPT-TEMPLATE.md §9. CLAUDE.md gates paragraph (R-891), architecture pointer, docs/website
rules, the doubled R-286 sentence in the workspace CLAUDE.md.
Decision 149: vaultwarden's step proven on bench 9401 and box 9202 (evidence here).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-06 13:52:28 +02:00

77 lines
4.4 KiB
Python

"""vwstep.py <to-ref> — R-890: vaultwarden's step on scratch 9202 (drill catalog), ONE process, through the product.
1 install vaultwarden fresh at the live pin (this run installs it — the admin seed refuses otherwise);
2 seed through the household's door, the invite through the admin page INSIDE the box
(FELHOM_BOX_ADMIN_SEED=1, upgrade_fixtures_box.box_admin_seed_allowed); read it back (C1);
3 a DRILL-only commit moves the image and adds a ladder entry; sync, rescan;
4 the product's guarded Update; the seed read back; box verdict JSON;
5 remove through the product.
Evidence: ../box/vaultwarden/step.txt + box-verdict-vaultwarden.json. The live entry is written ONLY by
`upgrade-test.py --write-ladder` from both verdicts."""
import json, os, re, subprocess, sys, time
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
import box_walk as w
import upgrade_fixtures_box as fixtures
APP, SUB, SVC = "vaultwarden", "vault", "vaultwarden"
to = sys.argv[1]
HERE = os.path.dirname(os.path.abspath(__file__))
EVD = os.path.join(HERE, "..", "box", APP); os.makedirs(EVD, exist_ok=True)
log = open(f"{EVD}/step.txt", "a", buffering=1)
D = "/mnt/5_hdd/felhom.eu/drill/app-catalog-drill"
def say(*a):
w.say(*a); log.write(" ".join(map(str, a)) + "\n")
fx = fixtures.FIXTURES[APP]
w.login()
if w.stack(APP).get("deployed"):
say("vaultwarden already installed on 9202 — removing it first (scratch box)")
w.remove(APP)
w.sync_rescan()
if not w.deploy(APP, SUB):
sys.exit(say("RESULT the install did not complete") or 1)
tok = fx.seed(w, SUB, say)
if tok is None or not fx.verify(w, SUB, tok, say):
say(f"RESULT C1 failed: {getattr(fx, 'tried', '')}")
w.remove(APP); sys.exit(1)
say("C1 seed reads back BEFORE: True")
before = (w.stack(APP).get("app_config") or {}).get("pinned_images")
say(f"before: pinned={before}")
subprocess.run(["git", "-C", D, "pull", "-q", "--rebase", "origin", "main"], check=True)
comp, fy = f"{D}/templates/{APP}/docker-compose.yml", f"{D}/templates/{APP}/.felhom.yml"
s = open(comp).read()
frm = re.search(r"^\s+image:\s*(\S+)", s, re.M).group(1)
open(comp, "w").write(s.replace("image: " + frm, "image: " + to, 1))
entry = {"from": {SVC: frm}, "to": {SVC: to}, "verdict": "proven", "tested_at": "DRILL", "harness_version": 5,
"evidence": "DRILL (box proof in progress)", "marks": {"files_may_change": False, "needs_person": None, "memory_tight": False}}
f = open(fy).read()
f = (f.rstrip("\n") + "\n - " + json.dumps(entry) + "\n") if "update_ladder:" in f else (f.rstrip("\n") + "\nupdate_ladder:\n - " + json.dumps(entry) + "\n")
open(fy, "w").write(f)
subprocess.run(["git", "-C", D, "commit", "-q", "-am", f"DRILL {APP}: {frm} -> {to} (box proof, R-890)"], check=True)
subprocess.run(["git", "-C", D, "push", "-q", "origin", "main"], check=True, capture_output=True)
say("drill:", subprocess.run(["git", "-C", D, "log", "--oneline", "-1"], capture_output=True, text=True).stdout.strip())
w.sync_rescan(APP, to)
say(f"badge before: {w.badges(APP)}")
since = w.guest("date -u +%Y-%m-%dT%H:%M:%SZ").strip()
res = w.press_update(APP, poll=1, cap_s=1800)
for p in res.get("phases", []):
log.write(f" phase +{p['t']}s {p['phase']} | err={p['error']}\n")
time.sleep(10)
read = fx.verify(w, SUB, tok, say)
lines = w.guest(f"docker logs --since {since} felhom-controller 2>&1 | grep -E 'update {APP}' | grep -v DEBUG | cut -c1-400")
log.write(lines + "\n")
st = w.stack(APP); after = (st.get("app_config") or {}).get("pinned_images")
verdict = {"app": APP, "venue": "box 9202 (drill catalog), the product's guarded Update; seeded through the admin invite inside the box (R-890)",
"from": before, "to": after,
"verdict": "proven" if (res.get("final_phase") == "done" and read and (after or {}).get(SVC) == to) else "failed",
"seed_read_before": True, "seed_read_after": read, "healthy_after": st.get("state") == "running",
"duration_s": res.get("duration_s"), "final_phase": res.get("final_phase"), "measured_at": since,
"evidence": "felhom.eu/documentation/audits/r890-instructions-2026-10-06/box/vaultwarden/step.txt"}
json.dump(verdict, open(f"{EVD}/box-verdict-{APP}.json", "w"), indent=2)
say(f"badge after: {w.badges(APP)}")
say(f"RESULT final_phase={res.get('final_phase')} after={after} seed_after={read} verdict={verdict['verdict']} ({res.get('duration_s')} s)")
say(f"remove -> {w.remove(APP)}")