The embedded guide catches up on its own — creation-time parity for both apps, an open-time refresh for the phone
The card's frozen spec recommended Option C (install the agent guide at board creation); the owner's follow-up comment extended that ruling to a second axis — embedded guidelines should update whenever a board opens if the on-disk version is older than Lanework's, which the Mac app already does via BoardStore.refreshAgentGuide()/runScheduledHeals(). This card implements both halves. - BoardWriter.createBoard now calls AgentGuide.install(atBoardRoot:) right after seedGitignoreIfAbsent, so every board — Mac- or phone-created, since KanbanMobile.BoardIndexStore.createBoard calls this same method — is born with a current-version CLAUDE.md, with no dependency on a later open. Routed through AgentGuide.install itself rather than a hand-rolled write, so never-downgrade, the CLAUDE.user.md rescue, squatter displacement, and the EchoLedger heal-attribution exclusion all carry over unchanged. - BoardSession (KanbanMobile) gains a private refreshAgentGuideOnce(), fired once from open() (already idempotent on the .idle phase), fire-and-forget through the same CoordinatedFileAccess.write bracket every phone write uses. Deliberately not a heal scheduler — a one-shot courtesy check at session open, silent on failure (logged, never surfaced to lastError or a banner), matching AgentGuide's own "nothing here is a user-facing event" posture. The type's doc comment now names this one exception while keeping "no heal scheduler" true. - project.yml: lifted the KanbanMobile target's AgentGuide.swift build exclusion (dating to the original mobile MVP, "agents work where the Mac app runs") — both changes above fail to compile on the phone without it, since the type simply wasn't in that module. Verified safe: AgentGuide.swift imports only Foundation, and its one upward dependency touches only EchoLedger's unconditional recording API, never the #if os(macOS)-gated consumer surfaces. Tests: KanbanTests/BoardWriterTests.swift gains createBoardInstallsTheCurrentAgentGuide, calling createBoard directly and asserting the guide lands at AgentGuide.version immediately — the card's own Done-when, and also the phone's creation-time coverage since it's the same call site. KanbanMobileUITests/AgentGuideUITests.swift covers the open-time refresh itself, the one piece only reachable end-to-end from a running KanbanMobile process (no mobile unit-test target exists): the bundle's fixture board already carries no CLAUDE.md, so tapping into it and polling disk proves the wiring with no fixture changes needed. Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -822,6 +822,26 @@ struct BoardWriterCreateBoardTests {
|
||||
let document = try FrontmatterDocument.parse(fixture.indexText("Board.kanban"))
|
||||
#expect(document.rawValue(for: FrontmatterKeys.created) == document.rawValue(for: FrontmatterKeys.modified))
|
||||
}
|
||||
|
||||
/// **Creation-time parity** (ruled 2026-08-09, extending Option C to every `createBoard` caller):
|
||||
/// a board is born with a current-version `CLAUDE.md`, with no dependency on a later Mac open to
|
||||
/// write the first copy — the gap `AgentGuideStoreTests` doesn't cover, since every fixture there
|
||||
/// hand-writes its tree and calls `store.refreshAgentGuide()` directly rather than going through
|
||||
/// `createBoard`. `KanbanMobile.BoardIndexStore.createBoard` calls this exact method, so this one
|
||||
/// assertion is also the phone's coverage — there is no second code path to test twice.
|
||||
@Test("createBoard installs a current-version agent guide, with no dependency on a later open")
|
||||
func createBoardInstallsTheCurrentAgentGuide() throws {
|
||||
let fixture = try WriterFixture()
|
||||
defer { fixture.tearDown() }
|
||||
let root = fixture.url("MyBoard.kanban")
|
||||
|
||||
try BoardWriter.createBoard(at: root, title: "My Board")
|
||||
|
||||
let guideText = try String(decoding: fixture.data("MyBoard.kanban/\(AgentGuide.filename)"), as: UTF8.self)
|
||||
#expect(AgentGuide.installedVersion(of: guideText) == AgentGuide.version)
|
||||
#expect(guideText == AgentGuide.content, "the exact shipped guide, not a hand-rolled write")
|
||||
#expect(!fixture.exists("MyBoard.kanban/\(AgentGuide.userFilename)"), "nothing existed to rescue at a brand-new root")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Create lane / card
|
||||
|
||||
Reference in New Issue
Block a user