Co-op Determinism Coverage Implementation Plan
Co-op Determinism Coverage Implementation Plan
Section titled “Co-op Determinism Coverage Implementation Plan”For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Fold the drones pool into the determinism checksums (baseline-safe) and add a 2-pilot determinism test, so the co-op sim (2 pilots + a per-pilot deployed drone) is provably byte-identical across same-seed runs.
Architecture: state_checksum()/snapshot_string() already loop over all pilots but omit the drones pool. Append a for d in drones: loop to both (empty in the existing single-pilot run → byte-identical → no re-pin of the existing baseline), deliberately excluding the accruing per-pilot drone_charge scalar. Then add a new test_determinism_coop.gd that deploys a drone for P2 and pins a new, separate 2-pilot baseline.
Tech Stack: Godot 4.6.3, typed GDScript, GUT 9.6.0 (headless).
Global Constraints
Section titled “Global Constraints”/sim-internal change only.sim/sim.gd(the two checksum functions) +sim/drone_director.gd(one comment) + a new test file. No render/UI/Node code.- The existing single-pilot baseline in
tests/test_determinism_survival.gd(state_checksum()==3312602286,snapshot_string().hash()==4217109746) MUST remain unchanged — no re-pin. This is the explicit proof the enrichment is baseline-safe (the existing run deploys no drones → empty loop → byte-identical). - Exclude the per-pilot
drone_chargescalar from the checksum. It accrues every tick (drone_director.gd:102), so including it would change the existing baseline. Its only observable effect (deploy timing) is already caught by the drones pool. - Include the DroneState pool fields, and
pilots.find(d.owner)as the owner index. snapshot_string()must NOT add a drone-count to the aggregate line (that changes the single-pilot format); only append per-drone segments in a loop (empty → no change).- Spec:
docs/superpowers/specs/2026-07-14-coop-determinism-coverage-design.md. - Test verification note: GUT’s headless run SIGABRTs (exit 134) at teardown even on green — judge from the printed “Run Summary”/“All tests passed!”, not the exit code. Narrow to one script with
-gselect=<script_name_without_.gd>.godotis at/opt/homebrew/bin/godot. - 2-pilot tick API:
sim.tick(inputs: Array[InputState])(e.g.sim.tick([inp1, inp2])).add_player2()appendspilots[1].sim.drone_director.deploy_drones(sim, pilot, [spec])deploys a drone owned bypilot.
File Structure
Section titled “File Structure”sim/sim.gd—state_checksum()(~line 2564) andsnapshot_string()(~line 2590): append the drones-pool loop to each.sim/drone_director.gd:508— update the stale “never affects determinism” comment.tests/test_determinism_coop.gd— NEW: the 2-pilot determinism test + pinned baseline.tests/test_determinism_survival.gd— UNCHANGED (regression proof it still passes).
Two tasks. Task 1 enriches the checksums (baseline-safe) with a red-first test proving drone state is now counted, plus the regression proof. Task 2 adds the 2-pilot test with a captured baseline; it depends on Task 1 (the deployed-drone coverage is only meaningful once drones are in the checksum).
Task 1: Fold the drones pool into state_checksum() and snapshot_string()
Section titled “Task 1: Fold the drones pool into state_checksum() and snapshot_string()”Files:
- Modify:
sim/sim.gd—state_checksum()(~2564),snapshot_string()(~2590). - Modify:
sim/drone_director.gd:508(comment). - Test:
tests/test_determinism_coop.gd(create with the Task-1 red test; Task 2 adds more to the same file).
Interfaces:
- Consumes:
drones: Array[DroneState](field on Sim);DroneStatefieldspos, vel, hp, life, charge, pulse_timer, phase, owner;pilots: Array[PlayerState];sim.drone_director.deploy_drones(sim, pilot, loadout). - Produces:
state_checksum()andsnapshot_string()that include drone-pool state.
Context: Both functions already loop for pilot in pilots (multi-pilot player state is covered). The gap is the drones pool. The existing single-pilot determinism run deploys no drones, so appending an empty-when-no-drones loop keeps it byte-identical.
- Step 1: Write the failing test proving drone state is now in the checksum
Create tests/test_determinism_coop.gd:
extends GutTest
# Co-op determinism coverage (2026-07-14). Task 1 guard: two sims identical except for a# deployed drone's position must now produce DIFFERENT state_checksum()/snapshot_string() —# i.e. the drones pool is actually included. Before the enrichment, drones were ignored, so# the two checksums were equal and this test failed.
const DRONE_SPEC := {"klass": "sentinel", "life": 1.0, "dmg": 1.0, "radius": 1.0, "heal": 0.0, "extras": 0}
func test_drone_pool_state_is_included_in_the_checksums() -> void: var a := Sim.new(1234, SimContentFixture.db()) a.enable_survival() var b := Sim.new(1234, SimContentFixture.db()) b.enable_survival() # Both deploy one drone (owned by the sole pilot); then move B's drone so the ONLY # difference between the two sims is drone position. a.drone_director.deploy_drones(a, a.player, [DRONE_SPEC]) b.drone_director.deploy_drones(b, b.player, [DRONE_SPEC]) assert_eq(a.drones.size(), 1, "precondition: sim A has one drone") assert_eq(b.drones.size(), 1, "precondition: sim B has one drone") b.drones[0].pos += Vector2(50.0, 0.0) # the only difference assert_ne(a.state_checksum(), b.state_checksum(), "drone pool position must affect state_checksum()") assert_ne(a.snapshot_string(), b.snapshot_string(), "drone pool position must affect snapshot_string()")- Step 2: Run it to verify it fails
Run: godot --headless --path . -s res://addons/gut/gut_cmdln.gd -gselect=test_determinism_coop -gexit 2>&1 | grep -iE "Passing|Failing|\[Failed\]"
Expected: FAILS — before the enrichment, drones are ignored, so a.state_checksum() == b.state_checksum() (and snapshots equal), so the assert_nes fail.
- Step 3: Append the drones pool to
state_checksum()
In sim/sim.gd, state_checksum(), immediately BEFORE return hash(parts):
for d in drones: parts.append(d.pos) parts.append(d.vel) parts.append(d.hp) parts.append(d.life) parts.append(d.charge) parts.append(d.pulse_timer) parts.append(d.phase) parts.append(pilots.find(d.owner)) # owner as a stable pilot index (co-op)Do NOT append the per-pilot drone_charge scalar (it accrues every tick and would change the existing baseline; its effects surface via the pool).
- Step 4: Append the drones pool to
snapshot_string()
In sim/sim.gd, snapshot_string(), after the per-pilot xp=/lv= loop and BEFORE return s:
for d in drones: s += " d=(%.3f,%.3f)" % [d.pos.x, d.pos.y]Do NOT add a drone-count field to the aggregate e=… a=… pr=… g=… k=… line — that would change the single-pilot format and break byte-identical output.
- Step 5: Update the stale comment in
sim/drone_director.gd:508
Find the comment (near func decoy_positions() / the decoy-charge query returning {"active":…, "charge": sim.drone_charge, …}) that reads roughly:
# NOT in snapshot_string()/state_checksum() (drones are opt-in, so this never affects determinism).
Replace it with:
# Deployed drones (the sim.drones pool) ARE now in state_checksum()/snapshot_string()# (2026-07-14) — they affect determinism once fielded. Only the per-pilot drone_charge# recharge scalar stays excluded (it accrues every tick); its sole observable effect is# deploy timing, which surfaces through the pool.(Match the exact surrounding wording in the file; keep it a comment.)
- Step 6: Run the coop test — the red test now passes
Run: godot --headless --path . -s res://addons/gut/gut_cmdln.gd -gselect=test_determinism_coop -gexit 2>&1 | grep -iE "Passing|Failing|All tests passed|\[Failed\]"
Expected: test_drone_pool_state_is_included_in_the_checksums PASSES.
- Step 7: Prove the existing single-pilot baseline is UNCHANGED (no re-pin)
Run: godot --headless --path . -s res://addons/gut/gut_cmdln.gd -gselect=test_determinism_survival -gexit 2>&1 | grep -iE "Passing|Failing|All tests passed|\[Failed\]"
Expected: PASSES unchanged (the 3312602286 / 4217109746 baseline still holds — the existing run has no drones, so the new loop appended nothing). If this FAILS, the enrichment is not baseline-safe — STOP and report (do not re-pin; investigate whether a drone_charge scalar or an aggregate-line change leaked in).
- Step 8: Regression — snapshot_string byte/relative comparison tests still pass
Run: godot --headless --path . -s res://addons/gut/gut_cmdln.gd -gselect=test_pilots_query -gexit 2>&1 | grep -iE "Passing|Failing|All tests passed|\[Failed\]"
Run: godot --headless --path . -s res://addons/gut/gut_cmdln.gd -gselect=test_determinism -gexit 2>&1 | grep -iE "Passing|Failing|All tests passed|\[Failed\]"
Expected: both PASS unchanged (empty drones pool → snapshot_string byte-identical for their single-pilot / no-drone runs).
- Step 9: Commit
git add sim/sim.gd sim/drone_director.gd tests/test_determinism_coop.gdgit commit -m "feat(sim): include the drones pool in determinism checksums
state_checksum()/snapshot_string() already covered all pilots but omitted the dronespool (documented as safe only because no determinism test deployed a drone). Append adrones-pool loop to both — baseline-safe: the existing single-pilot run deploys no drones,so the loop is empty and the 3312602286/4217109746 baseline is unchanged (no re-pin). Theaccruing per-pilot drone_charge scalar stays excluded (its effect surfaces via the pool).Updates the stale drone_director comment."Task 2: 2-pilot determinism test with a pinned baseline
Section titled “Task 2: 2-pilot determinism test with a pinned baseline”Files:
- Modify:
tests/test_determinism_coop.gd(add the 2-pilot test to the file created in Task 1).
Interfaces:
- Consumes:
Sim.new(seed, db),enable_survival(),add_player2(),pilots[1],drone_director.deploy_drones(sim, pilot, [spec]),tick(inputs: Array[InputState]),state_checksum(),snapshot_string()(now drone-aware from Task 1).
Context: This proves the co-op sim (2 pilots + a per-pilot deployed drone) is byte-identical across two same-seed runs — the Phase-3 netcode guarantee — and pins a new, separate 2-pilot baseline. The baseline VALUES don’t exist until the code runs; you capture them from the first green run (standard determinism-baseline bootstrapping, Steps 2–4).
- Step 1: Add the 2-pilot test with placeholder baselines that print the real values
Append to tests/test_determinism_coop.gd:
const COOP_SPEC := {"klass": "sentinel", "life": 1.0, "dmg": 1.0, "radius": 1.0, "heal": 0.0, "extras": 0}
# Two pilots, seed 1234, P2 owns a deployed Sentinel kept alive (life/hp forced high, identically# in both runs, so it survives all 600 ticks and the final checksum genuinely includes drone state).func _coop_run() -> Sim: var s := Sim.new(1234, SimContentFixture.db()) s.enable_survival() s.add_player2() s.drone_director.deploy_drones(s, s.pilots[1], [COOP_SPEC]) s.drones[0].life = 9999.0 # keep it alive the whole run (deterministic: identical in both) s.drones[0].hp = 9999.0 s.drones[0].max_hp = 9999.0 for i in range(600): var d1 := Vector2(cos(float(i) * 0.05), sin(float(i) * 0.03)) if d1.length() > 0.0: d1 = d1.normalized() var d2 := Vector2(sin(float(i) * 0.04), cos(float(i) * 0.06)) if d2.length() > 0.0: d2 = d2.normalized() s.tick([InputState.new(d1), InputState.new(d2)]) return s
func test_coop_trace_is_deterministic_and_pinned() -> void: var a := _coop_run() var b := _coop_run() assert_eq(a.pilots.size(), 2, "precondition: two pilots") assert_gt(a.drones.size(), 0, "precondition: the drone is still deployed at run end") # PRIMARY guarantee: identical inputs + seed -> byte-identical co-op trace. assert_eq(a.state_checksum(), b.state_checksum(), "2-pilot + drone sim is deterministic (a==b)") assert_eq(a.snapshot_string(), b.snapshot_string(), "2-pilot snapshot is deterministic (a==b)") # TEMP: print the values to capture the baseline (remove in Step 3). print("COOP_CHECKSUM=", a.state_checksum(), " COOP_HASH=", a.snapshot_string().hash())- Step 2: Run it and capture the printed baseline values
Run: godot --headless --path . -s res://addons/gut/gut_cmdln.gd -gselect=test_determinism_coop -gexit 2>&1 | grep -E "COOP_CHECKSUM|Passing|Failing|\[Failed\]"
Expected: the test_coop_trace_is_deterministic_and_pinned test passes its a==b asserts, and a line like COOP_CHECKSUM=<N> COOP_HASH=<M> prints. Record <N> and <M>. (If a==b FAILS, the co-op sim is non-deterministic — STOP and report; do not pin a baseline over a real desync.)
- Step 3: Replace the temp print with pinned baseline assertions
In test_coop_trace_is_deterministic_and_pinned, delete the print(...) line and append (substituting the captured <N>/<M>):
# 2-pilot baseline — seed 1234, 600 ticks, P2 owns a deployed Sentinel (life/hp forced high). # SEPARATE from the single-pilot baseline in test_determinism_survival.gd. Captured 2026-07-14. assert_eq(a.state_checksum(), <N>, "2-pilot state checksum baseline") assert_eq(a.snapshot_string().hash(), <M>, "2-pilot snapshot hash baseline")- Step 4: Run the coop test to verify it passes with the pinned baseline
Run: godot --headless --path . -s res://addons/gut/gut_cmdln.gd -gselect=test_determinism_coop -gexit 2>&1 | grep -iE "Passing|Failing|All tests passed|\[Failed\]"
Expected: all test_determinism_coop tests pass (the Task-1 red test + this pinned 2-pilot test).
- Step 5: Full suite + confirm the existing baseline still holds
Run:
godot --headless --path . -s res://addons/gut/gut_cmdln.gd -gselect=test_determinism_survival -gexit 2>&1 | grep -iE "All tests passed|\[Failed\]"godot --headless --path . -s res://addons/gut/gut_cmdln.gd -gdir=res://tests -ginclude_subdirs -gexit 2>&1 | grep -A26 "= Run Summary" | grep -iE "^Scripts|^Tests|^Passing|^Failing|^Risky"Expected: single-pilot baseline “All tests passed!”; full suite 0 failing (only the pre-existing 4 Risky/Pending), script/test counts up by the new file/tests.
- Step 6: Commit
git add tests/test_determinism_coop.gdgit commit -m "test(sim): 2-pilot determinism trace + baseline
Two same-seed Sims, each with a P2 and a deployed per-pilot Sentinel, run 600 ticksdriving both pilots; asserts byte-identical state_checksum()/snapshot_string() acrossruns (the Phase-3 co-op netcode guarantee) and pins a new, separate 2-pilot baseline.The single-pilot baseline in test_determinism_survival.gd is untouched."Self-Review
Section titled “Self-Review”1. Spec coverage:
- Component 1 (state_checksum drones pool, exclude drone_charge scalar) → Task 1 Step 3. ✓
- Component 2 (snapshot_string per-drone segment, no count field) → Task 1 Step 4. ✓
- Component 3 (update stale comment) → Task 1 Step 5. ✓
- Component 4 (2-pilot test, deploy P2 drone, a==b + new baseline) → Task 2. ✓
- Baseline impact (existing single-pilot baseline unchanged, no re-pin) → Task 1 Step 7 (explicit regression proof) + Task 2 Step 5. ✓
- Testing (coop test is the test; survival/pilots_query/determinism regressions) → Task 1 Steps 7–8, Task 2 Step 5. ✓
- Out-of-scope items (drone_charge scalar, forced level-ups, networked MP) → honored (not implemented). ✓
2. Placeholder scan: No “TBD/TODO/handle edge cases”. The <N>/<M> in Task 2 are deliberate captured-at-runtime determinism baselines with an explicit capture procedure (Steps 2–3) — this is the standard, unavoidable bootstrapping for a determinism pin, not a vague placeholder.
3. Type consistency: DRONE_SPEC/COOP_SPEC are the same dict shape used in tests/test_drones.gd. deploy_drones(sim, pilot, [spec]), tick([inp1, inp2]), add_player2(), pilots[1], and the DroneState field names (pos/vel/hp/life/charge/pulse_timer/phase/owner) all match the verified sources. state_checksum()/snapshot_string() signatures unchanged (still zero-arg).