BS-PR-601 - Transactional scene saves, autosave, and crash recovery #39

Closed
opened 2026-07-10 04:03:16 -04:00 by Rbanh · 4 comments
Owner

Summary

Make scene persistence safe enough for daily production with atomic writes, timed autosaves, recoverable snapshots, and explicit recovery UX.

Scope

  • Write scenes through a temporary file plus fsync/rename so interruption cannot corrupt the last good scene.
  • Add configurable autosave intervals and bounded per-scene recovery generations outside source-controlled assets.
  • Detect a newer recovery snapshot on startup/open and offer inspect, restore, discard, or keep-both actions.
  • Record save/recovery failures in the status strip and diagnostics with the exact path and recovery action.
  • Never autosave generated hydration state or mutate the authored scene dirty flag merely because an autosave ran.

Acceptance criteria

  • Killing the editor during a scene write leaves the prior scene loadable.
  • Dirty scenes produce bounded recovery snapshots without replacing the authored file.
  • Recovery discovery and restore/discard flows are covered by deterministic tests.
  • Successful manual save retires obsolete recovery snapshots.
  • README and scene I/O documentation cover configuration and recovery locations.
## Summary Make scene persistence safe enough for daily production with atomic writes, timed autosaves, recoverable snapshots, and explicit recovery UX. ## Scope - Write scenes through a temporary file plus fsync/rename so interruption cannot corrupt the last good scene. - Add configurable autosave intervals and bounded per-scene recovery generations outside source-controlled assets. - Detect a newer recovery snapshot on startup/open and offer inspect, restore, discard, or keep-both actions. - Record save/recovery failures in the status strip and diagnostics with the exact path and recovery action. - Never autosave generated hydration state or mutate the authored scene dirty flag merely because an autosave ran. ## Acceptance criteria - [x] Killing the editor during a scene write leaves the prior scene loadable. - [x] Dirty scenes produce bounded recovery snapshots without replacing the authored file. - [x] Recovery discovery and restore/discard flows are covered by deterministic tests. - [x] Successful manual save retires obsolete recovery snapshots. - [x] README and scene I/O documentation cover configuration and recovery locations.
Rbanh added this to the M6 - Reliability, recovery, and project workflow milestone 2026-07-10 04:03:16 -04:00
Author
Owner

Local implementation progress (2026-07-10)

The first reliability slice is implemented locally:

  • same-directory temporary write, file sync, atomic rename, and parent-directory sync on Unix;
  • manual scenes, prefabs, and selection exports share the transactional byte writer;
  • scene serialization is separated from persistence and always rebuilds stripped hydrated state, including error paths;
  • dirty saved scenes create user-local recovery snapshots after 120 seconds by default with a 15-second floor and five-generation retention;
  • recovery storage uses stable project/scene hashes under XDG state or ~/.local/state, outside project assets;
  • newer snapshots are discovered on load and surfaced through status plus File-menu Restore/Discard actions;
  • restore loads into the original scene context and remains dirty until manual Save;
  • successful manual Save retires the scene's recovery generations;
  • ADR 0023 and user/editor documentation define the contract and current unsaved-scene limitation.

Verification so far:

  • three recovery/atomic-write tests pass;
  • all 38 editor library tests pass;
  • strict editor dev-feature Clippy passes;
  • live Ctrl+S completed with no temp-file residue, scene diff, warning, error, panic, or validation failure.

Remaining before #39 closes: end-to-end timed recovery/restore/discard QA, interruption/failure injection, unsaved-new-scene recovery identity, and broader workspace/release-gate evidence.

## Local implementation progress (2026-07-10) The first reliability slice is implemented locally: - same-directory temporary write, file sync, atomic rename, and parent-directory sync on Unix; - manual scenes, prefabs, and selection exports share the transactional byte writer; - scene serialization is separated from persistence and always rebuilds stripped hydrated state, including error paths; - dirty saved scenes create user-local recovery snapshots after 120 seconds by default with a 15-second floor and five-generation retention; - recovery storage uses stable project/scene hashes under XDG state or ~/.local/state, outside project assets; - newer snapshots are discovered on load and surfaced through status plus File-menu Restore/Discard actions; - restore loads into the original scene context and remains dirty until manual Save; - successful manual Save retires the scene's recovery generations; - ADR 0023 and user/editor documentation define the contract and current unsaved-scene limitation. Verification so far: - three recovery/atomic-write tests pass; - all 38 editor library tests pass; - strict editor dev-feature Clippy passes; - live Ctrl+S completed with no temp-file residue, scene diff, warning, error, panic, or validation failure. Remaining before #39 closes: end-to-end timed recovery/restore/discard QA, interruption/failure injection, unsaved-new-scene recovery identity, and broader workspace/release-gate evidence.
Author
Owner

Recovery P0 implementation and live QA update (2026-07-10):

  • Scene, prefab, and selection writes now use same-directory durable temp files plus sync/rename.
  • Dirty saved scenes write bounded user-local recovery generations (default 120 s / 5 generations) without clearing dirty state or serializing hydration helpers.
  • Startup/open discovers newer recovery; status strip and File menu expose restore/discard.
  • Restore keeps the authored path, marks the scene dirty, clears history, and now deliberately retains the snapshot until explicit discard or successful manual save.
  • Successful manual save retires all generations for that scene.
  • Added deterministic coverage for atomic replacement, simulated interruption before rename preserving the last authored scene, stable path isolation, newer-snapshot discovery, bounded pruning, restore lifecycle, and all-generation discard.
  • Live crash QA passed: generated a short-interval snapshot, SIGKILLed the editor, relaunched normally, discovered recovery, restored without modifying the authored file, retained recovery after restore, explicitly discarded it, and verified the recovery directory was empty.
  • Full gate passed: strict workspace Clippy, strict editor dev-feature Clippy, 120 tests passed / 1 intentionally ignored, and cargo validate-levels.

