Files
felhom.eu/documentation/audits/pg-last-six-2026-09-30/tools/boxmove.py
T
admin 25cb3eb9c1
gates / gates (push) Successful in 29s
The last six PostgreSQL apps decided; the gate's wait seen live by day; catalog currency; stale rows
- R-463 CLOSED: rallly, outline, sparkyfitness -> PostgreSQL 18 (catalog 25ffd89 / aeb0cd6 / 1666572),
  each proven on the bench and on 9202 through the guarded Update with one undo case; zipline,
  adventurelog, immich stay on 16 by 09 decision 42's rule (upstream runs 16 / 16 / 14).
- Part F: bookstack, kimai, audiobookshelf, n8n, navidrome, grafana, komga moved on both venues;
  immich not (R-732: its first start OOM-killed its database on the bench).
- R-687 item 4 PROVEN LIVE on demo-hp: two deferrals while the leg stepped two apps, the whole-guest
  backup on the first poll after, success; config + window put back and read back.
- Catalog currency audit (Part D): 25/53 behind inside a major, 19 across; night-updatable 28 -> 31 (+1).
- R-446 and R-440 narrowed (measured on demo-hp); R-624 corrected (outline, rallly, zipline have routes);
  R-548 note (demo-hp's local tier refused for space since 09-27).
- New rows: R-730 (the ISO 1.29.0 build commit cannot be proven -> no tag; installer-v* is the script's
  line), R-731 (tag-shape switches), R-732. Register 361 -> 364.
- 09 §3: the 2026-09-30 operator notes (by day; Tester-2 pre-checks done; decision 52 not needed, not
  recorded); §6.4 dated currency note. STATUS, CONTEXT, REPORT-pg-last-six-2026-09-30.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-09-30 14:47:12 +02:00

59 lines
3.9 KiB
Python

"""boxmove.py <phase> <app> <sub> [<svc> <from> <to> <fmj> <tmj>] — Part B's BOX venue on 9202 (drill catalog):
prep: deploy (reuse if installed), seed through the app's own route, read it back (C1)
move: drill commit with the mark → sync → the guarded Update → engine state + seed read back → box verdict JSON
Evidence: B/apps/<app>/box/."""
import json, os, sys, time, subprocess
import walk as w
sys.path.insert(0, '/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts')
import upgrade_fixtures_box as fixtures, upgrade_fixtures_box28 as _f28
FX = dict(_f28.FIXTURES28); FX.update(fixtures.FIXTURES) # the box set wins, as upgrade_boxport.get() decides
phase, app, sub = sys.argv[1:4]
EVD = f"{w.EV}/box/{app}"; os.makedirs(EVD, exist_ok=True)
TOK = w.SC + "/seed-tokens-box.json"
log = open(f"{EVD}/{phase}.txt", "a", buffering=1)
def say(*a):
w.say(*a); log.write(" ".join(map(str, a)) + "\n")
w.login()
toks = json.load(open(TOK)) if os.path.exists(TOK) else {}
if phase == "prep":
say(f"deploy/reuse {app}: {w.deploy(app, sub)}")
toks[app] = FX[app].seed(w, sub, say)
if isinstance(toks[app], dict) and w.GENERATED.get(app):
# the deploy secrets THIS run generated (grafana's admin password): kept beside the seed, in the 0600 scratch
# file only, so a LATER process (boxstep/undocase) can read the app back. Found 2026-09-30: grafana's box step
# read back with the default password and got 401 — the instrument, not the product.
toks[app]["__generated"] = w.GENERATED[app]
json.dump(toks, open(TOK, "w"), default=str); os.chmod(TOK, 0o600)
say(f"C1 seed reads back BEFORE: {FX[app].verify(w, sub, toks[app], say)}")
else:
svc, frm, to, fmj, tmj = sys.argv[4:9]
st = w.stack(app); before = (st.get("app_config") or {}).get("pinned_images")
pg = lambda: w.guest(f"docker exec {svc} sh -c 'cat $PGDATA/PG_VERSION' 2>&1").strip()
say(f"before: pinned={before} PG_VERSION={pg()}")
if os.environ.get("SKIPDRILL"):
say("drill: SKIPPED — the drill commit was made by an earlier attempt")
else:
r = subprocess.run([sys.executable, os.path.join(os.path.dirname(__file__), "drillmove.py"), app, svc, frm, to, fmj, tmj], capture_output=True, text=True)
say("drill:", r.stdout.strip().splitlines()[0] if r.stdout else r.stderr[-300:])
w.sync_rescan(app, to)
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)
after_pg = pg(); read = FX[app].verify(w, sub, toks[app], say)
conv = w.guest(f"docker logs --since {since} felhom-controller 2>&1 | grep -E 'update {app}|CONVERT' | grep -v DEBUG | cut -c1-400")
log.write(conv + "\n")
line = next((l for l in conv.splitlines() if "CONVERTED" in l), "")
st = w.stack(app)
verdict = {"app": app, "venue": "box 9202 (drill catalog, controller 0.283.1), the product's guarded Update",
"from": before, "to": (st.get("app_config") or {}).get("pinned_images"),
"verdict": "proven" if (res.get("final_phase") == "done" and read and after_pg == str(tmj)) else "failed",
"seed_read_before": True, "seed_read_after": read, "healthy_after": st.get("state") == "running",
"engine_conversion": {"service": svc, "engine": "postgres", "from": int(fmj), "to": int(tmj),
"result": "converted" if line else "not-seen", "controller_line": line.split("] ", 2)[-1] if line else ""},
"duration_s": res.get("duration_s"), "measured_at": since,
"evidence": f"felhom.eu/documentation/audits/pg-last-six-2026-09-30/box/{app}/move.txt"}
json.dump(verdict, open(f"{EVD}/box-verdict-{app}.json", "w"), indent=2)
say(f"RESULT final_phase={res.get('final_phase')} PG_VERSION={after_pg} seed_after={read} verdict={verdict['verdict']}")