Realign code with the 2026-07-29 findings-resolution rulings
Nine rulings land as code. Reorders don't stamp — one container-change predicate (WriteOperation.rewritesOrderOnly): within-container reorders and the renumber rescale rewrite only order, while cross-lane, cross-board, and trash moves stamp modified and clear modified-by; no trash special case exists, and the m8 undo inverses conform through the same seam. Copies are transactions: the root-strict/nested-lenient split retires for a whole-subtree stampability preflight that refuses loudly naming the offender, and every item-level copy severs remote/remote-state at every level (whole-board forks carry them verbatim). Paste refuses, never degrades: the embedded-index.md materialization and its loss row retire; a missing staged snapshot produces nothing and posts an error-tone one-shot named from manifest metadata. Coerce-tier fallbacks log through the Defect stream with path context attached loader-side. Displacement is level-uniform: a file squatting attachments inside a card heals by the same rename ladder as board-root squatters; comments stays tolerated. Delete Immediately joins card and lane context menus as Delete's ⌥-alternate with its own VO custom action, routed through an explicit container so the menu target outranks standing selection. Agent guide v7 teaches the stamp discipline and the card-level attachments claim, and sheds two stale v6 lines (lanes trash now; kind is taught). Verified conformant, unchanged: edition-aware Undo/Redo disable, trash marquee full-height backdrop. Both schemes 1854 tests / 318 suites green; verify-editions 30/30. Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -828,3 +828,130 @@ struct BoardLoaderEncodingTests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - The coerce tier's trace (01-storage-format.md § Frontmatter, ruled 2026-07-29)
|
||||
|
||||
/// **"A no-sensible-reading fallback logs"** — field, path, and raw text, carried as coerce-tier
|
||||
/// entries in the integrity service's Defect stream:
|
||||
///
|
||||
/// > the one place where an observed-in-the-wild shape can later be promoted to a heuristic heal or a
|
||||
/// > notice; no banner, no behavior change.
|
||||
///
|
||||
/// So these tests assert two things at once, and the second matters as much as the first: the fallback
|
||||
/// is *reported*, and nothing about the board changed because of it — the field still renders its
|
||||
/// default, the bytes on disk are still verbatim, and no heal is scheduled.
|
||||
@Suite("BoardLoader ▸ coerce-tier fallbacks")
|
||||
struct BoardLoaderCoercionTraceTests {
|
||||
|
||||
/// The pure half first: which lenient fields report, and which deliberately do not.
|
||||
///
|
||||
/// `schema` and `order` are the **refuse** tier — a malformed one fails the load loudly, so there is
|
||||
/// no silent recovery to leave a trace of — and `deleted`'s rule is presence-not-validity, so
|
||||
/// nothing falls back to a default there either.
|
||||
@Test("The document reports its lenient fallbacks, and only those")
|
||||
func theDocumentReportsItsLenientFallbacks() throws {
|
||||
let document = try FrontmatterDocument.parse("""
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: [a, b]
|
||||
width: 1.5
|
||||
created: not-a-date
|
||||
icon: {a: b}
|
||||
deleted: also-not-a-date
|
||||
---
|
||||
Body.
|
||||
""")
|
||||
|
||||
let byKey = Dictionary(uniqueKeysWithValues: document.coercedFields.map { ($0.key, $0.raw) })
|
||||
#expect(Set(byKey.keys) == ["title", "width", "created", "icon"])
|
||||
#expect(byKey["width"] == "1.5", "the raw text as written — what a future heuristic would read")
|
||||
#expect(byKey["created"] == "not-a-date")
|
||||
#expect(byKey["deleted"] == nil, "presence, not validity, decides a tombstone")
|
||||
}
|
||||
|
||||
@Test("A clean document reports nothing")
|
||||
func aCleanDocumentReportsNothing() throws {
|
||||
let document = try FrontmatterDocument.parse("""
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: Fine
|
||||
width: 2
|
||||
---
|
||||
""")
|
||||
#expect(document.coercedFields.isEmpty)
|
||||
}
|
||||
|
||||
/// **A scalar of the wrong type is not a fallback** — it coerces to the text the author typed
|
||||
/// (`title: 2048` reads as "2048"), which is a *successful* reading and leaves no trace. Only "no
|
||||
/// sensible reading exists" does.
|
||||
@Test("A coerced scalar leaves no trace — it was read, not defaulted")
|
||||
func aCoercedScalarLeavesNoTrace() throws {
|
||||
let document = try FrontmatterDocument.parse("---\nschema: 1\ntitle: 2048\nwidth: \"3\"\n---\n")
|
||||
#expect(document.title.value == "2048")
|
||||
#expect(document.width.value == 3)
|
||||
#expect(document.coercedFields.isEmpty)
|
||||
}
|
||||
|
||||
/// The loader's half: the path is attached at every level, because the rule is about fields and
|
||||
/// every level has them.
|
||||
@Test("The loader attaches the path, at every level")
|
||||
func theLoaderAttachesThePath() throws {
|
||||
let fixture = try BoardFixture()
|
||||
defer { fixture.tearDown() }
|
||||
let lane = uuidFolderName()
|
||||
let card = uuidFolderName()
|
||||
let trashed = uuidFolderName()
|
||||
try fixture.index("", "schema: 1\ntitle: [a, b]\n")
|
||||
try fixture.index(lane, "schema: 1\norder: 1024\nwidth: 1.5\n")
|
||||
try fixture.index("\(lane)/\(card)", "schema: 1\norder: 1024\nicon: {x: y}\n")
|
||||
try fixture.index(".trash/\(trashed)", "schema: 1\norder: 1024\ncreated: nope\n")
|
||||
|
||||
let reported = try BoardLoader.load(boardRoot: fixture.root).coercedFrontmatter
|
||||
let byPath = Dictionary(uniqueKeysWithValues: reported.map { ($0.path, $0.fields.map(\.key)) })
|
||||
|
||||
#expect(byPath["index.md"] == ["title"])
|
||||
#expect(byPath["\(lane)/index.md"] == ["width"])
|
||||
#expect(byPath["\(lane)/\(card)/index.md"] == ["icon"])
|
||||
#expect(byPath[".trash/\(trashed)/index.md"] == ["created"])
|
||||
}
|
||||
|
||||
/// **No behavior change** — the whole point of the tier. The fields render their defaults exactly as
|
||||
/// they did before anything was reported, and the bytes are preserved verbatim.
|
||||
@Test("Nothing about the board changes — defaults render, bytes stay")
|
||||
func nothingChanges() throws {
|
||||
let fixture = try BoardFixture()
|
||||
defer { fixture.tearDown() }
|
||||
let lane = uuidFolderName()
|
||||
try fixture.index("", "schema: 1\ntitle: Board\n")
|
||||
try fixture.index(lane, "schema: 1\norder: 1024\ntitle: [a, b]\nwidth: 0.5\n")
|
||||
let before = try Data(contentsOf: fixture.root.appendingPathComponent("\(lane)/index.md"))
|
||||
|
||||
let result = try BoardLoader.load(boardRoot: fixture.root)
|
||||
let loaded = try #require(result.model.lanes.first)
|
||||
|
||||
#expect(loaded.title.isMalformed, "the field still reads as malformed")
|
||||
#expect(loaded.title.value == nil, "and renders its default — the untitled placeholder")
|
||||
#expect(loaded.width.value == nil, "width falls back to 1 at the render layer, not here")
|
||||
#expect(try Data(contentsOf: fixture.root.appendingPathComponent("\(lane)/index.md")) == before,
|
||||
"read-side only: the loader never writes")
|
||||
#expect(result.warnings.isEmpty, "a coercion is not a stray warning")
|
||||
}
|
||||
|
||||
/// **It is not healable work**, which is why it has no class: a class is a memo key and a
|
||||
/// banner-posture row in the engine, and inventing one would arm a memo against a repair nobody
|
||||
/// wrote. The other defects keep theirs.
|
||||
@Test("A coerce-tier defect has no heal class, and signs per field")
|
||||
func itHasNoHealClass() {
|
||||
let defect = IntegrityRules.Defect.coercedFrontmatter(CoercedFrontmatter(
|
||||
path: "lane/card/index.md",
|
||||
fields: [CoercedField(key: "width", raw: "1.5"), CoercedField(key: "icon", raw: "{}")]
|
||||
))
|
||||
#expect(defect.healClass == nil)
|
||||
#expect(Set(defect.signatures) == [
|
||||
"coerce:lane/card/index.md:width",
|
||||
"coerce:lane/card/index.md:icon",
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user