Every open passes through a real loading window — the walk moves off the main actor

Phase 2 of the decision surface: the pre-snapshot loading state ruled
2026-07-29 (02 § Launch and window lifecycle), built. The board window
appears immediately at its saved frame, titled with the registry
record's cached name, its content a centered spinner behind an
injectable ~200ms grace — no skeletons, and the first snapshot snaps in
place. The tree walk runs off-main via BoardStoreRegistry.acquireOffMain
(per-board single-flight keyed by file identity — concurrent opens of
one root share a walk, restoration of many boards is genuinely
parallel), landing in BoardStore's new designated init(rootURL:loaded:);
the self-walking init survives as a convenience for its ~470 callers.

⌘W during the walk is real: configureWindow split into a loading half
(frame restore, frame tracking, close interception — installed before
the walk) and a store half (toolbar, widget, hideTitle, undo — installed
at the snap), and the walk lives in an explicitly held BoardOpenWalk so
the user's close and SwiftUI's teardown end in one cancel().
Cancellation is discard-on-completion: nil from acquireOffMain means
nothing was built, nothing retained, and no open-now flag was ever set.
Failure keeps today's sequence exactly: record the launch failure,
welcome's row carries it, the window retires.

Claude-Session: https://claude.ai/code/session_01CqjXB7ASoWtbyoGod68k97
This commit is contained in:
2026-08-01 09:53:26 -04:00
parent ba1726fa77
commit 0933ac1b01
8 changed files with 795 additions and 41 deletions
+7
View File
@@ -36,6 +36,13 @@ enum AccessibilityPhrases {
return title
}
/// What the pre-snapshot loading surface says (02-architecture.md § Launch and window lifecycle;
/// `BoardLoadingView`). The surface draws a bare system spinner and no text "no skeleton lanes,
/// the motion language animates real data only" so this is the *only* description a VoiceOver
/// user gets of a board window that is still walking its tree, and a spinner with nothing to say
/// would leave that window silent.
static let boardLoading = "Loading board"
/// "3 cards", "1 card" the app's **one** plural folding for a card count, borrowed from
/// `TrashModel.phrase` rather than restated so a lane's spoken count and the trash column's
/// cannot drift apart.