Commit Graph
304 Commits
Author SHA1 Message Date
rzen 9818dfefbe Settle effective liveness for selection re-resolution; fix debounce attribution
Tombstoning a card's lane ejects the card from selection, drag
membership, and the pending cut - liveness is ancestor-walked, since
the card renders nowhere once 03 collapses the lane to a trash entry.
BoardStore's bullet now says it coalesces reloads (one walk in flight)
while the debounce lives in FolderWatcher, matching m3 as built.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 19:34:47 -04:00
rzen 68530ff569 Ratify the thrown-wholesale-operation arming rule into 02
The next-reload-or-lock expectation arms on every bracket exit, thrown
operations included - a mid-flight failure is when the tree's state is
least known, so the mandatory final reload runs regardless.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 19:29:04 -04:00
rzen 2ba6989481 Align FolderWatcher with the newly ratified design decisions
The parallel design session resolved the m3 watcher findings; two
changed behavior: .git internals are now filtered at any depth (a
nested clone or submodule is a stray whose internals never render),
and a root change landing mid-bracket is owned by the root-change
path — endBracket() skips its mandatory reload once a .rootChanged
tore the stream down, cleared when a fresh stream attaches. The skip
is scoped by an explicit flag, not by stream absence: a watcher whose
stream never came up still delivers post-bracket reloads, since
distrust of FSEvents delivery is the mandatory reload's whole reason.

