Write the loader fixture suite, valid and malformed
18 golden fixture boards as real on-disk trees under Fixtures/ — ten valid/tolerated cases (rich board, interrupted create, non-UUID strays, stray files, tombstones, tie-breaks, unknown-key order, coercion, last-wins duplicates, board-level deleted) and eight fail-fast cases, each pinned to its exact error reason and path; the rich board's whole tree round-trips byte-identically. Pins two contract subtleties now recorded in the design: tombstone flags are not inherited (renderers walk ancestors) and last-wins resolves before order strictness. README gains the storage-foundation feature entry. 146 tests total. Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
This commit is contained in:
+5
@@ -0,0 +1,5 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: Also Business As Usual
|
||||
---
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: Business As Usual
|
||||
---
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
schema: 1
|
||||
title: Board With A Stray 'deleted' Key
|
||||
deleted: 2026-01-01T00:00:00Z
|
||||
---
|
||||
'deleted' is legal per the common frontmatter table but meaningless at
|
||||
board level — a board can't tombstone itself out of its own window. The
|
||||
loader ignores it (and logs a warning) rather than acting on it; the rest
|
||||
of the board loads completely normally.
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: 2048
|
||||
---
|
||||
An unquoted integer title coerces to its source text, "2048".
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 2048
|
||||
title: [a, b]
|
||||
---
|
||||
A sequence has no sensible string reading — malformed, falls back to the
|
||||
untitled placeholder.
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 3072
|
||||
iconColor: 42
|
||||
---
|
||||
An integer iconColor coerces to its source text, "42" — not a real color,
|
||||
but the value round-trips rather than being rejected.
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 4096
|
||||
background: {x: 1}
|
||||
---
|
||||
A mapping has no sensible string reading — malformed, falls back to no
|
||||
color.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 5120
|
||||
background: 12345
|
||||
---
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 6144
|
||||
title: Deleted With An Unusable Timestamp
|
||||
deleted: definitely-not-a-date
|
||||
---
|
||||
The user's intent to delete outranks the broken date — this card still
|
||||
tombstones, its deletion date just unknown.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: Width Coerces From A Quoted Number
|
||||
width: "3"
|
||||
---
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 2048
|
||||
title: Width Is Unusable — Not An Integer
|
||||
width: 1.5
|
||||
---
|
||||
A malformed width is preserved on disk and simply not used — never
|
||||
rejected, never rewritten.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
schema: 1
|
||||
title: Coercion And Lenient-Field Fallback
|
||||
---
|
||||
Schema-owned display fields coerce where a sensible reading exists (a
|
||||
wrong-type scalar reads as its source text) and fall back to their default
|
||||
where none does (a sequence/mapping, a non-integer width). A present but
|
||||
unusable 'deleted' timestamp still tombstones — presence outranks validity.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: Should Render First (folder starts with 1)
|
||||
---
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: Should Render Second (folder starts with 2)
|
||||
---
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: Should Render Third (folder starts with 3)
|
||||
---
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: Lane With Three Tied Cards
|
||||
---
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 2048
|
||||
title: Should Render Before Lane bbb... (same order, 'a' < 'b')
|
||||
---
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 2048
|
||||
title: Should Render After Lane aaa... (same order, 'b' > 'a')
|
||||
---
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
schema: 1
|
||||
title: Duplicate Order Values, Tie-Broken By Folder Name
|
||||
---
|
||||
Two siblings that share an 'order' render in a deterministic order —
|
||||
ascending 'order' first, folder name (lexicographic) as the tiebreak.
|
||||
This board exercises the tiebreak at both lane level and card level.
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 2048
|
||||
title: First Title
|
||||
title: Second Title
|
||||
---
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
order: 4096
|
||||
title: Dup Order Lane
|
||||
---
|
||||
Duplicate keys aren't special-cased by field — 'order' is a strict,
|
||||
schema-owned field and still resolves via last-wins at the document layer,
|
||||
before the loader ever validates it. This is NOT a fail-fast case.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
schema: 1
|
||||
title: Draft Name
|
||||
title: Final Name
|
||||
---
|
||||
A hand-edit that types 'title' twice — strict YAML would reject the file
|
||||
outright. The engine reads the last occurrence and keeps both on disk.
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: A Real Card
|
||||
---
|
||||
Body.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: A Real Lane
|
||||
---
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
schema: 1
|
||||
title: Interrupted Two-Step Create
|
||||
---
|
||||
The app creates a folder first and writes index.md a moment later — a crash
|
||||
or a killed app between those two steps must not brick the board.
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: Real Card
|
||||
---
|
||||
Body.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 9999
|
||||
title: This looks like a card but isn't named like one
|
||||
---
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: Real Lane
|
||||
---
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
schema: 1
|
||||
title: Non-UUID Strays At Every Depth
|
||||
---
|
||||
Name shape gates level detection — a folder that isn't UUIDv4-shaped is a
|
||||
stray, whether or not it holds an index.md, and whether it sits at lane
|
||||
depth or card depth.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 9999
|
||||
title: This looks like a lane but isn't named like one
|
||||
---
|
||||
Never rendered — 'todo-notes' isn't UUIDv4-shaped.
|
||||
+1
@@ -0,0 +1 @@
|
||||
not-a-real-png-just-a-placeholder-attachment
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
---
|
||||
schema: 1
|
||||
author: rzen
|
||||
created: 2026-07-21T08:30:00Z
|
||||
---
|
||||
Comments are enhanced-schema and out of scope for this version — this
|
||||
folder is here only to prove it's preserved verbatim and never descended
|
||||
into (cards are leaves; the loader never scans past index.md).
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
---
|
||||
schema: 1
|
||||
title: "Design the fixture taxonomy"
|
||||
order: 1024
|
||||
created: 2026-07-20T10:15:00Z
|
||||
modified: 2026-07-26T20:07:30Z
|
||||
modified-by: claude
|
||||
source: DESIGN/01-storage-format.md # inline comment on a plain scalar
|
||||
project: lanework
|
||||
sphere: work
|
||||
labels: [m1-storage-read]
|
||||
---
|
||||
Enumerate every fail-fast and tolerated case from the storage format and
|
||||
build one golden fixture board per case.
|
||||
|
||||
- attachments/ holds a sketch
|
||||
- comments/ holds a placeholder future-comment folder
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
---
|
||||
schema: 1
|
||||
title: Wire up the loader's stray tolerance
|
||||
order: 2048
|
||||
background: coral
|
||||
icon: flag.fill
|
||||
iconColor: orange
|
||||
---
|
||||
Plain, unquoted title this time — mixing styles on purpose.
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: Doing
|
||||
width: 2
|
||||
background: '#3478F6'
|
||||
icon: hammer.fill
|
||||
iconColor: blue
|
||||
---
|
||||
WIP limit: 3 cards. Pull from Backlog only when there's room.
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: 'Ship v1'
|
||||
created: 2026-06-01T12:00:00Z
|
||||
modified: 2026-07-15T09:00:00Z
|
||||
---
|
||||
Shipped. 🎉
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 2048
|
||||
title: Done
|
||||
background: green
|
||||
---
|
||||
Completed work lives here until someone clears it out.
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
schema: 1
|
||||
# board settings, hand-written
|
||||
title: "Rich Demo Board"
|
||||
created: 2026-07-01T09:00:00Z
|
||||
modified: 2026-07-26T16:41:38Z
|
||||
modified-by: claude
|
||||
background: "#1E1E1E"
|
||||
icon: rectangle.stack.fill
|
||||
iconColor: purple
|
||||
|
||||
project: lanework # agent overlay
|
||||
sphere: work
|
||||
labels: [feature, docs]
|
||||
template: {order: 3}
|
||||
# trailing note before close
|
||||
---
|
||||
A demo board exercising the full breadth of the storage contract: several
|
||||
lanes, several cards, body text, styling, and unknown/reserved frontmatter
|
||||
keys living alongside the schema-owned ones.
|
||||
|
||||
Nothing here is special-cased — it's an ordinary board.
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: A Card With Junk Inside It
|
||||
---
|
||||
Body.
|
||||
+1
@@ -0,0 +1 @@
|
||||
this whole subfolder is never scanned — cards are leaves
|
||||
+1
@@ -0,0 +1 @@
|
||||
scratch
|
||||
@@ -0,0 +1 @@
|
||||
draft notes
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: A Lane With Junk Beside It
|
||||
---
|
||||
@@ -0,0 +1 @@
|
||||
board readme
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
schema: 1
|
||||
title: Stray Files Everywhere
|
||||
---
|
||||
Stray files at board, lane, and card level are ignored — preserved
|
||||
verbatim, never rendered, never warned about (only directories are level
|
||||
candidates at all).
|
||||
@@ -0,0 +1 @@
|
||||
log line
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: A Living Card
|
||||
---
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 2048
|
||||
title: A Tombstoned Card
|
||||
deleted: 2026-07-10T08:00:00Z
|
||||
---
|
||||
Still on disk, at its recorded order, just hidden from the board.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: Still Here
|
||||
---
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
title: Not Itself Deleted
|
||||
---
|
||||
My lane is tombstoned but I am not — isDeleted keys only on my own
|
||||
'deleted' field, never inherited from an ancestor.
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 2048
|
||||
title: A Tombstoned Lane
|
||||
deleted: 2026-07-11T09:30:00Z
|
||||
---
|
||||
Everything beneath a tombstoned lane is hidden from the board too — but
|
||||
that's a rendering rule, not a loader rule: the loader still reports this
|
||||
lane's card structurally, un-flagged, since the card's own 'deleted' key
|
||||
is absent.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
schema: 1
|
||||
title: Tombstoned Lane And Card Still On Disk
|
||||
---
|
||||
Deletion writes 'deleted:' into frontmatter; the item never leaves disk.
|
||||
The loader keeps tombstoned items in the snapshot, flagged — hiding them
|
||||
from the board is a rendering concern, not a loading one.
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
---
|
||||
schema: 1
|
||||
labels: [bug, urgent]
|
||||
order: 1024
|
||||
assignees: [bob, carol]
|
||||
title: Card With Unknowns
|
||||
due: 2026-08-15
|
||||
remote: {system: github, id: 42}
|
||||
agent-scratch: notes here
|
||||
---
|
||||
Card body describing the bug. Unknown/reserved keys are interleaved with
|
||||
schema-owned ones on purpose, to prove ordering survives the interleave.
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
schema: 1
|
||||
order: 1024
|
||||
remote-state: syncing
|
||||
title: Lane With Unknowns
|
||||
assignees: [alice]
|
||||
due: 2026-08-01
|
||||
custom: xyz
|
||||
---
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
schema: 1
|
||||
project: lanework
|
||||
title: Unknown Key Ordering
|
||||
sphere: work
|
||||
template: {order: 5}
|
||||
labels: [x, y, z]
|
||||
custom-note: keep me
|
||||
---
|
||||
Unknown keys and their relative order are preserved verbatim, on every
|
||||
level — this is load-bearing for agent overlays (project:, sphere:,
|
||||
custom tags).
|
||||
Reference in New Issue
Block a user