The titlebar stand-in stops reappearing on a window that attaches after the board has loaded

`configureWindow` removed the loading stand-in from whatever window it happened to be
running against, but left `onAttach` installed exactly as `configureLoadingWindow` set
it — a closure that builds a fresh stand-in on every attach, deliberately, so it survives
the provisional-window swap while the board is still loading. SwiftUI's dismantle-then-
make swap has no deadline, though: when it lands after the store has already loaded, the
stale closure reinstalls a stand-in on the real window a moment before `attach`'s own
`addTitlebarAccessoryIfPossible()` installs the real widget beside it — both in the
titlebar, stand-in leading, and nothing left holding a reference to remove it a second
time. `onAttach` is now replaced in `configureWindow` too, keeping only the frame-
placement half a re-attached window still needs. New suite
`BoardLoadingTitlebarSwapRaceTests` drives `HostedWindowController` through the same
attach sequence and pins both the fixed and (as a negative control) the unfixed shape.

Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
2026-08-09 17:32:22 -04:00
parent cb0af4b95a
commit 01aa9a0c94
2 changed files with 136 additions and 0 deletions
+18
View File
@@ -808,6 +808,24 @@ struct BoardWindowHost: View {
}
self.loadingAccessory = nil
// **`onAttach` itself has to be replaced here, not just the stand-in it already installed**
// (Pipeline card a73bad86, reopened). SwiftUI's dismantle-then-make swap of the provisional
// window for the real one is not guaranteed to land before the load does
// (`HostedWindowController.detach`'s own doc comment) a fast load and a slow swap means the
// real window's `attach` fires *after* this method has already run. Left alone,
// `configureLoadingWindow`'s closure is still installed at that point, and it builds a fresh
// stand-in and installs it on whatever window attaches next; `HostedWindowController.attach`
// then runs `addTitlebarAccessoryIfPossible()` right after `onAttach`, installing the real
// widget beside it both in the titlebar, stand-in leading, and nothing left to remove it a
// second time. "Occasionally" was exactly this ordering. The frame placement half survives,
// because a late-swapped window is still a window whose saved frame has not been applied yet
// only the stand-in-building half goes.
windowController.onAttach = { window in
if let saved = appModel.boardRegistry.record(id: recordID)?.windowFrame {
window.setFrame(HostedWindowController.placementOnCurrentScreens(for: saved), display: true)
}
}
// The window-title widget (03-board-ui.md § Board popover) **board windows only**, which
// is why it is installed here rather than in `WindowAccessor`: welcome, the bootstrap and
// card windows share that machinery and have no board to describe. It goes in after the