Accept liberally, emit conservatively (764a4d4): the gate is 8-4-4-4-12
hex in any case and any UUID version — uuidgen and UUID().uuidString
print uppercase, and a strict lowercase gate would silently stray an
agent's standard-tool card. Identity comparison is UUID-value equality
everywhere: ItemID keeps its byte-faithful rawValue but equates and
hashes on the lowercased canonical form, and the writer's
import-boundary collision check canonicalizes, so a same-UUID arrival
spelled in another case remints instead of slipping past. The app still
mints only lowercase v4 and never renames to canonicalize.
Full suite 342 tests in 63 suites green. Two findings filed.
Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
Fixture boards
Golden fixture boards for the storage-contract test suite — real on-disk folder trees, not inline strings, so the same fixtures can later drive XCUITests via the --open-board launch hook.
Bundled into the unit-test target as a folder reference (see project.yml). Valid boards live under Valid/, fail-fast cases under Malformed/. Tests live in KanbanTests/FixtureBoardTests.swift.
Lane/card folder names are fixed literal lowercase-UUIDv4-shaped strings (never generated at test time), chosen so their lexicographic order matches the expected tie-break order — usually a leading digit (10000000-..., 20000000-..., …) so folder order reads the same as array-index order in the tests. That is a fixture-authoring convention, not the loader's gate: the identity predicate is shape-only (8-4-4-4-12 hex, any case, any version — 01-storage-format.md § Fractal layout ▸ Rules), and the case/version coverage lives in KanbanTests/BoardLoaderTests.swift rather than here.
Valid/ — one board per tolerated/valid case
| Board | Case |
|---|---|
rich-board.kanban |
A full-breadth well-formed board: 2 lanes, 3 cards, bodies, styling (background/icon/iconColor/width), unknown + reserved frontmatter keys, attachments/ and comments/ with real content. Also the board every index.md in the tree is round-tripped against. |
interrupted-create.kanban |
The motivating skip-not-error case: a UUID-shaped lane folder and a UUID-shaped card folder, each with no index.md yet (folder created, write not yet landed). |
non-uuid-strays.kanban |
Non-UUID-shaped folders at both lane and card depth, with and without index.md — name shape gates candidacy before the file is ever read. |
stray-files.kanban |
Stray (non-directory) files at board, lane, and card level — never level candidates, never warned about. |
tombstones.kanban |
A tombstoned lane and a tombstoned card, both still on disk and still in the snapshot, flagged (isDeleted) rather than removed. Also proves a tombstoned lane doesn't recursively flag its own un-deleted children. |
duplicate-order-tie-break.kanban |
Three cards sharing one order in one lane, and two lanes sharing one order — both broken by folder name, ascending. |
unknown-key-order.kanban |
Unknown/reserved frontmatter keys interleaved with schema-owned ones at board, lane, and card level — document.unknownFields must preserve exactly the order they were written in. |
coercion.kanban |
Lenient-field coercion and fallback: wrong-type scalars that coerce (title: 2048, iconColor: 42, background: 12345, width: "3") versus ones with no sensible reading that fall back to the default (title: [a, b], background: {x: 1}, width: 1.5), plus a deleted with an unusable timestamp that still tombstones. |
duplicate-top-level-keys.kanban |
A top-level key written twice — at board, lane (the strict order field), and card level. Last occurrence wins; not a fail-fast case (settled, newer than the original card text). Round-tripped to prove the earlier occurrence survives on disk, invisible only to reads. |
board-level-deleted.kanban |
A board-level deleted: key — legal per the frontmatter table but meaningless; ignored + warned, rest of the board loads normally. |
Malformed/ — one board per fail-fast case
Each board is minimal: one broken thing.
| Board | Case |
|---|---|
unparseable-yaml.kanban |
An unterminated flow sequence in the board's frontmatter. |
missing-schema.kanban |
Board root index.md has no schema key. |
schema-newer-than-app.kanban |
Board root schema: 2, newer than BoardLoader.supportedSchema. |
missing-order-lane.kanban |
A UUID-shaped lane index.md with no order key. |
missing-order-card.kanban |
A UUID-shaped card index.md with no order key. |
explicit-null-order.kanban |
A lane's order: key present with no value — reads as missing, not malformed. |
non-numeric-order.kanban |
A card's order: banana — present but not a number. |
board-root-missing-index.kanban |
The board root folder itself has no index.md. |