The decision surface — a refused open becomes a live repair, in place

Phase 3 of the decision surface, completing the card (01 ▸ Malformed
input, settled 2026-07-31). An attended open's fail-fast walk transforms
the loading window's content into one aggregated surface — never a
sheet, never a chain: defects grouped by class, each class stated once
with its files listed (Reveal in Finder + Open in Editor per row), a
class-level default preselected, per-item override behind a disclosure.
Only honest choices: YAML and malformed-schema get Editor + Re-check
(Skip below the root); newer-than-app gets Skip alone and blocks the
board at the root; the two root repairs — minted index, schema: 1 stamp
— are defaults. Repair and Open applies fixes in one store-less write
bracket and re-walks: clean proceeds, remainder re-aggregates into the
same surface. Cancel and ⌘W retire to welcome's row; restored opens
never see the surface at all (OpenOrigin rides the PendingOpen carrier).

Skips are per-open consent that rides the session — the store retains
the skip set and every reload passes it — and the opened board posts a
warning-tone notice naming what was left out, each item's Reveal riding
the banner strip's new reveal control. On Pro boards the repair bracket
binds its own EchoLedger, heal-marks everything, and the store adopts it
before the committer starts, so repairs land as one separate commit
authored Lanework Integrity — pinned end to end. Also fixed en route: a
retired loading window left its close interception installed and
returned false from windowShouldClose forever, blocking quit.

