fix(persist): only clear dirty after a successful save so a forced Save retries - #383
Conversation
…ve retries In scripts/main.gd:persist(), sim.dirty was cleared unconditionally before GameState.save_game() ran. If the write failed, sim.dirty stayed false and a fast second explicit Save click hit the `if not sim.dirty: return true` early-return, so the caller pushed "Game saved. Tiny bureaucracy, handled." without writing — silently losing colony progress in that failure window. Fix: - Gate the dirty early-return on `not force`, so a forced save (explicit Save click, recruit, placement, quit) always re-invokes GameState.save_game. - Move `sim.dirty = false` to after a successful write, so a failed write leaves the flag set and the next forced save re-attempts. scripts/game_state.gd: add a `_save_game_hook` (mirroring the existing _local_storage_reader hook) so tests can force a failed write and count invocations without touching the filesystem. tests/test_save_failure_surfaced.gd: add Flow 6, which forces a failed write, calls the forced save again immediately, and asserts GameState.save_game was invoked a second time and that no "Game saved" event was pushed. All 27 test suites pass headless; smoke test is clean. Fixes #379 Signed-off-by: Saffron <263493777+itsmiso-ai@users.noreply.github.com>
There was a problem hiding this comment.
AI Automated Review
Full PR review.
Analysis engine: MiniMax-M2.7@https://litellm.jory.dev/v1 (anthropic) — primary route
Recommendation: Approve
This PR correctly fixes issue PR 379. The three-file change is minimal, focused, and directly addresses both the root cause (dirty flag cleared before write completes) and the symptom (second Save reports success without writing).
Change-by-Change Findings
scripts/game_state.gd (+8/-0)
Added _save_game_hook: Callable pointing to _save_game_impl by default. save_game() now delegates to this hook. This enables test injection without touching the filesystem — the cleanest approach for counting invocations.
scripts/main.gd (+10/-2)
Two surgical moves:
- Guard flip:
if not sim.dirty:→if not force and not sim.dirty:. A forced save (explicit Save, recruit, placement, quit) now always reachesGameState.save_game(state), even if a previous failed write leftsim.dirtycleared. - Reset reorder:
sim.dirty = falsemoved from before the write to after it succeeds. The flag only clears whenGameState.save_game(state)returns true.
tests/test_save_failure_surfaced.gd (+78/-1)
Flow 6 exercises the exact failure window from the issue:
- Installs a hook that always returns
falseand increments a call counter. - Calls
persist(true)twice in sequence and asserts both returnfalse(not the second returningtrueand lying about success). - Asserts
_save_call_count == 2— the write was re-attempted on the second click. - Asserts
main.sim.dirtystaystruethrough both failures. - Asserts no
"Game saved"event was pushed. - Verifies
sim.dirtyclears only after the hook is restored to returntrue.
Standards Compliance
Repository standards file (AGENTS.md) is present. Notable applicable conventions:
- Testing traps: Tests use
assert_eq(actual, expected, name)with three required arguments — Flow 6 follows this correctly throughout. - Testing traps: The test avoids the 2-argument
assert_eqparse-error trap. - Save/version migration: Not applicable; this is a behavior fix, not a save format change.
No standards conflicts detected.
Linked Issue Fit
Issue PR 379 acceptance criteria:
- ✅
persist(force=true)always invokesGameState.save_game(state)— verified by guard flipif not force and not sim.dirty. - ✅
sim.dirty = falseonly on successful write — verified by move after theif not GameState.save_game(state)branch. - ✅ Regression test in
tests/test_save_failure_surfaced.gdthat forces a failed write, callssave_game()again immediately, and assertsGameState.save_game(state)was invoked and"Game saved"was not pushed a second time — Flow 6 covers all three assertions.
Issue PR 379 expected files: scripts/main.gd, tests/test_save_failure_surfaced.gd, optionally scripts/game_state.gd — all three are touched.
Tool Harness Findings
No tool harness output in this corpus.
CI Check Results
All checks passed: Script test suite, macOS validation, and all platform exports (Windows, Web, Linux, macOS) plus headless smoke test. CI is green.
Unknowns / Needs Verification
None — all required evidence is present and verifiable from the corpus.
Fixes issue #379 by ensuring forced saves always attempt to write and that failed writes don't clear the dirty flag prematurely.
Fixes #379
Opened by foreman on review GO (workload wl-misospace-windowstead-379).