Build the frontmatter engine with byte-perfect round-trip

Yams validates and reads; writes are surgical line-span edits over
retained raw text, so untouched bytes — unknown keys, comments, odd
formatting, bodies — round-trip identically by construction. Strict
schema/order readers distinguish valid/missing/malformed for the
loader's fail-fast; lenient fields preserve malformed values verbatim;
modified-by is schema-owned. Adds the Yams package and the fixture
folder-reference wiring. 44 tests (105 cases).

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
This commit is contained in:
2026-07-26 15:26:12 -04:00
parent 954c4b351d
commit cb6f6100a7
8 changed files with 1162 additions and 0 deletions
+586
View File
@@ -0,0 +1,586 @@
import Foundation
import Testing
@testable import Kanban
// MARK: - Fixtures
private enum Fixture {
static let rich = """
---
# board settings, hand-written
schema: 1
title: My Board
project: lanework # agent overlay
sphere: work
labels: [a, b, c]
template: {order: 3}
notes: |
line one
indented two
folded: >
wrapped
text
created: 2026-07-26T16:41:38Z
modified-by: claude
tagged: !!str 42
"quoted key": yes
# trailing note
---
Body text.
More body — with *markdown*.
"""
static let minimal = """
---
schema: 1
order: 1024
title: Thing
---
Body
"""
/// Verbatim copy of a real card written by an agent.
static let realCard = """
---
schema: 1
title: Build the frontmatter engine with byte-perfect round-trip
order: 3072
created: 2026-07-26T16:41:38Z
modified: 2026-07-26T19:06:55Z
modified-by: claude
source: DESIGN/01-storage-format.md
labels: [m1-storage-read]
---
Build the Frontmatter component.
**Design constraints:**
- index.md is YAML frontmatter between `---` delimiters, then a Markdown body.
"""
/// A flow mapping whose continuation sits at column 0 the shape that would fool a naive
/// "any `key:` at column 0 starts a field" scanner.
static let unindentedFlow = """
---
flow: {a: 1,
b: 2}
last: x
---
body
"""
}
private func parseError(_ text: String) -> FrontmatterError? {
do {
_ = try FrontmatterDocument.parse(text)
return nil
} catch {
return error
}
}
// MARK: - Round-trip
struct FrontmatterRoundTripTests {
@Test(arguments: [
Fixture.rich,
Fixture.minimal,
Fixture.realCard,
Fixture.unindentedFlow,
// body with a trailing newline
"---\nschema: 1\n---\nbody\n",
// body with several trailing newlines
"---\nschema: 1\n---\nbody\n\n\n",
// no body at all, closing delimiter unterminated
"---\nschema: 1\n---",
// no body, closing delimiter terminated
"---\nschema: 1\n---\n",
// empty frontmatter mapping
"---\n---\nbody\n",
// frontmatter that is only blank lines
"---\n\n\n---\nbody\n",
// frontmatter that is only comments
"---\n# nothing to see\n---\nbody\n",
// explicit empty flow mapping
"---\n{}\n---\nbody\n",
// top-level flow mapping
"---\n{schema: 1, order: 5}\n---\nbody\n",
// odd key order, blank lines between entries
"---\nzzz: last\n\norder: 5\n\n\nschema: 1\n---\nbody\n",
// block scalar keep/strip indicators
"---\nkeep: |+\n text\n\nstrip: |-\n text\n---\nbody\n",
// anchors and aliases
"---\nbase: &b\n x: 1\ncopy: *b\n---\nbody\n",
// quoted values with escapes and embedded delimiters
"---\ntitle: \"a \\\"quoted\\\" --- thing\"\nother: 'single ---'\n---\nbody\n",
// a --- inside a block scalar (indented, so not a delimiter)
"---\nnote: |\n ---\n still the value\n---\nbody\n",
// delimiter line with trailing spaces
"--- \nschema: 1\n--- \nbody\n",
// no trailing newline on the body
"---\nschema: 1\n---\nbody without newline",
// CRLF throughout
"---\r\nschema: 1\r\n---\r\nbody\r\n",
])
func serializingAnUntouchedDocumentIsByteIdentical(text: String) throws {
let document = try FrontmatterDocument.parse(text)
#expect(document.serialized() == text)
}
@Test func bodyIsEverythingAfterTheClosingDelimiter() throws {
let document = try FrontmatterDocument.parse(Fixture.rich)
#expect(document.body == "Body text.\n\nMore body — with *markdown*.")
}
@Test func emptyBodyIsEmpty() throws {
#expect(try FrontmatterDocument.parse("---\nschema: 1\n---\n").body == "")
#expect(try FrontmatterDocument.parse("---\nschema: 1\n---").body == "")
}
@Test func bodyTrailingNewlinesAreNotNormalized() throws {
let document = try FrontmatterDocument.parse("---\nschema: 1\n---\nbody\n\n\n")
#expect(document.body == "body\n\n\n")
}
}
// MARK: - Malformed input
struct FrontmatterMalformedTests {
@Test func missingOpeningDelimiter() {
#expect(parseError("schema: 1\n---\nbody\n") == .missingOpeningDelimiter)
#expect(parseError("") == .missingOpeningDelimiter)
#expect(parseError("\n---\nschema: 1\n---\n") == .missingOpeningDelimiter)
#expect(parseError("----\nschema: 1\n---\n") == .missingOpeningDelimiter)
#expect(parseError("--- yaml\nschema: 1\n---\n") == .missingOpeningDelimiter)
}
@Test func missingClosingDelimiter() {
#expect(parseError("---\nschema: 1\nbody\n") == .missingClosingDelimiter)
#expect(parseError("---\n") == .missingClosingDelimiter)
#expect(parseError("---") == .missingClosingDelimiter)
}
@Test(arguments: [
"---\nfoo: [1, 2\n---\nbody\n",
"---\nschema: 1\n\tindented-with-tab: 2\n---\nbody\n",
"---\nschema: 1\n bad: indent\n---\nbody\n",
"---\ndupe: 1\ndupe: 2\n---\nbody\n",
"---\n\"unterminated: 1\n---\nbody\n",
"---\nfoo: {a: 1\n---\nbody\n",
"---\n*undefined-alias\n---\nbody\n",
])
func unparseableYAML(text: String) {
guard case .unparseableYAML = parseError(text) else {
Issue.record("expected .unparseableYAML for \(text.debugDescription), got \(String(describing: parseError(text)))")
return
}
}
@Test func unparseableYAMLCarriesAFileRelativeLineNumber() {
guard case let .unparseableYAML(message, line) = parseError("---\nschema: 1\n bad: indent\n---\n") else {
Issue.record("expected .unparseableYAML")
return
}
#expect(line == 3)
#expect(!message.isEmpty)
}
@Test(arguments: [
"---\n- a\n- b\n---\nbody\n",
"---\njust a scalar\n---\nbody\n",
"---\n42\n---\nbody\n",
"---\n[1, 2]\n---\nbody\n",
])
func frontmatterNotAMapping(text: String) {
#expect(parseError(text) == .frontmatterNotAMapping)
}
@Test(arguments: [
"---\n---\nbody\n",
"---\n\n---\nbody\n",
"---\n# just a comment\n---\nbody\n",
"---\n{}\n---\nbody\n",
"---\nnull\n---\nbody\n",
"---\n~\n---\nbody\n",
])
func emptyFrontmatterIsAnEmptyMapping(text: String) throws {
let document = try FrontmatterDocument.parse(text)
#expect(document.keys.isEmpty)
#expect(document.serialized() == text)
}
@Test func wellFormedButUnusualYAMLIsAccepted() throws {
for text in [Fixture.rich, Fixture.unindentedFlow, "---\nbase: &b\n x: 1\ncopy: *b\n---\n"] {
#expect(parseError(text) == nil)
}
}
}
// MARK: - Generic access
struct FrontmatterAccessTests {
@Test func keysAreInDocumentOrder() throws {
let document = try FrontmatterDocument.parse(Fixture.rich)
#expect(document.keys == [
"schema", "title", "project", "sphere", "labels", "template",
"notes", "folded", "created", "modified-by", "tagged", "quoted key",
])
}
@Test func realCardReadsAsExpected() throws {
let document = try FrontmatterDocument.parse(Fixture.realCard)
#expect(document.schema == .valid(1))
#expect(document.order == .valid(3072))
#expect(document.title == .valid("Build the frontmatter engine with byte-perfect round-trip"))
#expect(document.modifiedBy == .valid("claude"))
#expect(document.created.value != nil)
#expect(document.unknownFields.map(\.key) == ["source", "labels"])
#expect(document.body.hasPrefix("Build the Frontmatter component.\n"))
}
@Test func unknownKeysExcludeSchemaOwnedOnes() throws {
let document = try FrontmatterDocument.parse(Fixture.rich)
#expect(document.unknownFields.map(\.key) == [
"project", "sphere", "labels", "template", "notes", "folded", "tagged", "quoted key",
])
}
@Test func modifiedByIsExposedDistinctlyAndIsNotAnUnknownKey() throws {
let document = try FrontmatterDocument.parse(Fixture.rich)
#expect(document.modifiedBy == .valid("claude"))
#expect(!document.unknownFields.contains { $0.key == "modified-by" })
#expect(document.contains("modified-by"))
}
@Test func reservedKeysAreOrdinaryUnknownKeys() throws {
let text = """
---
schema: 1
labels: [bug, ui]
assignees: [ann]
due: 2026-08-01
remote: {kind: gitea}
remote-state: synced
template: {order: 2}
---
body
"""
let document = try FrontmatterDocument.parse(text)
#expect(document.unknownFields.map(\.key)
== ["labels", "assignees", "due", "remote", "remote-state", "template"])
#expect(document.serialized() == text)
}
@Test func parsedValuesAreAvailableForUnknownKeys() throws {
let document = try FrontmatterDocument.parse(Fixture.rich)
#expect(document.value(for: "labels") == .sequence([.string("a"), .string("b"), .string("c")]))
#expect(document.value(for: "template")
== .mapping([YAMLValue.Pair(key: .string("order"), value: .int(3))]))
#expect(document.value(for: "quoted key") == .bool(true))
#expect(document.value(for: "tagged") == .string("42"))
#expect(document.value(for: "nope") == nil)
}
@Test func rawValuePreservesBlockScalarsVerbatim() throws {
let document = try FrontmatterDocument.parse(Fixture.rich)
#expect(document.rawValue(for: "notes") == "|\n line one\n indented two")
#expect(document.rawValue(for: "labels") == "[a, b, c]")
#expect(document.rawValue(for: "schema") == "1")
}
}
// MARK: - Strict fields
struct FrontmatterStrictFieldTests {
private func document(_ frontmatter: String) throws -> FrontmatterDocument {
try FrontmatterDocument.parse("---\n\(frontmatter)\n---\nbody\n")
}
@Test func schemaValidMissingMalformed() throws {
#expect(try document("schema: 1").schema == .valid(1))
#expect(try document("title: x").schema == .missing)
#expect(try document("schema:").schema == .missing)
#expect(try document("schema: banana").schema == .malformed(raw: "banana"))
#expect(try document("schema: 1.5").schema == .malformed(raw: "1.5"))
#expect(try document("schema: [1]").schema == .malformed(raw: "[1]"))
}
@Test func orderValidMissingMalformed() throws {
#expect(try document("order: 1024").order == .valid(1024))
#expect(try document("order: 1536.5").order == .valid(1536.5))
#expect(try document("order: -1024").order == .valid(-1024))
#expect(try document("schema: 1").order == .missing)
#expect(try document("order:").order == .missing)
}
@Test func nonNumericOrderIsMalformedNotNil() throws {
let malformed = try document("order: banana").order
#expect(malformed == .malformed(raw: "banana"))
#expect(malformed.value == nil)
#expect(malformed.isMalformed)
#expect(!malformed.isMissing)
#expect(malformed.rawText == "banana")
#expect(try document("order: \"1024\"").order == .malformed(raw: "\"1024\""))
#expect(try document("order: [1]").order == .malformed(raw: "[1]"))
}
}
// MARK: - Lenient fields
struct FrontmatterLenientFieldTests {
private func document(_ frontmatter: String) throws -> FrontmatterDocument {
try FrontmatterDocument.parse("---\n\(frontmatter)\n---\nbody\n")
}
@Test func wellFormedLenientValues() throws {
#expect(try document("title: My Board").title == .valid("My Board"))
#expect(try document("background: \"#ff8800\"").background == .valid("#ff8800"))
#expect(try document("background: slate").background == .valid("slate"))
#expect(try document("icon: tray.full").icon == .valid("tray.full"))
#expect(try document("iconColor: teal").iconColor == .valid("teal"))
#expect(try document("width: 3").width == .valid(3))
#expect(try document("width: 1").width == .valid(1))
}
@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]"))
}
@Test func widthIsLenientForAnythingButAnIntegerAtLeastOne() throws {
#expect(try document("width: 0").width == .malformed(raw: "0"))
#expect(try document("width: -3").width == .malformed(raw: "-3"))
#expect(try document("width: 1.5").width == .malformed(raw: "1.5"))
#expect(try document("width: wide").width == .malformed(raw: "wide"))
#expect(try document("schema: 1").width == .missing)
}
@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)
#expect(document.serialized() == text)
#expect(document.background.isMalformed)
#expect(document.width.isMalformed)
#expect(document.icon.isMalformed)
}
@Test func timestampsReadPlainAndQuoted() throws {
let expected = Date(timeIntervalSince1970: 1_785_084_098) // 2026-07-26T16:41:38Z
#expect(try document("created: 2026-07-26T16:41:38Z").created == .valid(expected))
#expect(try document("modified: \"2026-07-26T16:41:38Z\"").modified == .valid(expected))
#expect(try document("deleted: 2026-07-26T16:41:38Z").deleted == .valid(expected))
#expect(try document("schema: 1").deleted == .missing)
#expect(try document("created: never").created == .malformed(raw: "never"))
}
}
// MARK: - Surgical edits
struct FrontmatterEditTests {
@Test func settingOneKeyLeavesEveryOtherByteIdentical() throws {
var document = try FrontmatterDocument.parse(Fixture.rich)
document.set("title", to: .string("Renamed"))
let output = document.serialized()
let expected = Fixture.rich.replacingOccurrences(of: "title: My Board", with: "title: Renamed")
#expect(output == expected)
// Prefix and suffix around the edited span are untouched.
let marker = "title: "
let originalPrefix = Fixture.rich.prefix(while: { _ in true }).components(separatedBy: marker)[0]
#expect(output.hasPrefix(originalPrefix))
#expect(output.hasSuffix("\nMore body — with *markdown*."))
}
@Test func settingAKeyPreservesItsTrailingCommentsAndBlankLines() throws {
let text = "---\nschema: 1\nproject: lanework # agent overlay\n\n# a note\norder: 5\n---\nbody\n"
var document = try FrontmatterDocument.parse(text)
document.set("project", to: .string("kanban"))
#expect(document.serialized()
== "---\nschema: 1\nproject: kanban\n\n# a note\norder: 5\n---\nbody\n")
}
@Test func appendingANewKeyGoesBeforeTheClosingDelimiter() throws {
var document = try FrontmatterDocument.parse(Fixture.minimal)
document.set("modified", to: .date(Date(timeIntervalSince1970: 1_785_084_098)))
#expect(document.serialized() == """
---
schema: 1
order: 1024
title: Thing
modified: 2026-07-26T16:41:38Z
---
Body
""")
#expect(document.keys == ["schema", "order", "title", "modified"])
}
@Test func appendingToEmptyFrontmatter() throws {
var document = try FrontmatterDocument.parse("---\n---\nbody\n")
document.set("schema", to: .int(1))
#expect(document.serialized() == "---\nschema: 1\n---\nbody\n")
#expect(document.schema == .valid(1))
}
@Test func removingAKeyDeletesOnlyItsLines() throws {
var document = try FrontmatterDocument.parse(Fixture.minimal)
document.remove("order")
#expect(document.serialized() == "---\nschema: 1\ntitle: Thing\n---\nBody")
#expect(document.order == .missing)
#expect(document.keys == ["schema", "title"])
}
@Test func removingAKeyKeepsTrailingComments() throws {
var document = try FrontmatterDocument.parse("---\na: 1\n# note about b\nb: 2\n---\nbody\n")
document.remove("a")
#expect(document.serialized() == "---\n# note about b\nb: 2\n---\nbody\n")
}
@Test func removingAnAbsentKeyIsANoOp() throws {
var document = try FrontmatterDocument.parse(Fixture.minimal)
document.remove("nope")
#expect(document.serialized() == Fixture.minimal)
}
@Test func editingAKeyWhoseValueWasABlockScalar() throws {
var document = try FrontmatterDocument.parse(Fixture.rich)
document.set("notes", to: .string("flattened"))
#expect(document.serialized().contains("notes: flattened\nfolded: >\n"))
#expect(!document.serialized().contains("line one"))
#expect(document.value(for: "notes") == .string("flattened"))
// Everything after the edited span is untouched.
#expect(document.serialized().hasSuffix("More body — with *markdown*."))
}
@Test func editingAKeyAfterAnUnindentedFlowContinuation() throws {
var document = try FrontmatterDocument.parse(Fixture.unindentedFlow)
document.set("last", to: .string("y"))
#expect(document.serialized() == "---\nflow: {a: 1,\nb: 2}\nlast: y\n---\nbody")
}
@Test func editingAQuotedKeyKeepsItsOriginalSpelling() throws {
var document = try FrontmatterDocument.parse("---\n\"quoted key\": one\n---\nbody\n")
document.set("quoted key", to: .string("two"))
#expect(document.serialized() == "---\n\"quoted key\": two\n---\nbody\n")
}
@Test func buildingADocumentFromScratch() throws {
var document = FrontmatterDocument(body: "The card body.\n")
document.set("schema", to: .int(1))
document.set("order", to: .double(1024))
document.set("title", to: .string("New card"))
let text = document.serialized()
#expect(text == "---\nschema: 1\norder: 1024\ntitle: New card\n---\nThe card body.\n")
#expect(try FrontmatterDocument.parse(text).serialized() == text)
}
@Test func settingTheBodyReplacesOnlyTheBody() throws {
var document = try FrontmatterDocument.parse(Fixture.rich)
document.body = "Brand new body.\n"
let output = document.serialized()
#expect(output.hasSuffix("---\nBrand new body.\n"))
#expect(output.hasPrefix("---\n# board settings, hand-written\nschema: 1\n"))
#expect(!output.contains("More body"))
}
@Test func editsSurviveReparsing() throws {
var document = try FrontmatterDocument.parse(Fixture.rich)
document.set("order", to: .double(2048))
document.remove("modified-by")
document.set("title", to: .string("Renamed"))
let reparsed = try FrontmatterDocument.parse(document.serialized())
#expect(reparsed.order == .valid(2048))
#expect(reparsed.modifiedBy == .missing)
#expect(reparsed.title == .valid("Renamed"))
#expect(reparsed.unknownFields.map(\.key)
== ["project", "sphere", "labels", "template", "notes", "folded", "tagged", "quoted key"])
#expect(reparsed.serialized() == document.serialized())
}
}
// MARK: - Scalar emission
struct FrontmatterEmissionTests {
private func emittedTitle(_ value: FrontmatterValue) throws -> String {
var document = try FrontmatterDocument.parse("---\n---\n")
document.set("title", to: value)
return String(document.serialized().dropFirst("---\ntitle: ".count).dropLast("\n---\n".count))
}
@Test func numbersAndTimestampsAreEmittedPlain() throws {
#expect(try emittedTitle(.int(42)) == "42")
#expect(try emittedTitle(.double(1024)) == "1024")
#expect(try emittedTitle(.double(1536.5)) == "1536.5")
#expect(try emittedTitle(.bool(true)) == "true")
#expect(try emittedTitle(.date(Date(timeIntervalSince1970: 1_785_084_098))) == "2026-07-26T16:41:38Z")
}
@Test func ordinaryStringsAreEmittedPlain() throws {
#expect(try emittedTitle(.string("My Board")) == "My Board")
#expect(try emittedTitle(.string("Fix login — round 2")) == "Fix login — round 2")
#expect(try emittedTitle(.string("a/b?c")) == "a/b?c")
}
@Test(arguments: [
"",
" padded ",
"true",
"yes",
"null",
"~",
"42",
"1.5",
"2026-07-26T16:41:38Z",
"has: a colon",
"trailing colon:",
"hash # comment",
"#ff8800",
"- leading dash",
"[bracketed]",
"{braced}",
"line\nbreak",
"tab\there",
"quote\"inside",
"back\\slash",
"*anchor",
"&anchor",
"!tagged",
"%directive",
"@at",
"|pipe",
">gt",
"'single'",
])
func stringsNeedingQuotingSurviveAReparse(value: String) throws {
var document = try FrontmatterDocument.parse("---\n---\n")
document.set("title", to: .string(value))
let reparsed = try FrontmatterDocument.parse(document.serialized())
#expect(reparsed.title == .valid(value))
}
@Test func quotingIsUsedOnlyWhenNeeded() throws {
#expect(try emittedTitle(.string("true")) == "\"true\"")
#expect(try emittedTitle(.string("#ff8800")) == "\"#ff8800\"")
#expect(try emittedTitle(.string("")) == "\"\"")
#expect(try emittedTitle(.string("a\nb")) == "\"a\\nb\"")
// An interior quote is legal in a plain scalar; a leading one is not.
#expect(try emittedTitle(.string("say \"hi\"")) == "say \"hi\"")
#expect(try emittedTitle(.string("\"quoted\"")) == "\"\\\"quoted\\\"\"")
#expect(try emittedTitle(.string("back\\slash")) == "back\\slash")
}
@Test func rawValuesAreWrittenVerbatim() throws {
var document = try FrontmatterDocument.parse("---\nschema: 1\n---\nbody\n")
document.set("template", to: .raw("{order: 7}"))
#expect(document.serialized() == "---\nschema: 1\ntemplate: {order: 7}\n---\nbody\n")
#expect(document.value(for: "template")
== .mapping([YAMLValue.Pair(key: .string("order"), value: .int(7))]))
}
}