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:
2026-08-08 22:22:02 -04:00
parent 05ae703989
commit cdc91d669d
5 changed files with 162 additions and 5 deletions
+12
View File
@@ -369,6 +369,17 @@ public enum BoardWriter: Sendable {
/// `index.md`, after it, so the file that makes a folder a board is written first and a failure
/// to seed can never leave a half-made board. Seeding is `seedGitignoreIfAbsent`'s, so a
/// creation into a folder that somehow already carries one leaves it alone.
/// - **And with a current agent guide** (08-agent-integration.md The agent guide; ruled
/// 2026-08-09, extending the option-C recommendation to every `createBoard` caller): right
/// after the `.gitignore` seed, `AgentGuide.install(atBoardRoot:)` runs too, so a board never
/// lives even a moment without a current-version `CLAUDE.md` no dependency on a later Mac
/// open to write the first copy. `KanbanMobile`'s `BoardIndexStore.createBoard` calls this same
/// method, so a phone-made board gets the identical guarantee for free. Routed through
/// `install` rather than a hand-rolled write on purpose: that is what carries over the guide's
/// own promises (never-downgrade, the `CLAUDE.user.md` rescue, squatter displacement, and
/// `EchoLedger.current?.markHeal(at:)`'s attribution exclusion) to this new call site without
/// re-deriving any of them. A failure here is a genuine `BoardWriteError` and throws like any
/// other step; there is no new banner or notice class for it.
/// - Parameter operation: what the *caller* was doing, for the banner's sake `.createBoard` for
/// every gesture that makes a board, and `.mintBoardIndex` for the decision surface's repair of
/// a board folder that has everything except the file that says it is one
@@ -402,6 +413,7 @@ public enum BoardWriter: Sendable {
operation: operation
)
try seedGitignoreIfAbsent(atBoardRoot: rootURL)
try AgentGuide.install(atBoardRoot: rootURL)
}
/// Creates a lane in a board: mints a fresh lowercase-UUIDv4 folder directly under