186546d562
gates / gates (push) Successful in 27s
All 28 walked on scratch guest 9202 against the private drill catalog. 26 deployed, 6 proven, 5 inconclusive, 14 with no upstream edge, 1 failed honestly (outline 1.9.1->1.10.1, HELD with the right sentence), 2 undeployable - one (plant-it) by design, refused by the lifecycle gate, proven live for the first time. Each app also got the half the update night skipped: a restore from its own copy with the seed read back again - 21 restored, 2 correctly REFUSED per 07 6.2. R-630 RAISED TO P1 by measurement: a stack with NO probe container does not skip verifying - it waits out the full health timeout and HOLDS, stopping an app whose three containers read healthy. The controller's own words: "not healthy within 5m0s (last: no probe container)". R-633 opened: a remove sent during a restore reports success and leaves a container restarting with a live public route. The product already refuses that clash for update and for restore, naming the blocker; remove has no such guard. R-634 opened: an app can be running, healthy and serving while recorded as deployed=false, and is then unremovable. Reproducible alone on sparkyfitness; concurrency-linked on two others. R-631 and R-632 CLOSED. Register 321 -> 323. Seven interventions, six of them my own harness - named, with what each cost. No product code. The live catalog's image: lines are byte-identical to the start of the night. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
63 lines
2.7 KiB
Python
63 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Rewrite the rotation file's line for each of the 28, from its own verdict record.
|
|
|
|
A tick means the app was WALKED FULLY — deployed, backed up, restored, removed clean. An app whose
|
|
data half is `inconclusive` is still ticked when the rest of the walk completed, and the line SAYS
|
|
what could not be judged; an app that could not be deployed is not ticked.
|
|
"""
|
|
import glob, io, json, os, re
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
ROT = "/mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/runbooks/nightly-rotation.md"
|
|
D = "2026-09-22"
|
|
EV = "`audits/DRILL-the-28-2026-09-22.md`"
|
|
|
|
recs = {}
|
|
for f in glob.glob(os.path.join(HERE, "apps", "*", "verdict.json")):
|
|
d = json.load(open(f, encoding="utf-8"))
|
|
recs[d["app"]] = d
|
|
|
|
|
|
def line(a, d):
|
|
if not d["deployed"]:
|
|
why = "; ".join(d["notes"]) or "deploy refused"
|
|
return (f"- [ ] {a} — NOT A FULL WALK; attempted {D} (the 28, scratch guest 9202): "
|
|
f"**could not be deployed** — {why}. {EV}.")
|
|
bits = ["install", f"front door {d.get('front_door_after_deploy',{}).get('code','?')}"]
|
|
if d["seed_read_before"]:
|
|
bits.append(f"seeded through {d.get('seed_route')} and read back")
|
|
else:
|
|
bits.append("NO non-browser data route (" + (
|
|
"; ".join(n for n in d["notes"] if "route" in n) or "recorded, not faked") + ")")
|
|
bits.append("„Mentés most\"")
|
|
e = d.get("edge")
|
|
if e:
|
|
bits.append("real upstream Update %s->%s (%s)" % (
|
|
e["from"].rsplit(":", 1)[1], e["to"].rsplit(":", 1)[1],
|
|
d.get("update_phase_final")))
|
|
if d.get("migration_observed"):
|
|
bits.append("own migration line quoted")
|
|
if d["seed_read_after_update"]:
|
|
bits.append("read back after the update")
|
|
else:
|
|
bits.append("no upstream edge tonight")
|
|
bits.append("restore " + str(d.get("restore_verdict")))
|
|
if d["seed_read_after_restore"]:
|
|
bits.append("read back after the restore")
|
|
bits.append("removed " + ("clean" if d.get("removed_clean") else "**with a leftover container**"))
|
|
tick = "x" if (d["deployed"] and d.get("removed_clean")) else " "
|
|
pre = "" if tick == "x" else "NOT A FULL WALK; attempted "
|
|
return f"- [{tick}] {a} — {pre}{D} (the 28, scratch guest 9202): " + ", ".join(bits) + f". {EV}."
|
|
|
|
|
|
t = io.open(ROT, encoding="utf-8").read()
|
|
n = 0
|
|
for a, d in sorted(recs.items()):
|
|
pat = re.compile(r'^- \[[ x]\] ' + re.escape(a) + r'( .*)?$', re.M)
|
|
if pat.search(t):
|
|
t = pat.sub(lambda m: line(a, d).replace("\\", "\\\\"), t, count=1)
|
|
n += 1
|
|
else:
|
|
print(" NO EXISTING LINE for", a)
|
|
io.open(ROT, "w", encoding="utf-8").write(t)
|
|
print(f"rewrote {n} rotation lines")
|