Convert the Writer's operation vocabulary to a closed enum

WriteOperation replaces free-form operation strings throughout
BoardWriter, per the settled rule in 02 § Write-failure surfacing: the
banner layer will switch exhaustively over it, so a new operation
without a banner rendering is a compile-time hole. Titles enrich at the
two points a document read makes them known (updateIndex, and the
move/copy pre-flight), so failures after the read name the item;
purge never reads and stays title-less. Free-form English survives
only in the diagnostic reason.

Full suite 300 tests in 56 suites green.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
This commit is contained in:
2026-07-26 20:03:42 -04:00
parent 0ab0e58412
commit abca29054c
4 changed files with 217 additions and 65 deletions
+5 -5
View File
@@ -131,17 +131,17 @@ struct WriteFidelityMinimalTouchTests {
try step("reorder", targeting: ["\(Ident.lane1)/\(Ident.card1)"]) {
try BoardWriter.updateIndex(
inItemFolder: fixture.url("\(Ident.lane1)/\(Ident.card1)"), operation: "reorder card"
inItemFolder: fixture.url("\(Ident.lane1)/\(Ident.card1)"), operation: .style(title: nil)
) { $0.set(FrontmatterKeys.order, to: .double(1536)) }
}
try step("style write", targeting: ["\(Ident.lane1)/\(Ident.card2)"]) {
try BoardWriter.updateIndex(
inItemFolder: fixture.url("\(Ident.lane1)/\(Ident.card2)"), operation: "set background"
inItemFolder: fixture.url("\(Ident.lane1)/\(Ident.card2)"), operation: .style(title: nil)
) { $0.set(FrontmatterKeys.background, to: .string("blue")) }
}
try step("rename", targeting: ["\(Ident.lane2)/\(Ident.card3)"]) {
try BoardWriter.updateIndex(
inItemFolder: fixture.url("\(Ident.lane2)/\(Ident.card3)"), operation: "rename card"
inItemFolder: fixture.url("\(Ident.lane2)/\(Ident.card3)"), operation: .style(title: nil)
) { $0.set(FrontmatterKeys.title, to: .string("Renamed Three")) }
}
try step("delete", targeting: ["\(Ident.lane2)/\(Ident.card4)"]) {
@@ -308,7 +308,7 @@ struct WriteFidelityUnknownKeyOrderTests {
"""
let folder = try fixture.item("card", original)
try BoardWriter.updateIndex(inItemFolder: folder, operation: "rename card") { document in
try BoardWriter.updateIndex(inItemFolder: folder, operation: .style(title: nil)) { document in
document.set(FrontmatterKeys.title, to: .string("After"))
}
@@ -349,7 +349,7 @@ struct WriteFidelityCompositeTests {
let card3 = try BoardWriter.createCard(inLane: lane1Folder, title: "Third")
for (id, body) in [(card1, "First body.\n"), (card2, "Second body.\n"), (card3, "Third body.\n")] {
try BoardWriter.updateIndex(
inItemFolder: lane1Folder.appendingPathComponent(id.rawValue), operation: "edit body"
inItemFolder: lane1Folder.appendingPathComponent(id.rawValue), operation: .style(title: nil)
) { $0.body = body }
}