Files
lanework/DESIGN/09-templates.md
T
rzen cacd48cb0f Ratify the edition split and the 2026-07-28 design-review resolutions
Edition split follow-through: 12-editions.md and 13-native-undo.md join the
corpus; git-era docs (06, 07) are Pro-scoped and cross-references realigned.

Resolution session rulings written in place: caret chords yield to any focused
text control (04, 11); the drop settle holds its proposal as overlay state and
02 gains the overlays family; Duplicate gets the save-panel fallback and a
cancellable copy walk (03); Finder open is a standard document open (02);
failed first opens record before loading with the folder name provisional (02);
trash pointer ranges skip by kind, full-height marquee surface, Select All by
kind, rows-only trashed universe (02, 04); Empty Trash counts entries (03);
create handoff reads as one arrival (02); range-anchor lifecycle, board-
background click grammar, jump landing cards, lane-domain shift-arrows (04);
Reduce Motion restated per voice (10).

Claude-Session: https://claude.ai/code/session_01CqjXB7ASoWtbyoGod68k97
2026-07-27 20:29:32 -04:00

9.0 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.md carries 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/iconColor frontmatter. 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 tombstoned items (Save as Template already strips them, but hand-dropped user templates can carry them; 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 BoardModel via 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), tombstoned items are 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.kanbanBoard 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, tombstoned items) — 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, and CLAUDE.user.md carrying a board's custom agent instructions into boards born from it is a feature. The app-owned CLAUDE.md copies 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).