#!/usr/bin/env python3 """chaos_schedule.py — Part D's twelve rounds, drawn ONCE from seed 20260923 and committed before round 1. python3 chaos_schedule.py # prints the table the findings doc carries Each round is ACTION x ACCIDENT, the accident landing DELAY seconds into the action. actions : good_update · fail_health (wrong probe -> the undo) · cutoff (the undo's copy loses its finished-marker -> an honest HOLD) · install · remove · restore accidents : none · power_cut (pct stop/start) · controller_kill (kill -9, max 3, >= 20 min apart) · docker_restart · disk_fill (to 1 GB above the update floor) · memory_hog (1 GB, 5 min) · box_backup (the whole-box backup, on 9202 only) · two_updates (a second Update pressed at the same moment) The draw is constrained, and the constraints are the brief's, not tuning: rounds 1 and 12 are `none`; every action appears twice; every accident appears at least once; `controller_kill` at most three times and never in consecutive-or-adjacent rounds (a round lasts ~6-8 min, so two rounds between kills keeps them >= 20 min apart). A draw that breaks a constraint is redrawn from the SAME generator, so the schedule is a pure function of the seed. WHICH APP a round acts on is NOT drawn: it is fixed by rule in chaos.py (the action decides the app class) and printed beside the schedule, because an app's state after round N decides what round N+1 can do to it. """ import random SEED = 20260923 ACTIONS = ["good_update", "fail_health", "cutoff", "install", "remove", "restore"] ACCIDENTS = ["power_cut", "controller_kill", "docker_restart", "disk_fill", "memory_hog", "box_backup", "two_updates"] def draw(): rng = random.Random(SEED) tries = 0 while True: tries += 1 acts = ACTIONS * 2 rng.shuffle(acts) mid = ACCIDENTS + [rng.choice(ACCIDENTS) for _ in range(3)] rng.shuffle(mid) accs = ["none"] + mid + ["none"] kills = [i for i, a in enumerate(accs) if a == "controller_kill"] if len(kills) > 3 or any(b - a < 3 for a, b in zip(kills, kills[1:])): continue # an install/remove round cannot also be `two_updates` (there is no second update to race) if any(a in ("install", "remove") and c == "two_updates" for a, c in zip(acts, accs)): continue delays = [rng.randint(10, 60) for _ in range(12)] return [{"round": i + 1, "action": acts[i], "accident": accs[i], "delay_s": delays[i]} for i in range(12)], tries if __name__ == "__main__": rounds, tries = draw() print(f"seed {SEED}, drawn in {tries} attempt(s)\n") print("| round | action | accident | at +s |") print("|---|---|---|---|") for r in rounds: print(f"| {r['round']} | {r['action']} | {r['accident']} | {r['delay_s']} |")