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
+25
View File
@@ -178,6 +178,31 @@ struct BoardStoreTests {
}
}
@Test("A store built from a pre-walked result is the store the walking init would have built")
func prewalkedInitMatchesTheWalkingInit() async throws {
let fixture = try makeBoard()
defer { fixture.tearDown() }
// The two inits the board window's loading state put side by side: the walk on this actor,
// and the same walk run somewhere else and handed over (`BoardStoreRegistry.acquireOffMain`
// runs it on a detached task). If these ever disagreed, an open would show a different board
// depending on which actor walked it.
let walkedHere = try BoardStore(rootURL: fixture.root)
let result = try BoardLoader.load(boardRoot: fixture.root)
let walkedElsewhere = BoardStore(rootURL: fixture.root, loaded: result)
#expect(walkedElsewhere.rootURL == walkedHere.rootURL)
#expect(walkedElsewhere.snapshot == walkedHere.snapshot)
#expect(walkedElsewhere.loadWarnings == walkedHere.loadWarnings)
#expect(walkedElsewhere.defects.count == walkedHere.defects.count)
// The rest of the opening posture, which is what a second init could quietly get wrong.
#expect(walkedElsewhere.reloadFailure == nil)
#expect(walkedElsewhere.readOnlyLock == nil)
#expect(!walkedElsewhere.isReadOnly)
#expect(walkedElsewhere.selection == .empty)
#expect(walkedElsewhere.reloadGeneration == 0)
}
// MARK: Reloading
@Test("A foreign tree change reloads and the snapshot shows the external edit")