1cedf94d6b
gates / gates (push) Successful in 29s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
66 lines
2.8 KiB
Python
66 lines
2.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Part E's schedule — night 2026-09-24. Deterministic: seed 20260924, 12 rounds of action x accident.
|
|
|
|
Committed BEFORE round 1 and printed into DRILL-night-2026-09-24.md, so the schedule cannot be chosen after
|
|
the fact. Constraints from the brief: every action at least once; controller killed at most 3 times, at
|
|
least 20 minutes apart (rounds are ~6 min, so kills sit >= 4 rounds apart); a whole-box backup only on 9202.
|
|
|
|
python3 chaos_schedule.py # prints the table
|
|
python3 chaos_schedule.py --json # for the runner
|
|
"""
|
|
import json
|
|
import random
|
|
import sys
|
|
|
|
SEED = 20260924
|
|
ROUNDS = 12
|
|
ROUND_MIN = 6 # planned length of one round, minutes
|
|
|
|
ACTIONS = [
|
|
"ladder_step", # a proven ladder step (Update)
|
|
"failing_step", # a step whose probe fails -> undo
|
|
"cutoff_undo_file_app", # a file app's undo copy cut off -> the second-drive whole restore
|
|
"crash_loop", # a drill image that exits -> the box stops it
|
|
"oom_app", # a tiny mem_limit -> out-of-memory kills
|
|
"install",
|
|
"remove_held", # Remove on a held app
|
|
"start_after_unhealthy",# Start after the box stopped an app
|
|
]
|
|
ACCIDENTS = ["none", "power_cut", "controller_killed", "docker_restart", "disk_to_floor_plus_1g",
|
|
"whole_box_backup"]
|
|
MAX_KILLS, KILL_GAP_MIN = 3, 20
|
|
|
|
|
|
def schedule(seed=SEED):
|
|
rng = random.Random(seed)
|
|
for _ in range(10000):
|
|
acts = ACTIONS + [rng.choice(ACTIONS) for _ in range(ROUNDS - len(ACTIONS))]
|
|
rng.shuffle(acts)
|
|
accs = [rng.choice(ACCIDENTS) for _ in range(ROUNDS)]
|
|
kills = [i for i, a in enumerate(accs) if a == "controller_killed"]
|
|
if len(kills) > MAX_KILLS:
|
|
continue
|
|
if any((b - a) * ROUND_MIN < KILL_GAP_MIN for a, b in zip(kills, kills[1:])):
|
|
continue
|
|
# Start-after-unhealthy needs an app the box stopped: never before the first crash_loop/oom_app.
|
|
first_stop = min(i for i, a in enumerate(acts) if a in ("crash_loop", "oom_app"))
|
|
if acts.index("start_after_unhealthy") < first_stop:
|
|
continue
|
|
# Remove-on-held needs a held app: after a failing_step or cutoff.
|
|
first_hold = min(i for i, a in enumerate(acts) if a in ("failing_step", "cutoff_undo_file_app"))
|
|
if acts.index("remove_held") < first_hold:
|
|
continue
|
|
return [{"round": i + 1, "action": acts[i], "accident": accs[i]} for i in range(ROUNDS)]
|
|
raise SystemExit("no schedule satisfies the constraints")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
s = schedule()
|
|
if "--json" in sys.argv:
|
|
print(json.dumps(s, indent=1))
|
|
else:
|
|
print(f"seed {SEED}, {ROUNDS} rounds")
|
|
print("| round | action | accident |\n|---|---|---|")
|
|
for r in s:
|
|
print(f"| {r['round']} | {r['action']} | {r['accident']} |")
|