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
+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