Claude-Session: https://claude.ai/code/session_01CqjXB7ASoWtbyoGod68k97
This commit is contained in:
2026-08-01 10:52:02 -04:00
parent 0933ac1b01
commit 31fee00c73
17 changed files with 2448 additions and 107 deletions
+85 -15
View File
@@ -190,6 +190,32 @@ public final class ScopedAccess {
}
}
// MARK: - OpenOrigin
/// **Whether a person asked for this board right now** (01-storage-format.md § Malformed input, the
/// decision surface, settled 2026-07-31):
///
/// > It appears on **attended opens only** (welcome click, File Open, Finder): restoration
/// > failures keep the retire-to-welcome-row landing, and the row's retry click is the attended open
/// > that then shows the surface repair is an attended act, and launch never chains dialogs.
///
/// So this is not a description of *where* an open came from it is the one bit that decides what a
/// failed one does. A closed two-case vocabulary rather than a `Bool` because the sentence a reader
/// needs at the branch is "restored boards retire", not "`isAttended` is false".
///
/// **Attended is the default everywhere**, and that is load-bearing: welcome's rows, File Open,
/// the Finder open, Duplicate's follow-on open and the template chooser's are all somebody clicking
/// something. Exactly one caller says otherwise launch restoration (`RestoreBootstrapView`) which
/// makes "did a person ask for this" a question one place answers rather than a flag every call site
/// has to get right.
public enum OpenOrigin: Sendable, Equatable {
/// A person just asked for this board.
case attended
/// Launch restoration reopening what was open last time. Nobody is waiting on it, and a failure
/// lands on welcome's row rather than in a surface.
case restored
}
// MARK: - AppModel
/// The app's one piece of cross-window state: which boards are open, which card windows belong to
@@ -595,15 +621,31 @@ public final class AppModel {
// MARK: Pending opens
/// The security-scoped URL a board window is about to be built from, stashed between
/// `openBoard(at:)` and the host's first appearance.
/// Everything `openBoard(at:origin:)` knows that a `BoardWindowRef` cannot carry.
///
/// The handoff exists because a window value has to be `Codable` and a scoped URL is not a
/// string: by the time `BoardWindowHost` receives its `BoardWindowRef` the access token is gone
/// unless something carried it across. The host claims it on appear; an unclaimed entry (a window
/// that never opened) leaks one scope until quit, which is the cheapest failure available here.
/// **One struct rather than two parallel dictionaries** (the shape this replaced was
/// `pendingAccess` alone): both facts are stashed by the same call, claimed by the same call, and
/// meaningless apart a window that found an origin but no access, or the reverse, would be a
/// bug with no honest reading. Keeping them in one value makes "they are always in step" true by
/// construction instead of by two `removeValue`s that must not drift.
private struct PendingOpen {
/// The security-scoped URL the board will be built from, or `nil` where the open needed no
/// scope. See `ScopedAccess`: a `URL` rebuilt from `ref.path` grants nothing.
let access: ScopedAccess?
/// Whether a person asked for this board what a failed open branches on (`OpenOrigin`).
let origin: OpenOrigin
}
/// What a board window is about to be built from, stashed between `openBoard(at:origin:)` and the
/// host's first appearance.
///
/// The handoff exists because a window value has to be `Codable` and neither of these is a
/// string: by the time `BoardWindowHost` receives its `BoardWindowRef` the access token is gone,
/// and the origin was never in the ref at all. The host claims both on appear; an unclaimed entry
/// (a window that never opened) leaks one scope until quit, which is the cheapest failure
/// available here.
@ObservationIgnored
private var pendingAccess: [BoardWindowRef: ScopedAccess] = [:]
private var pendingOpens: [BoardWindowRef: PendingOpen] = [:]
private static let logger = Logger(subsystem: "dev.rzen.indie.Kanban", category: "app-model")
@@ -677,8 +719,19 @@ public final class AppModel {
/// **Called before any scene has appeared, and that's fine.** A cold launch's Finder-open can
/// reach here before `windowOpener` is captured; the URL joins `pendingOpenURLs` and this same
/// method runs again for it once `captureWindowActions` has something to open it with.
public func openBoard(at url: URL) {
///
/// - Parameter origin: whether a person asked for this board right now (`OpenOrigin`) the one
/// bit a *failed* open branches on (01-storage-format.md § Malformed input: the decision
/// surface "appears on attended opens only"). `.attended` by default, which is every caller but
/// launch restoration: welcome's rows, File Open, the Finder open, the template chooser's
/// follow-on, Duplicate's. The default is the rule stated once rather than repeated five times.
public func openBoard(at url: URL, origin: OpenOrigin = .attended) {
guard let windowOpener else {
// **The queue carries no origin, and needs none**: it is reachable only *before any scene
// exists*, which is the cold-launch Finder/URL open and nothing else restoration
// captures the window actions as its first act (`RestoreBootstrapView.restore`) and so can
// never queue. Everything in here is therefore attended, which is what the replay's
// default gives it.
pendingOpenURLs.append(url)
return
}
@@ -689,11 +742,22 @@ public final class AppModel {
}
let ref = BoardWindowRef(url: url)
stashPendingOpen(for: ref, url: url, origin: origin)
windowOpener(id: WindowID.board, value: ref)
}
/// Stashes what the window about to appear will claim the handoff's write half.
///
/// A method of its own rather than two lines inside `openBoard(at:origin:)` because that method
/// cannot run without SwiftUI's `OpenWindowAction`, which is not a thing a test can construct: the
/// carrier would otherwise be the one part of the attendance plumbing with no headless proof, and
/// an origin that quietly stopped travelling would look exactly like the app before this
/// milestone.
func stashPendingOpen(for ref: BoardWindowRef, url: URL, origin: OpenOrigin) {
// Replacing a stash for the same ref would strand the old scope; there is no such case today
// (an unopened window's ref is not reachable), but stopping the loser is free.
pendingAccess.removeValue(forKey: ref)?.stop()
pendingAccess[ref] = ScopedAccess(url)
windowOpener(id: WindowID.board, value: ref)
pendingOpens.removeValue(forKey: ref)?.access?.stop()
pendingOpens[ref] = PendingOpen(access: ScopedAccess(url), origin: origin)
}
/// Shows or focuses the welcome window. Its own scene id, so this works with no windows at
@@ -748,10 +812,16 @@ public final class AppModel {
sessions[ref]
}
/// Claims the scoped URL `openBoard(at:)` stashed for this window, or `nil` if it opened by some
/// other route. Claiming removes it: the session owns the balance from here.
func claimPendingAccess(for ref: BoardWindowRef) -> ScopedAccess? {
pendingAccess.removeValue(forKey: ref)
/// Claims what `openBoard(at:origin:)` stashed for this window. Claiming removes it: the session
/// owns the scope's balance from here.
///
/// A window that opened by some other route a route that never went through `openBoard` gets
/// no scope and reads as **attended**, which is the safe direction: the worst an attended reading
/// can do to a failed open is offer the user a repair they did not ask for, where the reverse
/// would silently retire a board somebody just double-clicked.
func claimPendingOpen(for ref: BoardWindowRef) -> (access: ScopedAccess?, origin: OpenOrigin) {
guard let pending = pendingOpens.removeValue(forKey: ref) else { return (nil, .attended) }
return (pending.access, pending.origin)
}
/// Starts a board's session the board window's host calls this once its load has succeeded.
+279 -16
View File
@@ -86,10 +86,39 @@ struct BoardWindowHost: View {
/// that precedes `start()`.
@State private var recordID: UUID?
/// This open's security-scoped access, claimed in `start()` and held until the session takes it
/// over or the open ends.
///
/// `@State` rather than a local in `start()` because the decision surface outlives that call: a
/// repair *writes into the board*, and a scope released when `start()` returned would be released
/// exactly before the one write that needs it. Every exit balances it the session adopts it,
/// or Cancel and the failure path stop it.
@State private var access: ScopedAccess?
/// Whether a person asked for this board (`OpenOrigin`) claimed beside the access, and read by
/// exactly one branch: what a failed walk does.
@State private var origin: OpenOrigin = .attended
/// The board's URL as this open resolved it the scoped one where there is one. Held for the
/// surface's sake, which re-walks and repairs against it long after `start()` has returned.
@State private var boardURL: URL?
/// **The repair bracket's ledger**, held between Repair and Open's writes and the store that the
/// following walk builds (`BoardRepairRun`, `EchoLedger.adopt`).
///
/// It cannot live anywhere else: the repairs run before a store exists and the receipts have to
/// reach that store's ledger before `beginSession` composes Pro's committer, or the app's own
/// repair commits as `Lanework External`. Cleared once adopted.
@State private var repairLedger: EchoLedger?
@State private var phase: Phase = .opening
private enum Phase {
case opening
/// **The walk refused and a person is looking at it** the decision surface, in the loading
/// window's own content area (01-storage-format.md § Malformed input, settled 2026-07-31:
/// "the loading content transforms in place, never a sheet over a spinner").
case deciding(BoardDecisionSurfaceModel)
case open(BoardStore)
/// The load failed; this window is on its way out and must not try again.
case failed
@@ -122,6 +151,15 @@ struct BoardWindowHost: View {
// when `phase` becomes `.open` a snap, which is what assigning outside `withAnimation`
// means here.
BoardLoadingView(indicator: loading)
case let .deciding(model):
// **In place.** The same content area the spinner was in, with no transition of its own:
// 02's snap, read for the surface that arrives instead of a snapshot.
BoardDecisionSurface(
model: model,
onRepairAndOpen: { repairAndOpen(model) },
onRecheck: { recheck(model) },
onCancel: { cancelDecision(model, closingWindow: false) }
)
case .failed:
// Nothing to render and nothing worth animating: this window is dismissing itself.
Color.clear
@@ -246,9 +284,13 @@ struct BoardWindowHost: View {
private func start() async {
guard case .opening = phase else { return }
// Claimed even on the failure path: an unclaimed stash is a scope nobody balances.
let access = appModel.claimPendingAccess(for: ref)
let url = access?.url ?? ref.url
// Claimed even on the failure path: an unclaimed stash is a scope nobody balances. The origin
// rides along one claim, one dictionary (`AppModel.claimPendingOpen`).
let claimed = appModel.claimPendingOpen(for: ref)
access = claimed.access
origin = claimed.origin
let url = claimed.access?.url ?? ref.url
boardURL = url
// Record before load (settled, 02 § Per-board app state). `displayName` is omitted an
// existing record's cached title survives untouched, and a brand-new one takes the folder
@@ -263,9 +305,23 @@ struct BoardWindowHost: View {
configureLoadingWindow(recordID: recordID)
loading.begin()
await attemptOpen(url: url, recordID: recordID, skipping: [])
}
/// **One walk, and what it lands in** the loop Repair and Open and Re-check re-enter.
///
/// It is a method rather than the tail of `start()` because the surface's two buttons "re-run the
/// whole walk" (01-storage-format.md § Malformed input) and must land in exactly the places this
/// lands: a clean walk proceeds into the ordinary open, and a walk that still refuses
/// re-aggregates into the *same* surface. Sharing the body is what makes "never a chained second
/// dialog" structural rather than remembered.
///
/// - Parameter skipping: the surface's consented skips, empty on a first attempt. It reaches the
/// walk *and* the store, which retains it for the session (`BoardStore.skippedPaths`).
private func attemptOpen(url: URL, recordID: UUID, skipping: Set<String>) async {
let store: BoardStore
do throws(BoardLoadFailure) {
guard let acquired = try await appModel.storeRegistry.acquireOffMain(url) else {
guard let acquired = try await appModel.storeRegistry.acquireOffMain(url, skipping: skipping) else {
// W landed while the walk was running, and the walk has now finished into a result
// nobody wants (`acquireOffMain`, discard-on-completion). The window is already
// closing and the registry kept nothing, so the only thing left to balance is this
@@ -273,27 +329,39 @@ struct BoardWindowHost: View {
// after the load, so a cancelled open never set one the very reason it lives there.
Self.logger.debug("board open cancelled during its walk")
loading.end()
access?.stop()
releaseAccess()
return
}
store = acquired
} catch {
Self.logger.error("board failed to open: \(error.description, privacy: .public)")
loading.end()
access?.stop()
phase = .failed
appModel.recordLaunchFailure(path: ref.path, message: error.description)
// The record above just changed the registry welcome, about to appear, must not
// render the stale list `AppModel` cached before this open began, or the failure would
// fall through to the unmatched-failures list for want of a row that already exists.
appModel.refreshRecents()
openWindow(id: WindowID.welcome)
dismissWindow(id: WindowID.board, value: ref)
handleWalkFailure(error, url: url, recordID: recordID)
return
}
// **The window retired while this walk was in flight** Cancel (or W) pressed during a
// Re-check, whose walk then landed successfully. The board must not open behind a window that
// has already gone to welcome, and the reference `acquireOffMain` took has to go back or the
// registry would hold a watcher for a board nobody is showing.
if case .failed = phase {
Self.logger.debug("a walk landed after the open was cancelled — releasing it")
appModel.storeRegistry.release(store)
releaseAccess()
return
}
loading.end()
// **The repair's receipts, into the board's own ledger before the session composes**
// (01: "On Pro boards the repairs drop heal-marked receipts and commit separately as one
// repair commit"). `beginSession` is where Pro's committer is built and started, and the
// committer harvests the ledger it is handed; receipts adopted after that line would be
// receipts the repair commit never sees, and the app's own repair would be authored
// `Lanework External`.
if let repairLedger {
store.echoes.adopt(repairLedger)
self.repairLedger = nil
}
// The load succeeded the frontmatter can be trusted now, so it replaces whatever
// provisional or stale name the record above was carrying. Through `syncDisplayState`,
// deliberately not a second `recordOpen`: this is a display-state refresh, not a second
@@ -307,15 +375,210 @@ struct BoardWindowHost: View {
)
appModel.boardRegistry.setOpenNow(id: recordID)
appModel.beginSession(ref: ref, store: store, recordID: recordID, access: access)
// The session owns the balance from here (`AppModel.beginSession`), so this window must not
// stop it on any later path.
access = nil
phase = .open(store)
configureWindow(store: store, recordID: recordID)
postSkipNoticeIfNeeded(store: store, skipping: skipping)
// "Opening a board from welcome closes welcome" (02 § Launch and window lifecycle). Harmless
// when welcome is not open, which is the ordinary case.
dismissWindow(id: WindowID.welcome)
}
/// **The attendance branch** (01-storage-format.md § Malformed input, settled 2026-07-31): the
/// surface "appears on attended opens only restoration failures keep the retire-to-welcome-row
/// landing".
///
/// Three outcomes, and the third is the new one:
///
/// - **A restored open retires**, exactly as it did before this milestone. Nobody is sitting in
/// front of a launch that reopened four boards, and "launch never chains dialogs".
/// - **An attended open whose failure is environmental retires too** a root that is gone, or a
/// root that is a file. There is nothing on disk to repair, so a surface would offer the user a
/// decision with no choices in it; welcome's row says the same thing in one line, which is
/// where it belonged already. (The carve-out the ruling implies rather than states Redesign
/// Gap 87cd782a.)
/// - **Anything else transforms into the surface**, in place, in this window.
///
/// A surface is *entered* rather than shown: it holds this window's access, its record, and its
/// URL for as long as the user is deciding, which is why none of the three is released here.
private func handleWalkFailure(_ error: BoardLoadFailure, url: URL, recordID: UUID) {
Self.logger.error("board failed to open: \(error.description, privacy: .public)")
// Retired while this walk ran (Cancel during a Re-check): the failure has already been
// recorded and the window is on its way out. A second `retire` would post the row twice.
if case .failed = phase { return }
guard Self.landing(for: error, origin: origin) == .decide else {
loading.end()
retire(message: error.description)
return
}
// Already deciding: this is a Re-check or a Repair and Open landing, and it re-aggregates in
// place the same model, the same window, no second dialog.
if case let .deciding(model) = phase {
model.reaggregate(error)
model.isWorking = false
return
}
loading.end()
phase = .deciding(BoardDecisionSurfaceModel(failure: error, boardRoot: url))
// W now means Cancel (01: the surface's own exit), replacing the loading half's
// cancel-the-walk closure a slot rather than a branch, `configureWindow`'s posture.
windowController.onCloseRequested = {
guard case let .deciding(model) = phase else { return }
cancelDecision(model, closingWindow: true)
}
}
/// Where a refused walk lands.
enum FailureLanding: Equatable {
/// Welcome, on the board's own recents row today's landing, unchanged.
case retire
/// The decision surface, in this window.
case decide
}
/// **The attendance branch as a pure rule** two facts in, one landing out, provable without a
/// window (`BoardDecisionSurfaceTests`).
///
/// It is a static rather than an `if` inside `handleWalkFailure` because it is the ruling's own
/// sentence and the one thing about this milestone that a regression would make silently wrong:
/// a launch that started showing surfaces would chain dialogs across four restored boards, and an
/// attended open that stopped showing one would look exactly like the app before this milestone.
static func landing(for failure: BoardLoadFailure, origin: OpenOrigin) -> FailureLanding {
guard origin == .attended else { return .retire }
return isEnvironmental(failure) ? .retire : .decide
}
/// **Nothing on disk to repair** the environmental carve-out's predicate.
///
/// A single defect, and that defect is a fact about the *root itself* rather than about a file
/// inside it: `BoardLoader` throws these immediately, before any walk, precisely because there is
/// nothing to walk. The single-defect check is stated rather than assumed the loader's
/// environmental throws are single by construction, and a future aggregate carrying one *among*
/// repairable defects should show the surface, because the rest of it is still actionable.
static func isEnvironmental(_ failure: BoardLoadFailure) -> Bool {
guard failure.defects.count == 1 else { return false }
switch failure.primary.reason {
case .unreadableRoot, .notADirectory:
return true
case .boardRootMissingIndex, .unparseableYAML, .missingSchema, .malformedSchema,
.schemaNewerThanApp, .missingOrder, .malformedOrder:
return false
}
}
// MARK: - The surface's three buttons
/// **Repair and Open**: apply every chosen fix in one write bracket, then re-run the whole walk
/// with the skip set (01-storage-format.md § Malformed input).
///
/// The repairs are store-less by necessity and heal-marked by rule (`BoardRepairRun`); the walk
/// that follows is the ordinary one, so a clean result proceeds into the ordinary open and a
/// dirty one re-aggregates here. A repair that failed does not abort anything the batch stops,
/// the failure shows on the surface, and the walk runs anyway, because "a partial repair simply
/// re-aggregates on the next walk".
private func repairAndOpen(_ model: BoardDecisionSurfaceModel) {
guard let url = boardURL, let recordID, !model.isWorking else { return }
model.isWorking = true
model.repairFailure = nil
let outcome = BoardRepairRun.apply(model.plannedRepairs, boardRoot: url)
model.repairFailure = outcome.failure
// Held for the store the walk below may build see `repairLedger`. Merged with anything an
// earlier pass left, so two rounds of repair both reach the commit.
if let existing = repairLedger {
existing.adopt(outcome.ledger)
} else {
repairLedger = outcome.ledger
}
let walk = Task { await attemptOpen(url: url, recordID: recordID, skipping: model.skipSet) }
openWalk.adopt(walk)
}
/// **Re-check**: re-run the whole walk with the current skip set, changing nothing on disk
/// (01: "a disk changed underneath re-aggregates into the *same* surface with the fresh defect
/// list a clean walk proceeds to the first snapshot").
private func recheck(_ model: BoardDecisionSurfaceModel) {
guard let url = boardURL, let recordID, !model.isWorking else { return }
model.isWorking = true
let walk = Task { await attemptOpen(url: url, recordID: recordID, skipping: model.skipSet) }
openWalk.adopt(walk)
}
/// **Cancel** and W, which means exactly this while the surface is up (01: "**Cancel** aborts
/// the open: the window retires and the board lands row-level on welcome, record-before-load
/// unchanged").
///
/// It is the failure path's own sequence, run against the aggregate the surface was showing: the
/// record was minted before the walk, so the row is already waiting for this message.
///
/// - Parameter closingWindow: true when AppKit asked (W), where the interception has to be
/// released for the close to complete. The button's own press dismisses through SwiftUI.
private func cancelDecision(_ model: BoardDecisionSurfaceModel, closingWindow: Bool) {
// A walk may still be in flight behind the surface (Cancel during a Re-check). Its landing is
// guarded by `phase`, which this sets to `.failed`; cancelling as well means the open is not
// merely ignored but abandoned.
openWalk.cancel()
retire(message: model.failure.description)
if closingWindow {
windowController.closeAfterFlush()
}
}
/// The retire-to-welcome landing, in one place: the sequence a failed restore has always had, and
/// the sequence Cancel now shares with it.
private func retire(message: String) {
releaseAccess()
phase = .failed
// **The interception has to go with the phase.** `windowShouldClose` returns `false` whenever
// a closure is installed that is how the close flush gets its turn so a retired window
// whose closure had nothing left to do would *refuse every close request for the rest of the
// app's life*, quit included. There is nothing to intercept once this window is on its way to
// welcome: no store, no session, no decision.
windowController.onCloseRequested = nil
appModel.recordLaunchFailure(path: ref.path, message: message)
// The record minted before the walk just changed the registry welcome, about to appear,
// must not render the stale list `AppModel` cached before this open began, or the failure
// would fall through to the unmatched-failures list for want of a row that already exists.
appModel.refreshRecents()
openWindow(id: WindowID.welcome)
dismissWindow(id: WindowID.board, value: ref)
}
private func releaseAccess() {
access?.stop()
access = nil
}
/// **The skip notice** (01: "the opened board carries a warning-tone notice naming the skipped
/// items, each with Reveal in Finder").
///
/// Written from the walk's own `LoadWarning.userSkipped` entries rather than from the surface's
/// skip set, deliberately: what the notice owes the user is what actually left the board, and a
/// skip for a defect that repaired itself between the decision and the walk names nothing.
///
/// **Only the open that carried the skips posts it.** A second window onto the same board
/// acquires the store that already exists, whose warnings still describe the first open and a
/// notice repeated per window would be the app reporting one decision twice. `skipping` is
/// non-empty for exactly the open that made the decision.
private func postSkipNoticeIfNeeded(store: BoardStore, skipping: Set<String>) {
guard !skipping.isEmpty else { return }
let root = store.rootURL
let items = store.loadWarnings.compactMap { warning -> RevealTarget? in
guard case let .userSkipped(path) = warning else { return nil }
return RevealTarget(path: path, url: root.appendingPathComponent(path))
}
store.banners.postSkippedOnOpen(items)
}
/// **The half of the wiring a window needs before it has a board** everything here is about
/// the *window*, and nothing here mentions the store, which is exactly the split
/// 02-architecture.md's loading state forces: this runs before the walk, and
+11 -4
View File
@@ -88,7 +88,12 @@ struct RestoreBootstrapView: View {
for board in appModel.boardRegistry.restorables() {
switch board {
case let .available(_, url):
appModel.openBoard(at: url)
// **The one restored open in the app** (01-storage-format.md § Malformed input, the
// decision surface): a board that fails here keeps today's retire-to-welcome-row
// landing "launch never chains dialogs", and nobody is sitting in front of a
// restoration waiting to repair four boards at once. The row's retry click is the
// attended open that then shows the surface.
appModel.openBoard(at: url, origin: .restored)
attempted += 1
case let .unavailable(record):
Self.logger.error("a flagged board could not be restored — its bookmark no longer resolves")
@@ -109,9 +114,11 @@ struct RestoreBootstrapView: View {
///
/// **Which board is the launch arguments' to say** (`UITestLaunch.variant`), and this method does
/// not care: the malformed variant is built and opened exactly like the other two, and its
/// failure arrives one layer down as the *loader's* a board window that records fail-fast's own
/// sentence and dismisses itself (`BoardWindowHost.start`). Special-casing it here would replace
/// the sentence under test with a sentence about the fixture.
/// failure arrives one layer down as the *loader's*. It opens **attended**, like every other
/// board a person asks for, so its refusal transforms the loading window into the decision surface
/// (`BoardWindowHost.handleWalkFailure`) rather than retiring which is precisely what the
/// fail-fast UI pass is there to see. Special-casing it here would replace the behaviour under
/// test with a behaviour about the fixture.
///
/// **A failure to *build* lands on welcome as an ordinary launch failure**, with the fixture's own
/// path on it. That is deliberate: a suite whose fixture failed to build would otherwise audit an