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
+109 -47
View File
@@ -3,29 +3,35 @@ import XCTest
/// **A board that will not load, from the outside** (01-storage-format.md § Malformed input;
/// 02-architecture.md § Launch and window lifecycle).
///
/// ### The claim
///
/// > A restored board that fails surfaces on welcome, row-level: its window doesn't open; welcome
/// > appears alongside whatever did restore, the failed board's recents row carrying fail-fast's
/// > specifics (load error) never a silent drop.
/// ### The claims, as they stand after the decision surface
///
/// The `malformed` fixture is a well-formed board with exactly one unparseable card `index.md`
/// (`UITestLaunch.malformedIndexText` a frontmatter flow sequence that is never closed). Building
/// it succeeds; loading it must not, and *how* it fails is the whole of this file:
///
/// 1. **No board window.** Not an empty one, not one with the good lanes in it fail-fast is
/// all-or-nothing, so a partial board on screen would be the worse failure.
/// 2. **Welcome, loudly.** The recents row for that board wears the loader's own sentence, which
/// names the offending file. A row that fell back to "Unavailable", or to a count, would be the
/// app declining to say what it found.
/// 3. **Nothing repaired.** The bytes on disk are the bytes the fixture wrote. The loader is a pure
/// function of the tree and writes nothing, ever the Repair precedent so a board it refused
/// must still be refusable, byte for byte.
/// 1. **The window stays, and explains itself.** The fixture opens through `AppModel.openBoard`, which
/// is an **attended** open and 01 (settled 2026-07-31) makes an attended refusal the decision
/// surface's case: "one aggregated surface hosted by the pre-snapshot loading window the loading
/// content transforms in place, never a sheet over a spinner". So there *is* a window, it is the
/// one that was opening, and what it shows names the offending file.
/// 2. **No board behind it.** Fail-fast is all-or-nothing: the surface is a decision, not a partial
/// board, so the good lanes are not on screen underneath it.
/// 3. **Cancel is the old landing, on demand.** "**Cancel** aborts the open: the window retires and
/// the board lands row-level on welcome, record-before-load unchanged" which is the sequence
/// this file used to assert *unconditionally*, now reached by the user's own choice.
/// 4. **Nothing repaired.** The bytes on disk are the bytes the fixture wrote. The loader is a pure
/// function of the tree and writes nothing, ever the Repair precedent and the surface writes
/// only what the user chooses, which here is nothing.
///
/// **What changed and why**: claims 1 and 2 used to read "no board window, welcome instead". That was
/// the whole landing for *every* refusal before this milestone; it is now the **restored** landing
/// (`RestoreBootstrapView`) and the environmental one. The fixture launch is neither it opens a
/// board the way a double-click does.
///
/// ### Where each claim is checked
///
/// The first two are here, because they are about *windows* and a window is what a unit test does not
/// have. The third is checked **both** here and in `KanbanTests` unconditionally there
/// The first three are here, because they are about *windows* and a window is what a unit test does
/// not have. The fourth is checked **both** here and in `KanbanTests` unconditionally there
/// (`UITestMalformedFixtureBoardTests`, which builds the fixture and re-reads the tree), and
/// opportunistically here, because reaching the app's container from the runner depends on how the
/// app under test was signed and installed. Where the container is not reachable this file says so
@@ -37,47 +43,107 @@ final class FailFastLaunchTests: XCTestCase {
continueAfterFailure = false
}
/// The whole of claims 1 and 2, in one launch: no board window, welcome instead, and the row
/// carrying the loader's specifics.
/// Claims 1 and 2: the surface appears in the board's own window, names the file, and shows no
/// board.
@MainActor
func testMalformedBoardFailsLoudlyAndOpensNoWindow() throws {
func testMalformedBoardShowsTheDecisionSurface() throws {
let app = XCUIApplication.launched(with: .malformed)
// Welcome is where a failed open lands (`BoardWindowHost.start`: record the failure, refresh
// the recents, open welcome, dismiss the board window).
// The surface, by the identifier it carries for exactly this
// (`BoardDecisionSurface.accessibilityIdentifier`).
let surface = app.descendants(matching: .any)["decision-surface"]
XCTAssertTrue(
app.windows["Welcome to Lanework"].waitForExistence(timeout: XCUIApplication.uiTimeout),
"the welcome window did not appear after a failed open"
surface.waitForExistence(timeout: XCUIApplication.uiTimeout),
"an attended open of a refusing board did not transform into the decision surface"
)
// Claim 1. Checked *after* welcome has appeared, so this is "the board window never came",
// not "the board window has not come yet".
XCTAssertFalse(
// It is the *opening window* that hosts it the loading content transformed in place, so the
// window is still called what the loading state called it.
XCTAssertTrue(
app.windows[FixtureBoard.malformed.windowTitle].exists,
"a board window opened for a board the loader rejected"
"the surface is not in the board's own window"
)
XCTAssertFalse(
app.windows["Welcome to Lanework"].exists,
"an attended refusal must not retire — that is the restored landing"
)
// Claim 2. The row is one combined accessibility element name, location, caption and the
// caption is `BoardLoadError.description`: "lane/card/index.md: unparseable YAML at line
// N: ". The UUIDs in that path are minted at launch and unknowable here, so the assertion is
// on the parts that are the *app's* to keep stable: the offending file is named, and the
// reason is stated.
// The class section for unparseable frontmatter, and the specifics on its row. The UUIDs in
// the path are minted at launch and unknowable here, so the assertion is on the parts that are
// the *app's* to keep stable: the offending file is named, and the reason is stated.
XCTAssertTrue(
app.element(labelContaining: "index.md").waitForExistence(timeout: XCUIApplication.uiTimeout),
"no welcome row named the offending index.md — fail-fast's specifics did not reach the surface"
app.descendants(matching: .any)["decision-section-unreadable-frontmatter"].exists,
"the defect was not grouped into its class section"
)
XCTAssertTrue(
app.element(labelContaining: "index.md").exists,
"no row named the offending index.md — fail-fast's specifics did not reach the surface"
)
XCTAssertTrue(
app.element(labelContaining: "unparseable YAML").exists,
"the welcome row did not say why the board was refused"
"the row did not say what is wrong with the file"
)
// And it is the malformed board's own row that says it.
// Claim 2: a decision, not a partial board. The fixture's intact lane is not on screen.
XCTAssertFalse(
app.element(labelContaining: "A good card").exists,
"a partial board rendered behind the surface — fail-fast is all-or-nothing"
)
}
/// Claim 3: Cancel is the old landing, reached by the user's own choice the window retires and
/// the board lands row-level on welcome with fail-fast's specifics on it.
@MainActor
func testCancelRetiresToWelcomeWithTheSpecifics() throws {
let app = XCUIApplication.launched(with: .malformed)
// Queried across every element type rather than as `app.buttons[...]`: the surface's footer
// sits inside a `.contain` accessibility group, and which AX type a SwiftUI `Button` lands on
// there is not something a test should be asserting about in passing.
let cancel = app.descendants(matching: .any)["decision-cancel"]
XCTAssertTrue(
app.element(labelContaining: FixtureBoard.malformed.windowTitle).exists,
cancel.waitForExistence(timeout: XCUIApplication.uiTimeout),
"the surface did not appear, so Cancel could not be pressed"
)
cancel.click()
XCTAssertTrue(
app.windows["Welcome to Lanework"].waitForExistence(timeout: XCUIApplication.uiTimeout),
"Cancel did not land on welcome"
)
XCTAssertFalse(
app.windows[FixtureBoard.malformed.windowTitle].exists,
"the window did not retire"
)
// The row is one combined accessibility element name, location, caption and the caption is
// `BoardLoadFailure.description`. Matched on *value* as well as label: welcome's rows are
// list cells whose combined text lands in the AX value, not in a label (see
// `Self.element(textContaining:in:)`).
XCTAssertTrue(
Self.element(textContaining: "unparseable YAML", in: app)
.waitForExistence(timeout: XCUIApplication.uiTimeout),
"the welcome row did not carry fail-fast's specifics"
)
XCTAssertTrue(
Self.element(textContaining: FixtureBoard.malformed.windowTitle, in: app).exists,
"the failure did not land on the failed board's row"
)
}
/// Claim 3, twice over: the malformed bytes survive the refusal, and a second launch is refused
/// The first element whose **label or value** contains `fragment`.
///
/// `XCUIApplication.element(labelContaining:)` matches the label alone, which is the right question
/// for the decision surface's own rows (each is one combined element with an explicit
/// accessibility label) and the wrong one for welcome's recents rows: those are list cells whose
/// name-location-caption text arrives as the cell's AX *value*.
@MainActor
private static func element(textContaining fragment: String, in app: XCUIApplication) -> XCUIElement {
app.descendants(matching: .any)
.matching(NSPredicate(format: "label CONTAINS %@ OR value CONTAINS %@", fragment, fragment))
.firstMatch
}
/// Claim 4, twice over: the malformed bytes survive the refusal, and a second launch is refused
/// the same way rather than opening a board the app quietly fixed.
///
/// The relaunch is not redundant with the byte check it is what the byte check *means* from the
@@ -86,8 +152,8 @@ final class FailFastLaunchTests: XCTestCase {
func testMalformedBoardIsNeverRepaired() throws {
let app = XCUIApplication.launched(with: .malformed)
XCTAssertTrue(
app.windows["Welcome to Lanework"].waitForExistence(timeout: XCUIApplication.uiTimeout),
"the welcome window did not appear after a failed open"
app.descendants(matching: .any)["decision-surface"].waitForExistence(timeout: XCUIApplication.uiTimeout),
"the surface did not appear"
)
// The bytes, where the runner can reach them. `NSTemporaryDirectory()` inside the sandboxed
@@ -101,7 +167,7 @@ final class FailFastLaunchTests: XCTestCase {
)
XCTAssertTrue(
malformed.contains("order: [1024"),
"the malformed frontmatter was repaired — fail-fast must not write"
"the malformed frontmatter was repaired — nothing here mints a rewrite of unparseable YAML"
)
} else {
// Not a failure, and not silence either: the run says which half of the claim it made.
@@ -120,16 +186,12 @@ final class FailFastLaunchTests: XCTestCase {
app.terminate()
let second = XCUIApplication.launched(with: .malformed)
XCTAssertTrue(
second.windows["Welcome to Lanework"].waitForExistence(timeout: XCUIApplication.uiTimeout),
"the second launch did not reach welcome"
)
XCTAssertFalse(
second.windows[FixtureBoard.malformed.windowTitle].exists,
second.descendants(matching: .any)["decision-surface"].waitForExistence(timeout: XCUIApplication.uiTimeout),
"the second launch opened the board the first one refused"
)
XCTAssertTrue(
second.element(labelContaining: "index.md").waitForExistence(timeout: XCUIApplication.uiTimeout),
"the second launch did not name the offending file"
second.element(labelContaining: "unparseable YAML").exists,
"the second launch did not name the same defect"
)
}
+7 -4
View File
@@ -22,8 +22,9 @@ enum FixtureBoard {
/// lot of it.
case large
/// A well-formed board with exactly one unparseable card `index.md`, for the fail-fast pass. It
/// is the only variant whose board window is *expected* never to appear.
/// A well-formed board with exactly one unparseable card `index.md`, for the fail-fast pass. Its
/// window is the only one that never holds a board: the open is attended, so the refusal
/// transforms the loading content into the decision surface (`FailFastLaunchTests`).
case malformed
/// `UITestLaunch.fixtureFlag` plus this variant's own flag. Both, always: the first is what "this
@@ -149,8 +150,10 @@ extension XCUIApplication {
/// Launches the app on `board` the audit fixture unless told otherwise and waits for its
/// window.
///
/// **Not for `.malformed`**, which has no window to wait for: that variant is launched with
/// `launched(with:)` and the caller waits for welcome instead.
/// **Not for `.malformed`**, whose window never holds a board: since the decision surface (01
/// § Malformed input, settled 2026-07-31) that variant's window does appear carrying the
/// surface rather than lanes so the wait below would pass while saying nothing. That variant is
/// launched with `launched(with:)` and the caller waits for the surface instead.
@MainActor
static func launchedWithFixtureBoard(_ board: FixtureBoard = .standard) -> XCUIApplication {
let app = launched(with: board)