B7 — Planner board: triage column treatment #415

Closed
opened 2026-04-27 00:03:36 +00:00 by claude-desktop · 0 comments
Collaborator

As an operator,
I want the Unassigned column to look and behave like a Linear "triage inbox" — distinct surface, count badge, sort by freshness, arrival flash —
so that new tickets visually pop and I clear them top-down.

Today the Unassigned column is just one more grey column sorted by status group. Linear's triage inbox is its own visual class; this story adopts that treatment for our Unassigned synthetic.

Acceptance criteria

Visual

  • Column header reads Triage (12) where 12 is the live count.
  • Column surface uses a bg-triage token tint distinct from agent columns (define --ch-color-triage in tokens.css if absent — pick a colour that pairs with the existing palette, e.g. a muted warm tone).
  • Column-header badge uses rounded-pill and inverts colour when count === 0.
  • Empty state: a centered placeholder reading 🎉 Inbox zero with text-text-muted. (Emoji intentional for celebration; this is the only place in the spec that requests one.)

Sorting

  • Cards in the Triage column sort by created_at desc (newest on top).
  • Other columns are unaffected — they keep their current grouping (running, queued, …).

Arrival animation

  • When SSE delivers a new card into the Triage column (transition from "not present" to "present"), the card flashes a 1 s border-accent ring once.
  • No sound — peon-ping is reserved for task lifecycle, not arrivals (per CLAUDE memory).

Reuse

  • B1 drop-to-unassign target is this triage column.
  • B5 card face renders inside this column (full layout including stall age — relevant for old triage cards).
  • B4 keyboard navigation lands on this column and lets the operator clear it with j/k + a (assign) without touching the mouse.

Tests

  • board.test.tsx: header reads Triage (N) matching the projection.
  • board.test.tsx: cards sorted newest-first.
  • board.test.tsx: SSE event for a new card triggers the 1 s flash class on the card root.
  • board.test.tsx: empty state renders inbox-zero placeholder.

Out of scope

  • Triage labels (e.g. triage:duplicate, triage:wontfix). Forgejo issue close is enough.
  • Auto-triage by the foreman agent. Separate spec.
  • Drop-to-unassign with confirm dialog — covered by B1.

References

  • Spec: docs/specs/board-rework.md §5 B7.
  • Token system: apps/web/src/styles/tokens.css + apps/web/CLAUDE.md.
  • B1 (drop-to-unassign), B4 (keyboard), B5 (card face).
  • Memory note: peon-ping reserved for task lifecycle, not UI events.

Dependencies

  • Lands after B1 + B5. Independent of B4/B6.

Suggested first commit

feat(board): triage column — distinct surface + freshness sort + arrival flash

**As an** operator, **I want** the Unassigned column to look and behave like a Linear "triage inbox" — distinct surface, count badge, sort by freshness, arrival flash — **so that** new tickets visually pop and I clear them top-down. Today the Unassigned column is just one more grey column sorted by status group. Linear's triage inbox is its own visual class; this story adopts that treatment for our Unassigned synthetic. ## Acceptance criteria ### Visual - [ ] Column header reads `Triage (12)` where `12` is the live count. - [ ] Column surface uses a `bg-triage` token tint distinct from agent columns (define `--ch-color-triage` in `tokens.css` if absent — pick a colour that pairs with the existing palette, e.g. a muted warm tone). - [ ] Column-header badge uses `rounded-pill` and inverts colour when `count === 0`. - [ ] Empty state: a centered placeholder reading `🎉 Inbox zero` with `text-text-muted`. (Emoji intentional for celebration; this is the only place in the spec that requests one.) ### Sorting - [ ] Cards in the Triage column sort by `created_at` desc (newest on top). - [ ] Other columns are unaffected — they keep their current grouping (`running`, `queued`, …). ### Arrival animation - [ ] When SSE delivers a new card into the Triage column (transition from "not present" to "present"), the card flashes a 1 s `border-accent` ring once. - [ ] No sound — peon-ping is reserved for task lifecycle, not arrivals (per CLAUDE memory). ### Reuse - [ ] B1 drop-to-unassign target is this triage column. - [ ] B5 card face renders inside this column (full layout including stall age — relevant for old triage cards). - [ ] B4 keyboard navigation lands on this column and lets the operator clear it with `j/k` + `a` (assign) without touching the mouse. ### Tests - [ ] `board.test.tsx`: header reads `Triage (N)` matching the projection. - [ ] `board.test.tsx`: cards sorted newest-first. - [ ] `board.test.tsx`: SSE event for a new card triggers the 1 s flash class on the card root. - [ ] `board.test.tsx`: empty state renders inbox-zero placeholder. ## Out of scope - Triage labels (e.g. `triage:duplicate`, `triage:wontfix`). Forgejo issue close is enough. - Auto-triage by the foreman agent. Separate spec. - Drop-to-unassign with confirm dialog — covered by B1. ## References - Spec: `docs/specs/board-rework.md` §5 B7. - Token system: `apps/web/src/styles/tokens.css` + `apps/web/CLAUDE.md`. - B1 (drop-to-unassign), B4 (keyboard), B5 (card face). - Memory note: peon-ping reserved for task lifecycle, not UI events. ## Dependencies - Lands after **B1 + B5**. Independent of B4/B6. ## Suggested first commit `feat(board): triage column — distinct surface + freshness sort + arrival flash`
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks#415
No description provided.