#!/usr/bin/env python3 """live262.py — the v0.262.0 scenarios, on scratch guest 9202, through the product's endpoints. A: R-630 — paperless-ngx has a probe that resolves to no container WITHOUT the explicit field, and an explicit one WITH it. Both are exercised: the catalog now carries the field, so the Update must reach `done`; the drill catalog lets the field be removed to show the OTHER half. B: R-633 — a remove sent during a restore is refused; a remove after it is VERIFIED clean. C: R-634 — a half-state (app.yaml, no deployed flag) is removable. F: R-614 — a redeploy reads no stale phase. """ import json, os, sys, time HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, os.path.join(os.path.dirname(HERE), "update-night-2026-09-21")) import walk as w # noqa: E402 OUT = {} def save(tag, rec): OUT[tag] = rec json.dump(OUT, open(os.path.join(HERE, "live262.json"), "w"), ensure_ascii=False, indent=2) print(json.dumps({tag: rec}, ensure_ascii=False, indent=2)[:1400], flush=True) def scenario_A(): """paperless-ngx: the update must now END `done`, where v0.261.0 held it at +313 s.""" app, sub = "paperless-ngx", "paperless" rec = {"scenario": "A (R-630)", "app": app} w.deploy(app, sub) w.wait_app(sub, "/", tries=60) st = w.stack(app) rec["before"] = {"state": st.get("state"), "front_door": w.app_curl(sub, "/")[1], "containers": w.guest("docker ps --format '{{.Names}}|{{.Status}}' | grep -i paperless").strip().split("\n")} # the probe now resolves — the controller's own log says which container t0 = time.time() rec["phases"] = w.press_update(app) rec["wall_s"] = round(time.time() - t0, 1) rec["after"] = {"state": w.stack(app).get("state"), "front_door": w.app_curl(sub, "/")[1]} rec["controller_says"] = w.guest( "docker logs --since 15m felhom-controller 2>&1 | grep -iE 'paperless' | " "grep -iE 'no probe container|settling|phase |health probe|FAILED after' | tail -8").strip() save("A", rec) return app def scenario_F(app): """R-614: remove clears the phase; a redeploy reads none.""" rec = {"scenario": "F (R-614)", "app": app} rec["phase_before_remove"] = w.stack(app).get("update_phase") w.remove(app) time.sleep(5) rec["after_remove_deployed"] = w.stack(app).get("deployed") w.deploy(app, "paperless") time.sleep(5) st = w.stack(app) rec["phase_after_redeploy"] = st.get("update_phase") rec["verdict"] = "clean" if not st.get("update_phase") else "STALE PHASE SURVIVED" save("F", rec) if __name__ == "__main__": w.login() which = sys.argv[1] if len(sys.argv) > 1 else "A" if which == "A": scenario_F(scenario_A())