Remaining ticket scope before closure: decide/implement an explicit keep-both/inspect workflow and add a user-facing diagnostics history for save/recovery failures (the status strip already reports exact failures).

Recovery P0 implementation and live QA update (2026-07-10): - Scene, prefab, and selection writes now use same-directory durable temp files plus sync/rename. - Dirty saved scenes write bounded user-local recovery generations (default 120 s / 5 generations) without clearing dirty state or serializing hydration helpers. - Startup/open discovers newer recovery; status strip and File menu expose restore/discard. - Restore keeps the authored path, marks the scene dirty, clears history, and now deliberately retains the snapshot until explicit discard or successful manual save. - Successful manual save retires all generations for that scene. - Added deterministic coverage for atomic replacement, simulated interruption before rename preserving the last authored scene, stable path isolation, newer-snapshot discovery, bounded pruning, restore lifecycle, and all-generation discard. - Live crash QA passed: generated a short-interval snapshot, SIGKILLed the editor, relaunched normally, discovered recovery, restored without modifying the authored file, retained recovery after restore, explicitly discarded it, and verified the recovery directory was empty. - Full gate passed: strict workspace Clippy, strict editor dev-feature Clippy, 120 tests passed / 1 intentionally ignored, and `cargo validate-levels`. Remaining ticket scope before closure: decide/implement an explicit keep-both/inspect workflow and add a user-facing diagnostics history for save/recovery failures (the status strip already reports exact failures).
Author
Owner

Follow-up observability slice completed:

  • SceneIo now retains a bounded 32-entry in-session operation log with monotonically increasing IDs and info/error classification.
  • Startup/load/save/recovery/restore/discard results flow through the log; exact paths and failure text remain inspectable after the status strip changes.
  • Window → Diagnostics now renders the newest-first Scene I/O log with error emphasis and a clear action.
  • Added bounded-log/error-classification regression coverage and updated README/editor architecture docs.
  • Live UI smoke passed on a clean authored-scene launch; Diagnostics showed startup load as event #01.
  • Final post-change gate: strict workspace and dev-feature Clippy clean, 121 tests passed / 1 intentionally ignored, cargo validate-levels, formatting, and diff whitespace checks clean.

The remaining #39 product decision is the explicit keep-both/inspect UX; core transactional persistence, recovery lifecycle, and failure observability are now implemented and verified.

Follow-up observability slice completed: - `SceneIo` now retains a bounded 32-entry in-session operation log with monotonically increasing IDs and info/error classification. - Startup/load/save/recovery/restore/discard results flow through the log; exact paths and failure text remain inspectable after the status strip changes. - Window → Diagnostics now renders the newest-first Scene I/O log with error emphasis and a clear action. - Added bounded-log/error-classification regression coverage and updated README/editor architecture docs. - Live UI smoke passed on a clean authored-scene launch; Diagnostics showed startup load as event #01. - Final post-change gate: strict workspace and dev-feature Clippy clean, 121 tests passed / 1 intentionally ignored, `cargo validate-levels`, formatting, and diff whitespace checks clean. The remaining #39 product decision is the explicit keep-both/inspect UX; core transactional persistence, recovery lifecycle, and failure observability are now implemented and verified.
Author
Owner

Keep-both workflow completed:

  • File → Save Recovery Copy As... writes the selected recovery generation to a separate scene through the same atomic writer.
  • The active scene path, dirty state, recovery pointer, and source snapshot are deliberately unchanged.
  • Restore is the inspect path: it loads recovery into dirty editor state without overwriting the authored file; users can then save, keep a separate copy, or discard.
  • Added byte-preservation/source-retention regression coverage and updated README, architecture notes, and ADR 0023.
  • Dev-feature strict Clippy, focused regression, formatting, diff checks, and a clean final desktop launch all pass.

This completes the ticket's transactional write, bounded autosave, discovery, inspect/restore, keep-both, discard, retirement, observability, documentation, deterministic test, and live crash-QA scope.

Keep-both workflow completed: - File → Save Recovery Copy As... writes the selected recovery generation to a separate scene through the same atomic writer. - The active scene path, dirty state, recovery pointer, and source snapshot are deliberately unchanged. - Restore is the inspect path: it loads recovery into dirty editor state without overwriting the authored file; users can then save, keep a separate copy, or discard. - Added byte-preservation/source-retention regression coverage and updated README, architecture notes, and ADR 0023. - Dev-feature strict Clippy, focused regression, formatting, diff checks, and a clean final desktop launch all pass. This completes the ticket's transactional write, bounded autosave, discovery, inspect/restore, keep-both, discard, retirement, observability, documentation, deterministic test, and live crash-QA scope.
Rbanh closed this issue 2026-07-10 05:25:44 -04:00
Rbanh reopened this issue 2026-07-10 05:26:09 -04:00
Rbanh closed this issue 2026-07-10 05:26:19 -04:00
Sign in to join this conversation.
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: Falling-Metal-Interactive/Blacksite#39
No description provided.