Two new watcher tests; full suite 281 tests in 54 suites green.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 19:24:11 -04:00
rzen 66516d38d1 Build BoardStore — one-way flow and resilient reloads
Per-board @Observable MainActor hub (Kanban/LiveStore/): watcher signals
drive off-main tree walks with a generation guard, single-flight
coalescing (strongest pending origin, watcher's merge rule), and the
resilience contract — a failed reload never replaces a good snapshot,
per-file breakage never locks editing, and a wholesale operation
(performWholesale) arms a reload-must-succeed-or-lock floor so a failed
post-bracket reload flips the board read-only until a good reload heals
it. Selection is a pure UUID-set value re-resolved on every swap;
liveness flips eject. performWrite brackets the watcher so Writer
round-trips come back app-mediated.

14 store tests; full suite 279 tests in 54 suites green. Three findings
filed on the Redesign board.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 19:14:15 -04:00
rzen d32f03f8c9 Resolve eleven Redesign-board findings across the design corpus
Contradictions: v1 gains a minimal Settings pane (⌘,) housing the
restore-at-launch toggle; Window ▸ Welcome to Lanework inventoried in 11.
Gaps: close/quit waits for in-flight bracketed and copy-shaped operations;
pull extends the Edit-session treatment to open raw-source buffers
(signpost + last-writer-wins Apply); File ▸ Duplicate keeps tombstones
(01's "as-is" softened to per-flow exclusions). Minor: Undo routing
promoted to a real heading in 06; 11's dead "10 ▸ Actions" citation
repointed. m3 watcher findings: root-gone cancels armed debounced
delivery; mid-bracket root changes owned by the root-change path;
.git filtered at any depth; banked mid-bracket reconciles and the
200 ms debounce ratified.
2026-07-26 19:06:08 -04:00
rzen cadb62564c Build FolderWatcher — FSEvents with bracketed suppression
Debounced FSEvents watcher over the board tree (Kanban/LiveStore/):
trailing 200ms debounce coalesces bursts; nestable brackets suppress
delivery and close with exactly one app-mediated reload; origins merge
reconciling > appMediated > foreign; missed-events flags degrade to a
reconciling reload; WatchRoot vanish tears the stream down (streams die
and are recreated — reattach() covers rename re-resolution); board-root
.git churn is filtered against the symlink-resolved root. The FSEvents
release callback must be top-level and non-isolated — a MainActor
closure traps in dispatch_assert_queue during stream deallocation.

15 watcher tests (4 consecutive green runs); full suite 265 tests green.
Five design findings filed on the Redesign board.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 18:49:52 -04:00
rzen 048bb8d244 Reuse the pathfinder app icon
Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 18:11:55 -04:00
rzen 4dcfd636a4 Ratify m2 findings into DESIGN: line endings, uneditable shapes, renumber partial, error enum
Four minor design-review findings resolved: appended frontmatter lines
adopt the file's prevailing line ending; empty-but-not-blank frontmatter
joins the readable-but-uneditable shapes; an interrupted renumber's
per-file partial is accepted; the write-failure operation becomes a
closed enum carrying the item title.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 18:06:43 -04:00
rzen b11fb9d070 Ratify three m2 write-path findings into the storage format
- Appended frontmatter lines adopt the file's prevailing line ending
  (read off the opening delimiter) — one stamp never produces a
  mixed-ending file.
- Empty-but-not-blank frontmatter ({}, null, ~) joins the
  readable-but-uneditable shapes: zero keys load fine, but appending
  after that text would be unparseable YAML.
- Renumber is atomic per file, not per lane — an interrupted rescale
  is accepted the same way as the shared-board burst: valid floats,
  deterministic order, next renumber completes the job.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 18:06:28 -04:00
rzen 328cfb629f Write the write-path fidelity test suite
The executable spec for BoardWriter's cross-cutting guarantees, in a
dedicated WriteFidelityTests.swift: minimal-touch verified down to
sibling mtimes across a seven-operation session; renumber pinned as
the sole exception (whole lane rewritten, board root / other lanes /
tombstones untouched); delete→restore byte-precise (only the modified
line differs, zero tombstone residue); unknown-key order preserved
with keys interleaved among schema-owned ones; and a composite
building a board purely through the writer, hand-editing a file like
an agent would, then moving/copying/deleting/renumbering to a
zero-warning reload. Guarantees already pinned by the per-operation
suites (atomicity, move/copy identity, body round-trip) are
referenced, not duplicated. Shared fixtures promoted to
WriterTestSupport.swift. README gains the write-side feature bullet.

5 new tests; 250 total green. Closes milestone m2-storage-write.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 18:04:57 -04:00
rzen 840528d14c Implement attachment import and listing
importAttachments lands files into a card's attachments/ (created on
first import — the one folder the app ever makes there), never
overwriting and never refusing a name: collisions auto-rename
Finder-style, counting up from 2, with a same-named subfolder
blocking a name exactly like a file. Sources are validated before any
copy; a failed copy removes the partial destination and throws naming
the source file; a multi-file batch stops at the first failure with
earlier files staying landed. listAttachments is the flat surface:
top-level regular files only, Finder-sorted, subfolders and hidden
entries excluded and untouched; a missing attachments/ lists empty.

11 new unit tests; 245 total green.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 17:48:46 -04:00
rzen fa834501b5 Implement tombstone delete, restore, and purge
deleteItem writes deleted: <now> in place through updateIndex — the
folder never moves, nothing beneath it is touched (hiding the subtree
is the renderer's ancestor walk, not a stored flag). restoreItem
removes the key — position-perfect by construction, every duplicate
occurrence taken so a hand-written twin cannot resurrect the
tombstone. purgeItem physically removes the tree; an already-missing
folder is success (a Finder deletion converges on the same end
state), and no prior tombstone is required (Delete Immediately skips
the stage by design). Board-root deletion is structurally unreachable
via the UUID-shape guard shared with move/copy. Neither delete nor
restore polices liveness — re-deleting refreshes the timestamp,
restoring a live item is a harmless stamped rewrite.

14 new unit tests; 234 total green.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 17:42:22 -04:00
rzen eb9e1e413f Implement move and copy identity semantics
moveItem: physical folder move, UUID and created travel unchanged;
exactly one file rewritten (the moved root's order, stamped). Import
boundary detected by resolved board-root comparison; on a cross-board
move-in, arriving UUIDs colliding with any identity in the destination
board (tombstones included) are reminted per folder at the finest
grain — folder rename only, file bytes untouched, every repair
reported in MoveResult. A colliding root moves straight to its minted
name. A same-parent move degrades to a plain reorder, self excluded
from the appended-rank scan.

copyItem: whole-tree copy minting fresh UUIDs at every depth; .fork
keeps created while stamping modified and clearing modified-by, .born
(template instantiation) stamps created fresh too. Nested uneditable
or unreadable files copy byte-verbatim rather than blocking the
gesture; the root must be rewritable. All-or-nothing at the
destination — any failure removes the partial tree.

24 new unit tests; 220 total green.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 17:37:24 -04:00
rzen 8c0cec06ee Implement create operations for board, lane, and card
createBoard fills or mints a .kanban (or extension-less) folder and
refuses to clobber an existing board; createLane/createCard mint fresh
lowercase-UUIDv4 folders and append at max+1024 among visible siblings
via the same strict scan renumber uses (extracted, shared). New files
carry schema/title?/order?/created/modified — created==modified, no
modified-by — written LF through the same atomic temp+rename path.
The editability pre-flight is now scoped to operations that rewrite
siblings: renumber refuses on an uneditable sibling, a create beside
one proceeds. Schema constant unified on BoardLoader.supportedSchema.
Info.plist declares the .kanban document type (UTI conforming to
com.apple.package) so Finder treats a board as one document.

20 new unit tests; 196 total green.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 17:01:09 -04:00
rzen 19f67bdb78 Build BoardWriter — atomic, minimal-touch writes
The single point through which every mutation becomes a filesystem
operation: read fresh from disk (strict byte-faithful UTF-8), refuse
readable-but-uneditable frontmatter shapes, apply the edit through the
span engine, stamp modified / clear modified-by, then temp-file+rename
atomically. Renumber-visible-children is the sole minimal-touch
exception, tombstones untouched. Structured BoardWriteError carries
operation + path + reason for the future banner surface.

Alongside, three edges the card surfaced:
- The loader now decodes byte-faithfully too, so a BOM'd or non-UTF-8
  index.md is rejected at load per the encoding contract, instead of
  loading via NSString's silent BOM strip and then refusing every write.
- An appended frontmatter line adopts the file's prevailing line ending
  (a CRLF file stays uniformly CRLF when a stamp first lands in it).
- Empty-but-not-blank frontmatter ({}, null, ~) is detected as
  uneditable — appending after it would be unparseable YAML.

30 new unit tests; 176 total green.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 16:49:42 -04:00
rzen a131399c02 Minor doc fixes across the corpus (ten review findings)
- 10 cites 11 for Add Attachment and the lane-width menu items
- 04 uses ⌘/⇧ symbols for click modifiers; card moves scoped to the
  ⌥⌘ chord; ../WISHLIST.md path fixed
- 03 says stepper (plus the menu pair) in the editing-surfaces table
- 06 says one commit per debounce window
- 03/10 Reduce Motion lists aligned at five items; rubber-band
  disambiguated as the lane-resize feedback
- 11 requires the trash shown for Empty Trash
- 02 rewords and-peers

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 16:30:58 -04:00
rzen 1b530e1b9a Unwritable board locations enter the read-only lock at open
Fills the gap: open-time writability probe, cause-naming banner, guide
write skipped-with-log, re-probe on reconciling reloads clears the
lock. Fail loudly, specifically, once.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 16:29:18 -04:00
rzen 092d226df6 Inline rename: UUID tracking, vanish discards, live filter uncarved
Fills the gap: the rename editor follows its card by UUID through
foreign moves, discards silently when the target vanishes or flips
liveness, and a mid-search rename re-runs the predicate like any edit
— creation stays the search filter's only carve-out.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 16:27:51 -04:00
rzen 7ef3a64a49 Watcher recovery: reconciling reloads close every blind window
Fills the gap: wake/activation reconciling reloads, MustScanSubDirs and
overflow degrade to a full reload, dead streams are recreated at the
re-resolved root. Stale boards structurally excluded.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 16:26:58 -04:00
rzen 06d172a804 Clipboard staging lifecycle and loud degraded pastes
Fills the gap: eager staging at copy time, at most the current copy
kept (sweep at launch and each copy against pasteboard references,
surviving relaunch), and attachment-less fallback pastes surface a
one-shot banner naming the loss.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 16:25:27 -04:00
rzen 5fff6e8244 State the encoding and line-ending contract for index.md
Fills the gap, matching the built engine: strict UTF-8 no-BOM reads
(undecodable or BOM'd files fail fast loudly), BOM-less writes,
per-line ending preservation (CRLF stays CRLF), final newline
preserved, app-created files LF. Agent guide teaches the contract.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 16:24:41 -04:00
rzen 3d7457d80f Drop semantics under mid-drag foreign reloads
Fills the gap: resting zones recompute per snapshot (frozen inputs are
the dragged items' sizes and the pointer); proposals re-validate by
liveness with release-cancels-when-invalid; an emptied drag cancels
itself, partial vanishing drops survivors.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 16:23:03 -04:00
rzen 498d23e15e Renamed board root re-resolves by file identity mid-session
Fills the gap: on root-gone signals the bookmark re-resolves first —
success absorbs the rename transparently (watcher re-attach, URL and
window-key re-derive, full reload); only an unresolvable bookmark is
the vanished-root lock.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 16:22:12 -04:00
rzen 9fb5845a5b Raw-source buffers join the branch-switch settle step
Fills the gap: Save All applies each raw buffer (a validation failure
cancels the switch, focus on the offender), Discard exits without
writing, Cancel keeps everything. External checkouts remain accepted
last-writer-wins per the dirty-buffer rule.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 16:21:26 -04:00
rzen 7470255886 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
2026-07-26 16:20:47 -04:00
rzen e6c34c0ccf Failure paths for app-initiated git operations
Fills the gap: pull/push/switch/restore share the auto-committer's lock
posture with a visible waiting state; clean failures are one-shot
banner errors; the never-mutate rule exempts the app's own stamped
leftovers, which it aborts and reports.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 16:20:25 -04:00
rzen 607e1b837b The undo stack is HEAD's first-parent ancestry, live
Fills the gap: foreign commits — auto-committed agent work and agent
self-commits — push onto the in-session stack as steps, with a
pre-flight HEAD re-sync before every undo/redo. One rule in-session
and across relaunch; arriving commits clear redo.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 16:18:57 -04:00
rzen 03ec2f0879 Extend the read-only lock to card-window write paths
Fills the gap: the lock spans every window sharing the store — Edit
entry, raw-source Apply, attachments, sidebar actions, checkbox toggles
disable; drags out offer copy only. Open Edit buffers stay typable with
saves suspended; held text resolves by lock cause (switch/restore
settle first, post-pull buffer-wins, returned root saves normally).

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 16:16:49 -04:00
rzen 5af3680070 Undo restore gates on save-or-discard when its diff touches a session card
Fills the gap: restores materialize only the current-vs-target diff, so
untouched Edit sessions are unaffected; a touching restore reuses the
branch-switch Save All / Discard / Cancel step. Redo symmetric;
raw-source buffers follow the branch-switch settle treatment.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 16:15:32 -04:00
rzen 1f469cebcb Scope 09's no-silent-git rule to instantiation
Resolves the 09-vs-03 contradiction: the principle is never-init-
unasked, scoped to template instantiation; File ▸ Duplicate carries
.git deliberately as a history fork.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 16:12:00 -04:00
rzen 0b779470f2 Name the app-claimed board-root files in 01's stray-tolerance rule
Resolves the ownership contradiction: CLAUDE.md / CLAUDE.user.md (08)
and the git-init-seeded .gitignore (06) are app-claimed at board root —
the one scope on the preserved-verbatim promise agents read.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 16:11:28 -04:00
rzen 1337265dd7 Menu item mandatory, default chord where warranted — contract reworded
Resolves the contract-vs-practice contradiction: 04/10/11 now state the
menu home is what makes every board function keyboard-reachable and
remappable; 11's no-default rows are deliberate, not violations.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 16:10:37 -04:00
rzen 5a459f0ee4 Exclude inline comments from raw value reads
rawValue and the coercion/malformed paths now stop at the quote-aware
inline-comment boundary, so 'title: 2048  # note' coerces to "2048"
and a read-then-write no longer compounds the comment (pinned
idempotent). Strictly read-side; round-trip bytes untouched. +6 tests
(124 total).

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 16:10:32 -04:00
rzen 4085dd7a46 Engine — duplicate keys last-wins, inline-comment re-splice
Duplicates: Yams' duplicate-key error is disarmed by placeholder-
renaming first occurrences and re-composing (line count preserved, so
span layout stays exact); reads take the last occurrence, set()
rewrites the winner and clears earlier twins, remove() clears all.
Top-level only — nested duplicates stay YAML errors (doc updated).
Re-splice: an inline comment on a rewritten scalar line survives, with
quote-aware detection so a # inside quotes is never a comment. +31
tests (118 total).

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 16:07:06 -04:00
rzen 31f7691062 Gate level detection on UUID folder-name shape
Only lowercase-hex 8-4-4-4-12 folder names are lane/card candidates;
anything else is a stray — skipped with a distinct warning, never
descended, never able to fail-fast a load. UUID-shaped folders keep the
prior contract (missing index skips, malformed frontmatter fail-fasts).
Design resolution from the Redesign board. +5 tests.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 16:03:39 -04:00
rzen 30af21a66e Settle nine design-review items
Storage: UUID-shape gates level detection; readable-but-uneditable
frontmatter shapes load and refuse writes; duplicate keys last-wins;
inline-comment re-splice; null reads as missing. Architecture: the
banner surface specified (origin-window host, dismiss vs heal,
precedence stack, tones incl. in-progress rows and safe-copy Cancel);
launch/window lifecycle (restoration preference, welcome policy,
row-level restore failures); bracketed operations lock writes at the
failed-reload lock's scope.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 16:03:00 -04:00
rzen ca22d5459c Trash quasi-lane spans a fixed width unit; Show Trash re-divides
Resolves 03's internal conflict: the shown trash consumes one fixed
width unit (no width frontmatter, not resizable) and Show/Hide Trash
joins the re-divide trigger list — lane units + 1 while shown. Small
windows compress like any lane add.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 15:59:52 -04:00
rzen 8858a38d7d Name the drop proposal as drag reflow's animation key
Resolves 03 ▸ Motion's self-conflict: narrow keys are the sole-selected
card, the search query, and the drag's drop proposal (reflow-to-make-
room animates under it); animation-free means the replica and marquee's
1:1 cursor tracking and multi-select churn, not reflow.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 15:57:57 -04:00
rzen 22e53b6c88 History trail starts fresh for repo reasons, not identity reasons
Fixes 05's rationale: a cross-board move keeps its UUID (remint is
collision-only); the listing starts fresh because the destination repo
has no commits for the arriving folder.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 15:54:15 -04:00
rzen 18a23280cd Whole-board copies keep GUIDs — carve-out from the copies-remint rule
Resolves the 01-vs-09/03 contradiction: Save as Template and File ▸
Duplicate copy the board folder as-is, GUIDs kept — the remint rule
governs item-level copies inside an existing board. Duplicate's
fork-keeps-history guarantee requires retention (copied .git history
must keep naming real paths); 03's Duplicate paragraph now says so.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 15:53:29 -04:00
rzen 94e795aa89 Renumber commits as the triggering event, not a standalone message
Resolves the 01-vs-06 contradiction: the pure-diff composer cannot emit
'Renumber lane X' (order-only bookkeeping composes nothing), and a
renumber never happens alone — it batches with the insert/move that
exhausted the midpoint, so the commit reads as that event. 06's
bookkeeping rule now says sequence-preserving order changes are the
bookkeeping; repositioning order changes still compose Reorder.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 15:51:31 -04:00
rzen 0871b96789 A liveness flip is a vanish — selection re-resolves by UUID and liveness side
Resolves the 02-vs-04 contradiction: foreign edits that flip deleted:
on a selected card eject it from the selection (and pending cut, which
already behaved this way), so 04's homogeneous-by-liveness invariant
holds across reloads and menu validation never sees a mixed selection.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 15:50:10 -04:00
rzen 4b20f8499b Moves are not deletions for stamp attribution; re-stamp on move
Resolves the 06-vs-08 contradiction: 06's Lanework External fallback
demotes on true deletions only (id vanished from the board, per the
message engine's whole-board id matching), not folder moves. A bare mv
leaves the moved card unstamped, so 08 now teaches re-stamping on every
move as well as every write.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 15:49:14 -04:00
rzen 8e7c565e07 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
2026-07-26 15:47:33 -04:00
rzen a6f4960492 Search result set is derived, not frozen transient state
Resolves the 02-vs-04 search-filter contradiction: 04's live predicate
wins. 02's frozen-UUID transient-state list drops the result set; the
query is transient, the result set recomputes against each snapshot.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
2026-07-26 15:44:38 -04:00
rzen 95418a2670 Build BoardLoader — fail-fast fractal tree loading
Pure tree walk: root → lanes → cards, level is position. Fail-fast with
path+reason for bad YAML, missing/malformed schema or order, newer
schema, rootless board; index-less folders skip with a collected+logged
warning; strays, hidden files, and symlinks ignored; board-level
deleted ignored with a warning; tombstones loaded and flagged. 17 tests.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 15:40:46 -04:00
rzen c7cc97aeec Define the board, lane, and card model types
Immutable snapshot value types encoding the frontmatter tables: ItemID
wraps the exact folder name (not Foundation UUID, which uppercases);
lanes/cards carry FieldValue-typed fields, display-ordered children
including flagged tombstones, and their full FrontmatterDocument so
unknown and reserved keys ride along uninterpreted. Boards are
identified by rootURL — package folders are human-named, not UUIDs.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 15:40:45 -04:00
rzen cb6f6100a7 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
2026-07-26 15:26:12 -04:00
rzen 954c4b351d Refine design — raw-source Apply carve-out, watcher scope, lock scope
Resolved on the Redesign board: raw-source Apply preserves a typed
modified-by stamp; FolderWatcher is FSEvents-only best-effort with no
iCloud/network fallback; read-only lock scope enumerated; matching
touch-ups in board-ui, card-window, accessibility docs.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 15:26:12 -04:00
rzen 4e016fe8fe Implement Ranks — gapped fractional ordering
Pure ordering math: append at max+1024, head-insert at min−1024,
midpoint insertion with precision-exhaustion detection (nil on ties and
rounding onto an endpoint), renumbering to whole multiples of 1024, the
shared display-order tie-break (order, then folder name), and
tombstone-excluding overloads. 18 tests.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 15:12:26 -04:00