Adopt read-side coercion for schema-owned fields

Design-review resolutions: schema-owned display fields coerce where a
sensible reading exists (wrong-type scalars read as source text, width
accepts exact-integer strings/doubles) and default where none does;
null reads as missing; duplicate keys last-one-wins and inline-comment
re-splicing recorded in the design (engine change follows). Strict
schema/order fail-fast unchanged. 85 tests green.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
This commit is contained in:
2026-07-26 15:47:33 -04:00
parent a6f4960492
commit 8e7c565e07
4 changed files with 107 additions and 22 deletions
+21
View File
@@ -100,6 +100,27 @@ struct BoardLoaderWellFormedTests {
#expect(result.warnings.isEmpty)
}
/// Tombstone semantics key on the `deleted` key's *presence*, not its validity
/// (01-storage-format.md § Frontmatter, § Deletion): a `deleted` value with no sensible
/// date reading still tombstones the user's intent to delete outranks the broken date.
@Test func malformedDeletedValueStillTombstonesLaneAndCard() throws {
let fixture = try BoardFixture()
defer { fixture.tearDown() }
try fixture.index("", "schema: 1\n")
try fixture.index("lane-1", "schema: 1\norder: 1024\ndeleted: yesterday\n")
try fixture.index("lane-1/card-1", "schema: 1\norder: 1024\ndeleted: yesterday\n")
let result = try BoardLoader.load(boardRoot: fixture.root)
let lane = try #require(result.model.lanes.first { $0.id.rawValue == "lane-1" })
#expect(lane.deleted == .malformed(raw: "yesterday"))
#expect(lane.isDeleted)
let card = try #require(lane.cards.first { $0.id.rawValue == "card-1" })
#expect(card.deleted == .malformed(raw: "yesterday"))
#expect(card.isDeleted)
}
@Test func tiesAreBrokenByFolderNameNotTitle() throws {
let fixture = try BoardFixture()
defer { fixture.tearDown() }
+35 -2
View File
@@ -345,11 +345,26 @@ struct FrontmatterLenientFieldTests {
#expect(try document("width: 1").width == .valid(1))
}
/// A scalar of the wrong YAML type still has a sensible string reading it coerces to the
/// source text the author typed (01-storage-format.md § Frontmatter). Only a sequence or
/// mapping no scalar to read at all is malformed.
@Test func scalarsOfTheWrongTypeCoerceToTheirSourceText() throws {
#expect(try document("title: 2048").title == .valid("2048"))
#expect(try document("title: true").title == .valid("true"))
#expect(try document("background: 42").background == .valid("42"))
#expect(try document("iconColor: true").iconColor == .valid("true"))
#expect(try document("icon: 2026-07-26T16:41:38Z").icon == .valid("2026-07-26T16:41:38Z"))
}
@Test func quotedStringLenientValuesAreUnaffectedByCoercion() throws {
#expect(try document("title: \"2048\"").title == .valid("2048"))
#expect(try document("title: \"true\"").title == .valid("true"))
#expect(try document("title: My Board").title == .valid("My Board"))
}
@Test func malformedLenientValuesArePreservedVerbatimAndDoNotThrow() throws {
#expect(try document("background: [red, blue]").background == .malformed(raw: "[red, blue]"))
#expect(try document("background: 42").background == .malformed(raw: "42"))
#expect(try document("icon: {a: 1}").icon == .malformed(raw: "{a: 1}"))
#expect(try document("iconColor: true").iconColor == .malformed(raw: "true"))
#expect(try document("title: [a, b]").title == .malformed(raw: "[a, b]"))
}
@@ -361,6 +376,17 @@ struct FrontmatterLenientFieldTests {
#expect(try document("schema: 1").width == .missing)
}
/// A string or double with an exact integer reading 1 coerces; a fractional reading,
/// zero, a negative, or non-numeric text still has none.
@Test func widthCoercesStringsAndWholeNumberDoubles() throws {
#expect(try document("width: \"2\"").width == .valid(2))
#expect(try document("width: 2.0").width == .valid(2))
#expect(try document("width: 2.7").width == .malformed(raw: "2.7"))
#expect(try document("width: 0").width == .malformed(raw: "0"))
#expect(try document("width: -1").width == .malformed(raw: "-1"))
#expect(try document("width: banana").width == .malformed(raw: "banana"))
}
@Test func malformedLenientValuesStillRoundTrip() throws {
let text = "---\nschema: 1\nbackground: [red, blue]\nwidth: 0\nicon: {a: 1}\n---\nbody\n"
let document = try FrontmatterDocument.parse(text)
@@ -378,6 +404,13 @@ struct FrontmatterLenientFieldTests {
#expect(try document("schema: 1").deleted == .missing)
#expect(try document("created: never").created == .malformed(raw: "never"))
}
/// A quoted ISO-8601 string is not YAML's implicit timestamp type it parses as `.string`
/// but still coerces to a valid date (01-storage-format.md § Frontmatter).
@Test func quotedISO8601StringCoercesToAValidDate() throws {
let expected = Date(timeIntervalSince1970: 1_767_323_045) // 2026-01-02T03:04:05Z
#expect(try document("created: \"2026-01-02T03:04:05Z\"").created == .valid(expected))
}
}
// MARK: - Surgical edits