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:
@@ -244,6 +244,25 @@ public final class BoardStore: HealHost {
|
||||
/// describe the tree currently on screen.
|
||||
public private(set) var loadWarnings: [LoadWarning]
|
||||
|
||||
/// **The skips this open consented to** — the decision surface's Skip set, riding the *session*
|
||||
/// (01-storage-format.md § Malformed input, ruled 2026-07-31; the posture settled here rather
|
||||
/// than left to each reload).
|
||||
///
|
||||
/// The ruling makes skips **per-open decisions, never persisted** — "the next open of a
|
||||
/// still-broken board presents the surface again" — and this is what "per open" means once the
|
||||
/// board is on screen: every reload of this session passes the same set, so the consent the user
|
||||
/// gave when the window opened holds for as long as that window does. The alternative — a reload
|
||||
/// that dropped the set — would blank the board on the first foreign filesystem event, because
|
||||
/// the defect the user tolerated is still on disk and would fail the walk again.
|
||||
///
|
||||
/// Nothing writes it and nothing persists it: it arrives at `init` from the open that composed
|
||||
/// it, and dies with the store, which is the ruling's own "not a stored preference".
|
||||
///
|
||||
/// Empty on every ordinary board — no surface, no skips — which is what makes the reload path
|
||||
/// below byte-identical to what it was for every board that opens cleanly.
|
||||
@ObservationIgnored
|
||||
public let skippedPaths: Set<String>
|
||||
|
||||
/// **The pending work the load that produced `snapshot` found** — the typed defect stream
|
||||
/// (`IntegrityRules.Defect`, settled 2026-07-29). Replaced with the snapshot, like
|
||||
/// `loadWarnings`, so it always describes the tree currently on screen.
|
||||
@@ -602,9 +621,9 @@ public final class BoardStore: HealHost {
|
||||
/// with a reload behind it rather than a write into a board nothing is watching yet. A store
|
||||
/// built directly (a test, a storeless consumer) heals when it is asked to, and on every reload
|
||||
/// thereafter.
|
||||
public convenience init(rootURL: URL) throws(BoardLoadFailure) {
|
||||
let result = try BoardLoader.load(boardRoot: rootURL)
|
||||
self.init(rootURL: rootURL, loaded: result)
|
||||
public convenience init(rootURL: URL, skipping: Set<String> = []) throws(BoardLoadFailure) {
|
||||
let result = try BoardLoader.load(boardRoot: rootURL, skipping: skipping)
|
||||
self.init(rootURL: rootURL, loaded: result, skipping: skipping)
|
||||
}
|
||||
|
||||
/// The same board, from a walk that already happened somewhere else.
|
||||
@@ -618,11 +637,16 @@ public final class BoardStore: HealHost {
|
||||
/// `rootURL` is passed rather than read off `result.model` for the reason the property's own doc
|
||||
/// comment gives — the store's root follows an absorbed rename ahead of the snapshot that will
|
||||
/// carry it.
|
||||
public init(rootURL: URL, loaded result: LoadResult) {
|
||||
///
|
||||
/// - Parameter skipping: the skip set the walk was run with, retained for this session's reloads
|
||||
/// (`skippedPaths`). Defaulted to none, which is every board that opened without a decision
|
||||
/// surface.
|
||||
public init(rootURL: URL, loaded result: LoadResult, skipping: Set<String> = []) {
|
||||
self.rootURL = rootURL
|
||||
self.snapshot = result.model
|
||||
self.loadWarnings = result.warnings
|
||||
self.defects = result.defects
|
||||
self.skippedPaths = skipping
|
||||
self.reloadFailure = nil
|
||||
self.readOnlyLock = nil
|
||||
self.transient = TransientBoardState()
|
||||
@@ -693,6 +717,9 @@ public final class BoardStore: HealHost {
|
||||
// `Sendable` value that touches libgit2 only if this walk finds a duplicate identity to
|
||||
// break a tie for. `nil` everywhere the app manages no git.
|
||||
let historyRanker = makeIdentityHistoryRanker?()
|
||||
// **This session's consented skips, on every walk it runs** (`skippedPaths`): the open's
|
||||
// decision stands for the session, so a reload sees the board the user chose to open.
|
||||
let skipping = skippedPaths
|
||||
reloadInFlight = true
|
||||
Self.logger.debug("reload \(generation, privacy: .public) started (\(origin.rawValue, privacy: .public))")
|
||||
|
||||
@@ -701,7 +728,8 @@ public final class BoardStore: HealHost {
|
||||
// and the loader's typed failure is lost on the way into `Result`.
|
||||
let outcome: Result<LoadResult, BoardLoadFailure>
|
||||
do throws(BoardLoadFailure) {
|
||||
outcome = .success(try BoardLoader.load(boardRoot: root, historyRanker: historyRanker))
|
||||
outcome = .success(try BoardLoader.load(
|
||||
boardRoot: root, skipping: skipping, historyRanker: historyRanker))
|
||||
} catch {
|
||||
outcome = .failure(error)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user