Skip to content

Overnight swarm runbook — 2026-07-16 (skilled AutoPlayer + balance-signal reporting)

Mission (Chris, 2026-07-16 23:35, live in chat before going to bed): build and run the skilled-AutoPlayer + balance-signal reporting tool end to end overnight — “see how far we can take it.” Unlike prior runs, the work queue is NOT docs/RELEASE-0.1.md (still under feature freeze, untouched by this run) — it is the 5-task implementation plan at docs/superpowers/plans/2026-07-16-skilled-autoplayer-balance-signal.md, backed by the design at docs/superpowers/specs/2026-07-16-skilled-autoplayer-balance-signal-design.md. Read both in full before acting — the design doc’s Guardrails section is binding, not optional.

Power note — REAL RISK, not template boilerplate: at mission start (23:35) the Mac was on BATTERY at 78%, discharging — Chris intended AC but this was not confirmed connected at dispatch time. If it’s not actually plugged in, expect the Monitor’s BATTERY_CRITICAL (≤25%) to fire an early wind-down well before any extended horizon — that is the correct, safe behavior, not a bug. Note the true power state honestly in the morning handoff report.

Permission posture: .claude/settings.local.json was broadened at mission start with wildcard allows for godot --headless *, git commit/push/status/log/diff *, bash scripts/*.sh, pkill/pgrep — the actual commands the bh-dev-chunk ritual and this plan’s steps invoke. If a worker still hits an unanswered permission prompt, treat it as the #1 stall cause (see bh-overnight-swarm §0) — kill and reclaim rather than assuming it’s machine sleep.

  1. git pull --rebase before starting AND before every commit. On .git/index.lock — wait 5s and retry, never delete.
  2. Work off the plan file’s 5 tasks, in dependency order: Task 1 and Task 2 are independent (dispatch both immediately, disjoint files — Task 1 touches sim/sim.gd + tools/soak/soak_stats.gd, Task 2 touches only ai/skilled_auto_player.gd — no overlap). Task 3 depends on BOTH 1 and 2 completing (it touches tools/soak/soak_run.gd + tools/soak/soak_stats.gd — don’t start it until Task 1’s soak_stats.gd changes are merged, to avoid a two-worker collision on that file). Task 4 depends on Task 3. Task 5 depends on Task 4. Do not skip ahead — each task’s Interfaces block in the plan documents exactly what the next task consumes.
  3. Every code task (1-4) runs the full bh-dev-chunk ritual: TDD (test first, watch it fail, implement, watch it pass) → scripts/check-test-count.sh → boot smoke → the plan’s own Step-by-step determinism sanity checks (test_determinism_survival after Tasks 1-3; nothing in this plan should ever require a re-pin — if one seems to, STOP and treat it as a bug, not a re-pin candidate, per the plan’s Global Constraints) → commit → push.
  4. Follow the plan’s code EXACTLY as written — it was hand-verified against the actual current files (field names, method signatures, line anchors) before being committed, not sketched from memory. If something in the plan doesn’t match what you find in the actual file (a field renamed, a line moved), that’s real signal the plan is stale — stop, note the mismatch, adapt correctly, and say so in your report; don’t silently paper over a mismatch.
  5. Concurrency: disjoint file scopes per live worker. Claim protocol: first commit is a short claim note appended to THIS runbook under “Progress log” below (not a separate punch list — this run doesn’t have one), staged by exact pathspec, then push. Serialize commits.
  • NO deploys, NO App Store/TestFlight actions, NO BUILD-number bumps.
  • NO touching platform/tvos/ symlinks.
  • NO work on docs/RELEASE-0.1.md items — this run is scoped entirely to the skilled-AI plan; the v0.1 feature freeze is untouched by this mission (this mission itself qualifies as QA tooling under the freeze, per the design doc — but that’s the ONLY freeze exception, not a general license to also pick up RC items opportunistically).
  • NO re-pinning determinism on this plan — nothing in Tasks 1-4 should move the baseline by construction (see plan Global Constraints). A red determinism test here means investigate, not re-pin.
  • NO merging any Phase 3 guarded auto-tune to main — branch only, always, no exceptions. See the Guardrails section below, copied from the design doc so it’s binding without needing to cross-reference.
  • NO Apple Intelligence / native-bridge work — that idea (see docs/ideas/apple-intelligence-support-pilot.md) is explicitly parked, not part of this run.

Guardrails for Task 5 / Phase 3 (guarded auto-tune) — copied verbatim from the design doc

Section titled “Guardrails for Task 5 / Phase 3 (guarded auto-tune) — copied verbatim from the design doc”

Once Task 5’s large sweep produces fun_signals, auto-tweaks are allowed ONLY when ALL hold:

  1. Single named numeric constant, never structural.
  2. Magnitude-capped to roughly a 15% nudge from its current value.
  3. Triggered only by one of: a damage context at ~zero across (nearly) every run; an enemy type essentially never killed across (nearly) every run; an upgrade’s dud-rate or overpower-rate near-100% across (nearly) every run it appears in.
  4. One tweak per finding, then re-measure (re-run the relevant soak slice) before considering a second tweak to the SAME constant.
  5. Full bh-dev-chunk ritual, plus bh-determinism-repin if the tweak shifts the pinned baseline (routine per that skill, not risky, as long as followed properly).
  6. Branch only, never main. Each tweak its own commit, triggering evidence (numbers) quoted in the commit message. Name the branch soak-auto-tune-2026-07-16.
  7. Everything that doesn’t cleanly meet 1-3 (difficulty curve, build dominance, the anticlimax ratio) stays report-only — a "DECISION (Chris): ..." entry in the handoff report, with the supporting numbers. When in doubt, escalate, don’t guess.

Each tick: pull → check worker states (completion notifications, git log --oneline, git status --short, pgrep -fl godot for orphans) → dispatch the next unclaimed, dependency-satisfied task to a fresh worker (2 concurrent max — this plan’s tasks are mostly sequential, not a wide fan-out like a punch-list burn-down) → append progress to this runbook’s log → push. Never leave a task half-done at tick end — finish it, or note exactly what’s left.

(Workers append one line per claim/completion here, oldest first.)

  • W1 claimed Task 1 (stats hook), 2026-07-16 23:47
  • W2 claimed Task 2 (SkilledAutoPlayer), 2026-07-16 23:58
  • W1 DONE Task 1, commit e7eec0a, tests green (check-test-count.sh: 236/236 scripts, 0 failing), determinism unchanged (test_determinism_survival PASS). Note: the plan’s own test premise (“900 ticks takes SOME damage”) didn’t hold empirically for this fixed movement pattern at seed 1234 — verified via the pre-existing, unrelated Sim.damage_taken_total field that the player takes exactly 0.0 damage at 900 ticks with this drift pattern, first damage lands ~tick 1000, pilot dies ~tick 1100. Adapted the new test to run 1000 ticks locally (not the shared TICKS=900 const, other tests untouched) with a comment explaining why. Hook itself verified correct via direct _hurt_player call + a synthetic on_player_damage check before making this call.
  • W2 DONE Task 2, commit 60ff544, tests green (6/6 in test_skilled_auto_player.gd; check-test-count.sh: 236/236 scripts, 0 failing after re-running clean post-W1’s-push — an earlier run mid-race with W1’s in-flight commit showed 1 transient failure, gone once W1’s commit landed), determinism unchanged (test_determinism_survival PASS). Plan code matched the actual codebase exactly (AutoPlayer consts/methods, sim.enemies.is_elite, enemies.add signature) — no adaptation needed, ai/auto_player.gd left completely untouched. Note: ai/skilled_auto_player.gd’s new class_name wasn’t picked up by GUT until a godot --headless --path . --import pass — same fresh-registration behavior documented elsewhere for worktrees, applies here too even in the main checkout.
  • W3 claimed Task 3 (pluggable player + impact scoring), 2026-07-17 00:12
  • W3 DONE Task 3, commit 6d52449, tests green (15/15 in test_soak_run.gd; check-test-count.sh: 236/236 scripts, 0 failing), determinism unchanged (test_determinism_survival PASS), boot smoke clean (no SCRIPT ERROR). soak_stats.gd’s current shape matched the plan’s Interfaces block for Task 1’s output exactly (damage_taken_by_phase, on_player_damage) — Steps 3-4 in soak_run.gd/soak_stats.gd applied with zero code deviation from the plan. One real empirical deviation, same class as W1’s: the plan’s Step 1 test premise (“1200 ticks -> already leveled up, so upgrade_impacts non-empty”) didn’t hold for SkilledAutoPlayer at seed 1234 — traced directly: first kill lands ~tick 687, each basic kill is worth only 2.0 XP against a 100.0 xp_to_next threshold, so 1200 ticks only reaches 64/100 XP (still level 1, zero upgrades). Bumped the test to 2000 ticks (verified 1800 is the minimal point a level-up + its 180-tick impact window both close; used 2000 for margin) with a comment explaining why — the ring-buffer/impact-scoring CODE itself needed no changes, only the test’s tick budget.
  • W4 claimed Task 4 (soak_runner.gd –player= flag + fun-signal analysis), 2026-07-17 00:35
  • W4 DONE Task 4, commit 89bbd8b, no GUT tests for this file per plan (dev-only CLI, matching tests/test_soak_run.gd’s own header convention) — verification was Step 4’s manual CLI run + Step 5’s count guard, both per the plan. Code matched the plan exactly, zero deviation: --player= flag threaded into SoakRun.new({...})’s cfg dict, _analyze_fun_signals added verbatim after _find_anomalies, wired into both the written JSON (fun_signals top-level key) and a [soak] fun signals: {...} stdout line. Sim.PHASE_BOSS resolved exactly as the plan assumed (enum {PHASE_WAVES, PHASE_WIND_DOWN, PHASE_BOSS_PREP, PHASE_BOSS, PHASE_REST} at sim/sim.gd:21, accessible as a class constant Sim.PHASE_BOSS — no fallback needed). Manual run --runs=2 --ticks=1200 --player=skilled --seed0=1000: exit 0, fun_signals.skilled_runs=2, JSON report written with the key — but upgrade_impacts was empty at 1200 ticks (one run leveled up via split but its 180-tick impact window hadn’t closed yet), consistent with W1/W3’s prior findings that this seed/AI combo needs more ticks. Went further per my own instructions: reran at --runs=3 --ticks=2000 --player=skilled --seed0=1000 and confirmed upgrade_impacts DOES populate and flow all the way through to fun_signals — upgrade_dud_rate_ge_90pct showed {"split":1.0,"weapon:chalice":1.0} (small-sample noise per the plan’s own caveat, but proves the dud/overpowered aggregation pipeline works end-to-end). bash scripts/check-test-count.sh: 236/236 scripts, 0 failing — unchanged from Task 3, exit 0, as expected since this file has no GUT tests. No plan/codebase mismatches found. Task 5 (large 80-run sweep + findings report) is next and unclaimed.
  • W5 claimed Task 5, 2026-07-16 22:51 UTC. Power confirmed AC attached, 80% (not the feared battery-only start). Starting the 80-run sweep now (--runs=40 --ticks=36000 --ships=cobalt,obsidian --coop=solo --player=skilled --seed0=2000) — expect real wall-clock time, will report back when done.
  • W5 DONE Task 5, commit 6897b91, all 5 plan tasks now complete on main. Sweep: 80/80 runs (40 seeds × 2 ships), exit 0, 0 invariant failures, 0 anomalies, wall-clock ~1h5m (22:51→23:56 UTC). fun_signals.skilled_runs == 80 confirmed. No auto-tune candidates met the Guardrails bar — soak-auto-tune-2026-07-16 branch was NOT created, there was nothing to branch: all 7 upgrade ids at ≥90% dud/overpowered rate turned out to be 1-2-occurrence sample-size noise on direct recount (verified against the raw JSON, not just the aggregate), while the actually-frequent upgrades (weapon:chalice/armor at 20 picks, leech at 18, etc.) all sit in the healthy band. Real finding, report-only per Guardrails: 0 of 80 skilled runs ever reached PHASE_BOSS — all died in the wave phase (2731-12070 ticks), which also makes the anticlimax-ratio number in this sweep a divide-by-floor artifact (boss_taken=0 for every run), not usable signal — needs re-measuring once runs survive to boss content. Full findings + a build-diversity caveat + a noted Task-4 coverage gap (no enemy-never-killed signal wired into fun_signals) are in docs/overnight-swarm-report-2026-07-16.md. State of main at handoff: clean tree, check-test-count.sh 236/236 0 failing, boot smoke 0 SCRIPT ERROR, determinism baseline unchanged (test_determinism_survival PASS). Power: stable AC 80% throughout, the runbook’s flagged battery risk did not materialize. Mission complete — all 5 tasks done, no Phase 3 branch to hand off (nothing qualified).

Handoff (morning window, or BATTERY_CRITICAL, or all 5 tasks + Phase 3 done)

Section titled “Handoff (morning window, or BATTERY_CRITICAL, or all 5 tasks + Phase 3 done)”

Write docs/overnight-swarm-report-2026-07-16.md (or -night.md if past midnight when started — match existing convention): which tasks completed (commit SHAs), Task 5’s actual fun_signals findings summarized in plain language, any Phase 3 auto-tune branch + commits (name it explicitly, remind Chris it needs his review/merge — it is NOT on main), DECISION entries for anything that didn’t meet the narrow auto-tune bar, the true power/sleep state honestly, and the measured suite/count-guard/boot-smoke state of main at handoff. Commit + push the report, TaskStop the Monitor, summarize for Chris with the decision queue up top.