# Guarded Shutdown And History Savepoints

> **Historical plan — not current implementation guidance.** Use the [documentation index](../../docs/README.md) for current behavior and architecture.

Date: 2026-07-13
Issue: BS-PR-709 / Gitea #55
Milestone: M7 - Production Readiness

## Goal

Route every editor-exit surface through one non-blocking dirty-document decision and make scene
dirtiness follow the authored undo timeline. Closing the native window, choosing File > Quit, and
requesting an editor exit programmatically must never silently discard dirty scene tabs.

## Decisions

- The editor disables Bevy's automatic close-and-exit systems and owns primary-window close
  requests. A guarded-shutdown resource is the single authority that may emit `AppExit`.
- The final dirty-state recheck and `AppExit` authorization run in a dedicated schedule after
  Bevy's `Last`; session clean-marker persistence runs after that finalizer.
- Native window close, File > Quit, and internal editor quit requests enqueue the same shutdown
  intent. Repeated requests while confirmation or Save As is pending are coalesced.
- Clean sessions exit immediately. Dirty sessions use the shared `NativeDialogBroker` and a single
  Save All / Discard / Cancel decision. Cancel preserves all tabs and live editor state; Discard
  exits without rewriting authored files; Save All exits only after every dirty tab is saved.
- Untitled tabs and other asynchronous Save As work keep shutdown pending until their dialog result
  is applied on the main thread. Cancellation or any failed write returns the editor to an idle,
  dirty, recoverable state and does not emit `AppExit`.
- Each scene tab retains a canonical authored-content checkpoint established only by a successful
  load or save. The checkpoint is keyed by stable actor identity, canonical component order, stable
  parent identity, and scene composition rather than transient Bevy entity numbers.
- Push marks the active document dirty. Undo and redo serialize the authoritative authored
  projection and compare it with that tab's checkpoint, so saving at nonzero history depth,
  returning to that point, branching, tab switches, and direct non-history mutations remain exact.

## Implementation

1. Add per-tab canonical clean checkpoints to scene I/O and reconcile `SceneIo::dirty` after push,
   undo, redo, save, load, and document switches without persisting Entity-ID-based history stacks.
2. Add focused history tests for undo-to-clean, redo-away-from-clean, save at nonzero depth,
   branching before/after the clean point, and tab isolation.
3. Add a guarded-shutdown plugin/resource that intercepts `WindowCloseRequested`, accepts menu and
   programmatic requests, coordinates non-blocking confirmation/save completion, and emits the only
   final `AppExit` for the full editor.
4. Make Save All report complete, pending, cancelled, or failed explicitly. Resume pending shutdown
   after untitled Save As completion and preserve the originating tab plus dirty state on failure.
5. Add File > Quit and route Switch Project's exit half through the same guard without spawning a
   replacement process until the dirty-document decision succeeds.
6. Update ADR 0023, native-dialog/session-recovery documentation, editor architecture guidance,
   root README controls/checklist, and production-readiness evidence.
7. Run formatting, strict Clippy, workspace tests, and focused headless state-machine tests. Keep
   packaged tests deferred by project-owner direction.
8. Launch the exact editor commit under Hyprland. Exercise Cancel, Discard, Save All, clean close,
   File > Quit, and the compositor close button; verify the editor remains responsive during native
   dialogs, no state is lost on cancel/failure, and no process or warning remains after exit.

## Acceptance

- Native close, File > Quit, and programmatic editor exit share one guarded implementation.
- One or many dirty tabs cannot be lost without explicit Discard; failed or cancelled saves do not
  exit and preserve the session.
- Undoing exactly to the saved state clears the dirty marker; redo or a divergent edit restores it.
- Saving at nonzero history depth establishes a new clean point without deleting useful history.
- Headless state-machine tests and exact-commit native Linux QA pass with clean logs.
