Agent guide v6 - warn against lane-glob moves
A real agent incident (2026-07-29): moving cards with mv <lane>/* swept the lane's own index.md along with the card folders, overwriting the destination lane's identity file and leaving the source lane index-less (the Implementation board briefly lost its lane titles; restored from its auto-commit history). Both guide generations taught the correct named-folder form but never said why it is load-bearing. v6 adds the one-folder-at-a-time bullet to Moving and reordering: a lane folder holds its own index.md beside its cards, so a glob sweeps the identity file with them. Boards heal to v6 on their next open per the first-line marker rule. The guide tests now derive their expected marker and current/newer versions from AgentGuide.version instead of a hardcoded literal, so the next bump cannot silently break them. 1669 green on both schemes. Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -95,7 +95,7 @@ struct AgentGuideMarkerTests {
|
||||
@Test("The guide the app ships carries the current marker in its first line")
|
||||
func shippedGuideCarriesTheMarker() {
|
||||
let firstLine = String(AgentGuide.content.prefix { !$0.isNewline })
|
||||
#expect(firstLine.contains("lanework-agent-guide v5"))
|
||||
#expect(firstLine.contains("lanework-agent-guide v\(AgentGuide.version)"))
|
||||
#expect(AgentGuide.installedVersion(of: AgentGuide.content) == AgentGuide.version)
|
||||
// Files the app creates end with LF (01-storage-format.md § Encoding and line endings).
|
||||
#expect(AgentGuide.content.hasSuffix("\n"))
|
||||
@@ -152,8 +152,8 @@ struct AgentGuideDecisionTests {
|
||||
/// "Never downgraded" — a newer app version may have written it.
|
||||
@Test("Current and newer are left alone")
|
||||
func currentAndNewerAreLeftAlone() {
|
||||
#expect(AgentGuide.decide(state(.file(text: guideText(version: 5)))) == .leaveAlone)
|
||||
#expect(AgentGuide.decide(state(.file(text: guideText(version: 6)))) == .leaveAlone)
|
||||
#expect(AgentGuide.decide(state(.file(text: guideText(version: AgentGuide.version)))) == .leaveAlone)
|
||||
#expect(AgentGuide.decide(state(.file(text: guideText(version: AgentGuide.version + 1)))) == .leaveAlone)
|
||||
#expect(AgentGuide.decide(state(.file(text: AgentGuide.content))) == .leaveAlone)
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ struct AgentGuideStoreTests {
|
||||
store.refreshAgentGuide()
|
||||
|
||||
let written = try String(decoding: fixture.data(AgentGuide.filename), as: UTF8.self)
|
||||
#expect(written.prefix { !$0.isNewline }.contains("lanework-agent-guide v5"))
|
||||
#expect(written.prefix { !$0.isNewline }.contains("lanework-agent-guide v\(AgentGuide.version)"))
|
||||
#expect(written == AgentGuide.content)
|
||||
#expect(!fixture.exists(AgentGuide.userFilename))
|
||||
#expect(brackets.begins == 1, "one bracket — one app-mediated reload, one commit")
|
||||
@@ -232,9 +232,10 @@ struct AgentGuideStoreTests {
|
||||
func currentGuideIsNotRewritten() throws {
|
||||
let fixture = try makeBoard()
|
||||
defer { fixture.tearDown() }
|
||||
// Deliberately *not* the app's own text: a v5 marker is the whole gate, and a file carrying
|
||||
// one must survive verbatim even when its body differs from what this version would write.
|
||||
let url = try writeRoot(AgentGuide.filename, Data(guideText(version: 5).utf8), in: fixture)
|
||||
// Deliberately *not* the app's own text: a current marker is the whole gate, and a file
|
||||
// carrying one must survive verbatim even when its body differs from what this version
|
||||
// would write.
|
||||
let url = try writeRoot(AgentGuide.filename, Data(guideText(version: AgentGuide.version).utf8), in: fixture)
|
||||
let before = try stat(url)
|
||||
let store = try BoardStore(rootURL: fixture.root)
|
||||
let brackets = GuideBracketLog()
|
||||
|
||||
Reference in New Issue
Block a user