The tombstone model is retired wholesale: deletion is a move into the reserved <root>/.trash/, always entering at the top (rank minting — no deleted: key, no timestamp sort), with modified stamped for the future age-based purge. Lanes are never trashed — lane delete is physical, native undo in-session the net. Put Back is gone: restore is drag-out or cut+paste (⌘X now enabled in the trash as the keyboard restore path). Trash cards are ordinary cards — search, selection, clipboard, and rendering uniform — and one container boundary replaces the effective-liveness, ancestor-walk, and kind-homogeneity machinery across 02 and 04. Delete stages by place (board moves, trash deletes permanently under the recoverability confirm); legacy deleted: keys migrate without destroying (cards relocate, lanes return live, notices). Templates exclude .trash; Duplicate carries it; the agent guide teaches delete-as-move. Materialized reserved lanes are named as a pattern — .archive is WISHLIST #8, and the backed-out release-presentation question is marked open in 03. Ruling: Redesign board Resolved card e8f1a6c2; 19 mooted tombstone-era rulings moved to the board's new Mooted lane. Claude-Session: https://claude.ai/code/session_01CqjXB7ASoWtbyoGod68k97
8.9 KiB
Board Templates
Status: settled. The template inventory is settled (inherit all ten from the pathfinder, more to come); the definition format and the storage story below are signed off.
Inventory (settled)
All ten pathfinder templates carry over: Basic, Classic Kanban, Software Project, Content Pipeline, Job Hunt, Sales Pipeline, Weekly Planner, Roadmap, Event Planning, Bug Tracker — each with its existing lane titles, icons, palette icon colors, board icon, and blurb. Basic stays first and remains the plain To Do / Done scaffold. The set is expected to grow.
Definition format (settled)
A template is itself a board. Each template is a schema-valid board folder bundled in the app's resources — the same fractal folder + index.md structure, loadable by the same BoardLoader, validated by the same rules:
<app bundle>/Templates/
├── basic.kanban/
│ ├── index.md ← title: Basic; template: {order: 100}; body = picker blurb
│ ├── 3f2a9c41-…/index.md ← lane "To Do" (order: 1024)
│ └── 8d3b1f22-…/index.md ← lane "Done" (order: 2048)
├── classic-kanban.kanban/
│ └── …
- The template board's
index.mdcarries the picker metadata in its ordinary frontmatter:title= display name,icon/iconColor= the board icon shown in the picker and inherited by the new board, and the body = the blurb (shown in the picker and becoming the new board's description, as in the pathfinder). - One picker key:
template:— a mapping holding picker concerns;order(display position in the chooser) is its only subkey (settled — future subkeys possible). Carried onto instantiated boards, where it is ignored and preserved like any unknown key; specified in the core schema's board table (01-storage-format.md). - The bundle folder name (
basic.kanban) is the template's stable slug (tests, a11y ids). - Lane folders are ordinary GUID folders with ordinary
order/icon/iconColorfrontmatter. Templates may contain starter cards — a capability the Swift-struct format never had (e.g. a "How this board works" card).
Instantiation
Creating a board from a template: copy the tree — skipping .trash/ (Save as Template already strips it, but hand-dropped user templates can carry one; a new board isn't born with trash) — mint fresh GUIDs for every lane/card folder, stamp created/modified fresh (the stated exception to 01-storage-format.md's copies-keep-created rule — a new board is born today, not forked from the template), seed the save panel's suggested name from the template title, and set the new board's title to the user-chosen document name (per 01-storage-format.md's board-naming rule, so display name and folder name start out matching). The template: key is kept — inert on an ordinary board. .git is never copied — a template is content, not history, and a hand-dropped user template that carries one must not produce boards that are silently in git mode (06-history-undo.md's no-silent-auto-init — the principle is never give the user a repo they didn't ask for, and it scopes to instantiation: File ▸ Duplicate deliberately carries .git, because a duplicate of a git board is a fork of its history — 03-board-ui.md): an instantiated board is never in git mode — its actual mode follows 06's nearest-.git-wins detection at the destination the save panel chose: mode none in a plain folder, repo-nested when saved inside an existing repository (no undo, no add-git — the popover explains, 06-history-undo.md). Beyond the template: residue, the result is indistinguishable from a hand-built board.
Why this format
- Dogfood: the template format is the board format — no second schema, no parallel Swift model to keep in sync. The picker's mini per-lane preview renders from a real
BoardModelvia the normal loader. One bad template never fails the chooser (the user store is hand-editable, so a malformed board there is one edit away): a user template the loader rejects is still listed — by folder name, marked unloadable, carrying the loader's fail-fast specifics — but can't be instantiated or previewed; fix the files and it comes back. The same honest-surfacing posture as 02-architecture.md's welcome recents, which don't pre-detect broken boards either. - Authorable: adding a template = adding a folder (a human, or an agent, can author one without touching Swift); a template diff in review shows real frontmatter, not struct literals.
- Testable for free: template validity is enforced by the loader's own fail-fast rules at test time (walk
Templates/, load each). - Opens the door to user-defined templates — see Save as Template below.
Save as Template
A "Save as Template" function copies the current board into the user templates store; the chooser lists user templates after the bundled ones. The copy is preceded by the close flush (02-architecture.md ▸ Windows: editor saves, then the pending auto-commit — with the pull-style mechanical exception committing an open Edit session's on-disk saves as-is, sessions staying open), so the template never misses the last keystrokes; the same rule covers File ▸ Duplicate (03-board-ui.md), where the flush also keeps the copied .git's history from lagging its tree. Under the read-only lock, Save as Template disables with one exception — the unwritable-location state, where it stays live unless an open Edit or raw-source session holds unsaved content the suspended saves can't flush (reads the board, writes Application Support; 02-architecture.md ▸ Live-reload resilience has the settled scoping). Because a template is a board, the copy is nearly literal: .git is not copied (a template is content, not history — copying it would embed the board's full repo, every attachment version included, in the template store; see 06-history-undo.md's repo-growth note), .trash/ is dropped, a template: key is added — or, when the board already carries one (e.g. it was itself instantiated from a template), its stale order is overwritten — with an order appended after existing user templates; GUIDs are left as-is (instantiation mints fresh ones anyway), and timestamps are kept per 01-storage-format.md's copies-keep-created rule (equally inert — instantiation restamps them).
Two edges, settled:
- Store collisions auto-rename, Finder-style (
Board.kanban→Board 2.kanban) — the 01 import precedent: saving never overwrites an existing template and never refuses. - Strays copy through. The copy is literal apart from the stated exclusions (
.git,.trash/) —CLAUDE.user.md, a seeded.gitignore, and other non-schema files carry through Save as Template and instantiation alike. Deliberate: a template is the folder, andCLAUDE.user.mdcarrying a board's custom agent instructions into boards born from it is a feature. The app-ownedCLAUDE.mdcopies inertly and self-heals to the current guide version when the new board is opened (08-agent-integration.md).
Storage (settled): Application Support (…/Lanework/Templates/, inside the app container) as the canonical store — friction-free sandbox writes, no location ceremony — kept honest by a Reveal in Finder affordance in the template chooser: revealed, it's plain board folders, hand-editable and agent-writable, and a board folder dropped in becomes a template — no template: key required. Chooser order: bundled templates by template.order, then keyed user templates by template.order, then keyless user boards last, sorted by display name (title ?? folder name — 01-storage-format.md's board naming). An unloadable user template sorts with the keyless tier, by folder name — the failed load can supply neither template.order nor title, so the folder name is the only identity it has (and the one its unloadable row already shows). The app never stamps a key into store files it didn't write itself — a hand-dropped board is never touched, and adding a key by hand is how its user picks a position; the one writer of keyed files is Save as Template, whose own copies arrive keyed (above). A user-visible or user-configurable location was considered and set aside as ceremony disproportionate to a secondary feature; revisit if template sharing becomes a real workflow.
Rejected alternative
Keeping the Swift-struct catalog (pathfinder approach). Simpler to ship, but it's a second definition of "a board" that must track schema changes by hand, can't hold starter cards naturally, and forecloses non-programmer template authoring.
Settled
- No dialect, one schema: bundled (and user) templates use GUID folder names like any board — no relaxed template-only format.
Open questions
None currently — the storage location is settled (Application Support as the canonical store, kept honest by Reveal in Finder; a user-visible or configurable location was set aside as ceremony, revisit if template sharing becomes a real workflow).