Commit Graph
245 Commits
Author SHA1 Message Date
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
rzen 9472198848 Refine design — Put Back edge case, reserved-key commit messages
Resolved on the Redesign board: post-renumber Put Back placement is
deterministic but may land among new neighbors; the commit-message
composer deliberately models the reserved labels/assignees/due trio.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 15:04:00 -04:00
rzen c4d4fbe663 Set up repo hygiene — README, CHANGELOG, versioning
README with a maintained key-features section, CHANGELOG.md stub in the
portfolio's IndieAbout format, and a post-build phase stamping
CFBundleVersion (git commit count), BuildDate, and BuildHash into the
app and dSYM Info.plists on every build.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 15:01:35 -04:00
rzen 01eabbe796 Scaffold the Xcode project — XcodeGen, Swift 6, sandboxed
XcodeGen project (codename Kanban, display name Lanework, bundle id
dev.rzen.indie.Kanban): macOS 26+ app target with Swift 6 strict
concurrency, app sandbox with user-selected file access and app-scoped
bookmark entitlements, plus unit-test (Swift Testing) and XCUITest
targets wired into the Kanban scheme.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 15:00:31 -04:00
rzen b0763c3c82 Add design corpus and wishlist
Twelve design docs (00-vision through 11-command-nexus) plus assets and
the wishlist, authored ahead of implementation.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
2026-07-26 14:58:16 -04:00