58 lines
3.7 KiB
Markdown
58 lines
3.7 KiB
Markdown
# ADR 0042: Guarded Editor Shutdown And Document Savepoints
|
|
|
|
## Status
|
|
|
|
Accepted
|
|
|
|
## Context
|
|
|
|
Bevy's default native-window policy closes a requested window and exits after the final window is
|
|
gone. That policy bypasses Blacksite's scene-tab and project-switch confirmations, so a compositor
|
|
close request could silently discard dirty authored work. Reading `WindowCloseRequested` in another
|
|
system is insufficient because Bevy's default close system has its own message cursor and still
|
|
despawns the window.
|
|
|
|
Scene dirtiness was also independent of editor history. Every undo or redo marked the active scene
|
|
dirty, even when undo restored the exact state that had last loaded or saved. Undo depth alone is
|
|
not a valid savepoint: editor commands contain runtime entity IDs and are intentionally cleared when
|
|
tabs switch, while saves may establish a clean point in the middle of a timeline.
|
|
|
|
## Decision
|
|
|
|
- The full editor disables Bevy's automatic close-on-request behavior. The game and Project Browser
|
|
keep their existing native-window policy.
|
|
- One editor shutdown coordinator owns native window close, **File > Quit**, project switching, and
|
|
programmatic editor-exit requests. It coalesces duplicate requests and is the only normal path
|
|
authorized to emit `AppExit`.
|
|
- Final authorization runs in a dedicated main schedule after Bevy's `Last` schedule. The guard
|
|
rechecks dirty scene state after every editor authoring system, then clean-session persistence
|
|
runs after that finalizer in the same post-`Last` schedule.
|
|
- A clean editor exits immediately. Dirty scene tabs enter one non-blocking native decision with
|
|
explicit **Save All**, **Discard**, and **Cancel** actions. Save completion, including an untitled
|
|
tab's Save As picker, is applied on the main thread. The coordinator exits only after every dirty
|
|
tab saves or the user explicitly discards; cancel or failure retains the live session.
|
|
- Clean-session persistence accepts only an exit authorized by the coordinator. Crashes, forced
|
|
termination, and unguarded `AppExit` retain the abnormal-session marker.
|
|
- Every scene tab owns a canonical authored-content checkpoint established by a successful load or
|
|
save. The projection uses stable actor IDs, stable parent actor IDs, sorted component identities,
|
|
and scene composition; it excludes transient Bevy entity numbers and runtime hydration.
|
|
- A committed edit marks the active tab dirty. Successful undo or redo compares the current
|
|
canonical authored projection with that tab's checkpoint. Equality clears the dirty marker;
|
|
divergence sets it. Failed saves and recovery restores do not advance the checkpoint.
|
|
- Editor command stacks remain active-document runtime state and are still cleared on tab switches.
|
|
Checkpoint ownership belongs to the document tab, so dirty truth survives those clears.
|
|
|
|
## Consequences
|
|
|
|
- Native close can no longer silently discard authored scene tabs, and the editor remains responsive
|
|
while confirmation or Save As dialogs are open.
|
|
- A normal exit requires an explicit editor-owned authorization. New code must request shutdown
|
|
through the coordinator instead of writing `AppExit` directly.
|
|
- Undoing to the last saved authored state clears title and tab dirty indicators, including after a
|
|
save at nonzero history depth. Redo or a divergent branch marks the document dirty again.
|
|
- Reconciliation serializes the authored projection after undo and redo. This is more work than a
|
|
depth comparison, but it is bounded to explicit history navigation and remains correct across
|
|
direct non-history repairs, entity respawns, and tab switches.
|
|
- Operating-system process kill and power loss cannot be confirmed; recovery and abnormal-session
|
|
handling remain the safety boundary for those cases.
|