Comments card-window UI designed — three componentized panes and the .draft composer

Designed with the user 2026-07-29, closing Redesign card 82e2de6c (both halves). The card window recomposes as three componentized panes — body, comments, attributes — with a layout option (side-by-side default, body-over-comments for narrow displays; app-wide) and the thread visible through body Edit in both mounts. The column auto-shows when the thread is non-empty; sort direction flips at the header; the composer sits at the newest end. The composer edits comments/.draft/ — the user's idea, replacing any app-side draft store: one draft per card, ordinary comment schema with attachments, excluded from the thread, riding git/copies/trash, joining the claimed names; slow save cadence composing "Draft comment on 'X'"; posting renames to a fresh UUID and restamps created/modified in one bracket. Inline edits are body-edit sessions in miniature; no prompts anywhere. All editions ship comments — only tracker sync is Teams (matrix updated). A11y shapes in 10; menu, grammar, context-menu, and configuration rows in 11.

Board: Redesign card 82e2de6c → Resolved; Implementation card "Build comments" filed on Backlog (post-2.0).

Claude-Session: https://claude.ai/code/session_01CqjXB7ASoWtbyoGod68k97
This commit is contained in:
2026-07-29 18:48:08 -04:00
parent 0463540aea
commit a99e1a52f0
5 changed files with 26 additions and 5 deletions
+1 -1
View File
@@ -91,7 +91,7 @@ Schema-owned display fields are Lanework's to interpret — **coerce where a sen
Future integrations extend the core schema; this version reserves their names — and, for comments, specifies the storage schema ahead of the feature — so nothing else squats on them. The app treats reserved keys as ordinary unknown keys (preserved verbatim, invisible in the UI) and reserved folders as ignored non-level content:
- **`comments/` card child — the comment thread** (storage specified 2026-07-29; no comments functionality ships in this version — the card-window UI is its own pending design pass, tracked on the findings board). A comment is a **UUID-named folder** under `comments/`, holding `index.md` (frontmatter + Markdown body, the card-body subset) and optionally `attachments/` — a card's anatomy one level down, so the fractal rules apply verbatim: two-step-create tolerance, stray tolerance, loose-file relocation, displacement of a squatted `attachments`, shape-only identity. `comments/` itself stays a plain reserved child — never a level, never identity. **Field table for `kind: comment`** (exercising IntegrityRules' per-kind hook for the first time): `schema` required; `kind: comment` written at creation (the common rule); **`author`** lenient — self-reported *content*, not overlay: unlike `modified-by` it survives app writes; the app writes the macOS account's full name (the identity 06-history-undo.md's derived default already uses), agents write their own (the guide teaches it), tracker sync writes the remote author verbatim, and missing renders unattributed; `created`/`modified` as everywhere — `created` is load-bearing (ordering below), and the edited indicator is simply `modified` differing, no extra field; `remote`/`remote-state` ride as reserved tracker keys. **No `title`, no `order`.** The rules, all ruled 2026-07-29: **Ordering is chronology, not ranks** — the thread sorts by `created` ascending; chronology *is* a conversation's semantics, and cross-machine or tracker-synced comments carry independent clocks where minted ranks would interleave arbitrarily (the trash's no-timestamp-sort rationale inverts here); ties and missing/malformed `created` (coerce-tier fallback, logged) sort after dated siblings, folder-name order. **Flat this iteration** — no reply nesting, deliberately deferred with the door open (a future reply relation would be frontmatter, and the window-scoped read below holds either way). **Comment defects never refuse the board** — worst case is the stray posture (tolerated, logged, unrendered): a broken leaf annotation must not brick a load; deliberate, proportionate divergence from card fail-fast. **Delete skips the trash**`.trash/` holds cards and lanes; a comment deletes immediately and undoably (base captures and recreates, on git boards it's a commit), no confirm, undo is the net. **Copies carry the thread** — a copy is a fork, and dropping a subtree would be the one place a copy loses content; comment folders remint like every copied folder, and the copy's comments drop `remote`/`remote-state` (two local comments must never both claim to be the same remote object — content preserved, mapping severed); whole-board forks carry verbatim as always. **Comments are window-scoped, outside the board snapshot** — the walk stays O(cards): the card window reads its own thread and live-reloads it from the same FSEvents stream, and the board snapshot never loads comment content. This is the stated exception to snapshot completeness, costs named: board-level search never sees comment bodies (the card window's own find does), and foreign comment changes are described by **path shape** — the "Update agent guide (vN)" mechanism: a changed path under `…/comments/<uuid>/` composes "Comment on '⟨card title⟩'" / "Edit comment on…" / "Delete comment on…" (06 gains the verb family when the feature ships), and the announcer speaks arrivals the same way. Git protection needs nothing new — the committer already stages the whole root and its condition is the tree. Trash interplay is free: a trashed card carries its `comments/`, purge kills it, restore returns it. Comments never count in the welcome counts. A comment-count chip on card faces is WISHLIST #9. The schema is **edition-blind** (one format, no edition-specific keys); which edition renders and authors comments is the UI pass's question — 12-editions.md pins the enhanced schema to Teams today.
- **`comments/` card child — the comment thread** (storage specified 2026-07-29; no comments functionality ships in this version — the card-window UI is its own pending design pass, tracked on the findings board). A comment is a **UUID-named folder** under `comments/`, holding `index.md` (frontmatter + Markdown body, the card-body subset) and optionally `attachments/` — a card's anatomy one level down, so the fractal rules apply verbatim: two-step-create tolerance, stray tolerance, loose-file relocation, displacement of a squatted `attachments`, shape-only identity. `comments/` itself stays a plain reserved child — never a level, never identity. **Field table for `kind: comment`** (exercising IntegrityRules' per-kind hook for the first time): `schema` required; `kind: comment` written at creation (the common rule); **`author`** lenient — self-reported *content*, not overlay: unlike `modified-by` it survives app writes; the app writes the macOS account's full name (the identity 06-history-undo.md's derived default already uses), agents write their own (the guide teaches it), tracker sync writes the remote author verbatim, and missing renders unattributed; `created`/`modified` as everywhere — `created` is load-bearing (ordering below), and the edited indicator is simply `modified` differing, no extra field; `remote`/`remote-state` ride as reserved tracker keys. **No `title`, no `order`.** The rules, all ruled 2026-07-29: **Ordering is chronology, not ranks** — the thread sorts by `created` ascending; chronology *is* a conversation's semantics, and cross-machine or tracker-synced comments carry independent clocks where minted ranks would interleave arbitrarily (the trash's no-timestamp-sort rationale inverts here); ties and missing/malformed `created` (coerce-tier fallback, logged) sort after dated siblings, folder-name order. **Flat this iteration** — no reply nesting, deliberately deferred with the door open (a future reply relation would be frontmatter, and the window-scoped read below holds either way). **Comment defects never refuse the board** — worst case is the stray posture (tolerated, logged, unrendered): a broken leaf annotation must not brick a load; deliberate, proportionate divergence from card fail-fast. **Delete skips the trash**`.trash/` holds cards and lanes; a comment deletes immediately and undoably (base captures and recreates, on git boards it's a commit), no confirm, undo is the net. **Copies carry the thread** — a copy is a fork, and dropping a subtree would be the one place a copy loses content; comment folders remint like every copied folder, and the copy's comments drop `remote`/`remote-state` (two local comments must never both claim to be the same remote object — content preserved, mapping severed); whole-board forks carry verbatim as always. **Comments are window-scoped, outside the board snapshot** — the walk stays O(cards): the card window reads its own thread and live-reloads it from the same FSEvents stream, and the board snapshot never loads comment content. This is the stated exception to snapshot completeness, costs named: board-level search never sees comment bodies (the card window's own find does), and foreign comment changes are described by **path shape** — the "Update agent guide (vN)" mechanism: a changed path under `…/comments/<uuid>/` composes "Comment on '⟨card title⟩'" / "Edit comment on…" / "Delete comment on…" (06 gains the verb family when the feature ships), and the announcer speaks arrivals the same way. Git protection needs nothing new — the committer already stages the whole root and its condition is the tree. Trash interplay is free: a trashed card carries its `comments/`, purge kills it, restore returns it. Comments never count in the welcome counts. A comment-count chip on card faces is WISHLIST #9. **`comments/.draft/` is the card's single comment draft** (ruled 2026-07-29, the UI pass — user content with a lifecycle name, the `.trash` pattern one level down): a reserved dot-named folder holding ordinary comment schema, `attachments/` included; excluded from the thread; **posting renames it to a fresh lowercase UUID and restamps `created`/`modified` in the same bracket** — chronology is post time, not drafting time — one commit. `.draft` joins the claimed names (a wrong-kind node squatting it displaces by the ladder); copies and the trash carry it like any comment folder (fork-lossless); a draft emptied of text with no attachments is deleted by the app, never litter. The schema is **edition-blind** (one format, no edition-specific keys), and so is the feature: **comments render and author in every edition** (ruled 2026-07-29 — 05-card-window.md ▸ The comments column); only tracker sync is edition-gated (Teams — 12-editions.md).
- **Card metadata keys `labels`, `assignees`, `due`** — issue-style metadata belonging to the tracker-integration story (the pathfinder shipped them natively; the rewrite reserves them instead). One deliberate carve-out: the commit-message composer names changes to these three keys ("Relabel card 'X'" — 06-history-undo.md), invisible in the UI though they are.
- **`remote` key** (board, card) and **`remote-state` key** (lane) — future tracker connectors (Gitea/GitHub/GitLab/Jira), per 07-sync-collab.md's out-of-scope note.