Files
lanework/DESIGN/09-templates.md
T
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

8.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; adoption applies to boards the user opens, not boards the app materializes): every new board starts at mode none. 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. 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). 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).