Give the registry live display-state write-through and record-before-load

BoardRecord carries icon/iconColor; recordOpen/recordClose stamp them with the
display name, and a displayStateDelegate on the store (wired in BoardWindowHost
beside onFrameChanged) syncs all three through BoardRegistry.syncDisplayState on
every successful reload — welcome rows now wear the board's own icon and follow
in-app renames live. recordOpen now runs before the load with the folder name as
a brand-new record's provisional display name, so a first open that fails
fail-fast still lands in recents carrying the failure row-level (02's rule); an
existing record's cached name survives a failing retry, and the welcome fallback
list remains only for failures naming no record at all.

Claude-Session: https://claude.ai/code/session_01CqjXB7ASoWtbyoGod68k97
This commit is contained in:
2026-07-27 20:44:30 -04:00
parent cacd48cb0f
commit f2d9f3ad07
10 changed files with 608 additions and 41 deletions
+50
View File
@@ -245,6 +245,56 @@ struct BoardStoreTests {
#expect(fixture.exists("\(Ident.lane2)/\(created.rawValue)/index.md"))
}
// MARK: Display-state write-through
@Test("A successful reload calls the display-state delegate, whether or not anything actually changed")
func successfulReloadCallsDisplayStateDelegate() async throws {
let fixture = try makeBoard()
defer { fixture.tearDown() }
let store = try BoardStore(rootURL: fixture.root)
var calls = 0
store.displayStateDelegate = { calls += 1 }
// An ordinary foreign change with no board-level field touched at all the "did title,
// icon, or iconColor actually change" comparison is the registry's own no-op guard
// (`BoardRegistry.syncDisplayState`), not something this store decides by diffing itself.
try fixture.item("\(Ident.lane1)/\(Ident.card3)", Item.rich(order: "3072", title: "Third"))
store.handleWatcherEvent(.treeChanged(.foreign))
await store.awaitQuiescence()
#expect(calls == 1)
}
@Test("A failed reload does not call the display-state delegate — there is no new snapshot to report")
func failedReloadDoesNotCallDisplayStateDelegate() async throws {
let fixture = try makeBoard()
defer { fixture.tearDown() }
let store = try BoardStore(rootURL: fixture.root)
var calls = 0
store.displayStateDelegate = { calls += 1 }
try fixture.item("\(Ident.lane1)/\(Ident.card1)", brokenIndex)
store.handleWatcherEvent(.treeChanged(.foreign))
await store.awaitQuiescence()
#expect(calls == 0)
}
@Test("A reload with no display-state delegate wired is harmless")
func reloadWithoutADisplayStateDelegateIsANoOp() async throws {
let fixture = try makeBoard()
defer { fixture.tearDown() }
let store = try BoardStore(rootURL: fixture.root)
try fixture.item("\(Ident.lane1)/\(Ident.card3)", Item.rich(order: "3072", title: "Third"))
store.handleWatcherEvent(.treeChanged(.foreign))
await store.awaitQuiescence()
#expect(cardTitles(inLane: Ident.lane1, of: store.snapshot) == ["First", "Second", "Third"])
}
// MARK: Wholesale operations
@Test("A wholesale operation whose reload succeeds leaves no lock and no leaked expectation")