night 2026-09-24: chaos rounds 1-8 evidence; 07/08/09/capability map for decisions 26-28 + digests; R-681
gates / gates (push) Successful in 24s
gates / gates (push) Successful in 24s
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:
@@ -103,7 +103,7 @@ def readback(app, s):
|
||||
"files": nf.verify_files(w, SUB[app], A, {names[0]: S["files"][names[0]], names[2]: S["files"][names[2]]}, w.say)}
|
||||
if not A:
|
||||
return {"A": None, "note": "no seed recorded"}
|
||||
w.wait_app(SUB[app], "/", tries=90, delay=4)
|
||||
w.wait_app(SUB[app], "/", want=("200", "302", "303", "307", "401", "403"), tries=90, delay=4)
|
||||
return {"A": bool(FX[app].verify(w, SUB[app], A, w.say))}
|
||||
|
||||
|
||||
@@ -185,6 +185,11 @@ class Arm:
|
||||
self.note["accident"] = f"POST /api/backup/run -> {code} {str(d)[:160]} at {time.strftime('%H:%M:%S')}"
|
||||
w.say(" >>> " + self.note["accident"])
|
||||
return
|
||||
# R-320: the box's log off the machine BEFORE the accident — round 3's power cut replaced the controller
|
||||
# container and took the pre-cut lines with it. Only from the main thread: walk.guest shares one temp file.
|
||||
if threading.current_thread() is threading.main_thread():
|
||||
open(os.path.join(EVD, f"round-{self.n:02d}-controller-pre.log"), "w").write(
|
||||
w.guest(f"docker logs --since {self.note['started']} felhom-controller 2>&1 | grep -vE 'auth: valid session|router.go:81|Status refresh' | tail -3000"))
|
||||
json.dump({"round": self.n, "kind": self.kind, "at": time.time(), "why": why},
|
||||
open(os.path.join(EVD, f"arm-{self.n:02d}.json"), "w"))
|
||||
|
||||
@@ -216,8 +221,14 @@ def wait_controller(cap=900):
|
||||
# ------------------------------------------------------------------------------------------ actions
|
||||
def a_unhealthy_install(app, note, arm, t0):
|
||||
"""crash_loop / oom_app: install the drill app through the product; the box must stop it by itself."""
|
||||
for _ in range(12): # the drill template must be on the box first (attempt 0 of round 1: "not found")
|
||||
w.sync_rescan()
|
||||
if w.ctl("GET", f"/api/stacks/{app}/deploy-fields")[0] == "200":
|
||||
break
|
||||
time.sleep(5)
|
||||
vals = w.deploy_values(app, app)
|
||||
code, d = w.ctl("POST", f"/api/stacks/{app}/deploy", {"values": vals})
|
||||
w.say(f" deploy -> {code} {str(d)[:120]}")
|
||||
note["act"] = f"deploy -> {code}"
|
||||
threading.Timer(20, arm, args=("20 s after the install",)).start()
|
||||
return steady(app, t0, cap=1500, until=lambda st: st.get("hold_kind") == "unhealthy_stop")
|
||||
@@ -262,7 +273,7 @@ def a_install(app, note, arm, t0, s):
|
||||
return secs, st
|
||||
|
||||
|
||||
def a_update(app, note, arm, t0, on_verify=None):
|
||||
def a_update(app, note, arm, t0, on_verify=None, arm_at_starting=False):
|
||||
code, d = w.ctl("POST", f"/api/stacks/{app}/update")
|
||||
note["act"] = f"Update -> {code} {str(d)[:140]}"
|
||||
w.say(" " + note["act"])
|
||||
@@ -281,7 +292,7 @@ def a_update(app, note, arm, t0, on_verify=None):
|
||||
if on_verify:
|
||||
on_verify()
|
||||
arm("at verifying")
|
||||
if ph == "starting":
|
||||
if ph == "starting" and arm_at_starting:
|
||||
arm("at starting")
|
||||
if st and not st.get("updating") and ph in ("done", "failed", "undone") and time.time() - t0 > 3:
|
||||
break
|
||||
@@ -352,7 +363,7 @@ def a_ladder_step(app, note, arm, t0, s):
|
||||
st = w.stack(app)
|
||||
note["steps_left_before"] = st.get("ladder_steps_left")
|
||||
note["pinned_before"] = (st.get("app_config") or {}).get("pinned_images")
|
||||
r = a_update(app, note, arm, t0)
|
||||
r = a_update(app, note, arm, t0, arm_at_starting=True)
|
||||
w.sync_rescan()
|
||||
st = w.stack(app)
|
||||
note["steps_left_after"] = st.get("ladder_steps_left")
|
||||
@@ -443,8 +454,29 @@ def run_round(n):
|
||||
w.say(f"==== ROUND {n} END: steady in {note['time_to_steady_s']}s; readback={note['readback']}; events={note['events']}")
|
||||
|
||||
|
||||
def collect(n, t_iso, extra):
|
||||
"""Finish a round whose runner was stopped: the same five observations, no action."""
|
||||
s = load()
|
||||
sched = {r["round"]: r for r in schedule()}[n]
|
||||
app = TARGET[n]
|
||||
w.login()
|
||||
note = {"round": n, **sched, "app": app, "started": t_iso, "collected_by_hand": True, **extra}
|
||||
note["after"] = household(app)
|
||||
if not w.stack(app).get("deployed"):
|
||||
note["after"]["leftovers"] = w.guest(f"docker ps -a --filter label=com.docker.compose.project={app} --format '{{{{.Names}}}} {{{{.Status}}}}'; docker volume ls -q | grep '^{app}_' || true; ls /opt/docker/stacks/{app}/ 2>/dev/null || true").strip()
|
||||
note["readback"] = readback(app, s)
|
||||
note["events"] = events_since(t_iso)
|
||||
note["box_log"] = box_log(t_iso, app)
|
||||
open(os.path.join(EVD, f"round-{n:02d}-controller.log"), "w").write(
|
||||
w.guest(f"docker logs --since {t_iso} felhom-controller 2>&1 | grep -vE 'auth: valid session|router.go:81|Status refresh' | tail -3000"))
|
||||
json.dump(note, open(os.path.join(EVD, f"round-{n:02d}.json"), "w"), indent=2, ensure_ascii=False, default=str)
|
||||
w.say(f"==== ROUND {n} COLLECTED: after={json.dumps(note['after'], ensure_ascii=False)[:400]} events={note['events']}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if sys.argv[1] == "setup":
|
||||
if sys.argv[1] == "collect":
|
||||
collect(int(sys.argv[2]), sys.argv[3], json.loads(sys.argv[4]) if len(sys.argv) > 4 else {})
|
||||
elif sys.argv[1] == "setup":
|
||||
setup()
|
||||
elif sys.argv[1] == "round":
|
||||
run_round(int(sys.argv[2]))
|
||||
|
||||
Reference in New Issue
Block a user