3.3.6 WIP-limit UI — per-column config (column `[⋯]` menu) + SOFT over-limit warning
Estimate: 18m · Depends on: 3.3.1, 3.3.3, 3.2.3
Let a user set a per-column WIP limit and surface an over-limit column as a soft warning — drawn per design/boards/swimlanes-wip.mock.html, on the 3.2.3 BoardColumn (which already reserved the WIP slot + the [⋯] actions menu).
Config editor. The column [⋯] menu gains a "Set WIP limit" action opening a small integer field (clearable to remove the limit). Saving calls PATCH …/board/columns/[columnId] (3.3.3) with { wipLimit } (or null), optimistically updates the column, and reconciles to the returned column DTO. Validation mirrors the service (non-negative integer); an invalid entry is blocked client-side with the error copy.
Over-limit warning (SOFT — the load-bearing semantic). The column header + count badge render the limit as n/limit (e.g. 3/5). When n > limit (strictly greater — 5/5 is NOT warned) the column shows the over-limit treatment from 3.3.1: a warning hue (--el-warning/--el-danger) PAIRED with an icon and/or the n/limit label so it is not colour-alone (finding #35), and announced to assistive tech. Soft = advisory: an over-limit column does NOT block drops — the 3.2.4 move contract is untouched; dragging a card into an at/over-limit column still succeeds. No limit set → the count renders plain (no slot, no warning). When swimlanes are on, the limit is the per-column total across lanes (counted from the per-column total, not per lane).
Out of scope here: swimlane rendering + cross-lane drag (3.3.5); per-lane WIP; any HARD/blocking enforcement (explicitly NOT built — the stub says soft).
Acceptance criteria
- The column
[⋯]menu offers "Set WIP limit" with an integer field (clearable); saving PATCHes…/board/columns/[id](3.3.3), updates optimistically, reconciles to the DTO, and blocks an invalid (negative/non-integer) entry client-side. - A column with a limit shows
n/limit; whenn > limitit shows the over-limit warning (hue + icon/label, not colour-alone, finding #35) and announces it;n == limitis NOT warned; no limit → plain count. - The warning is SOFT: a drop into an at/over-limit column still succeeds (the 3.2.4 move contract is unchanged) — verified, not assumed.
- With swimlanes on, the limit is evaluated against the per-column total across lanes.
- Colours via
--el-*, shape via element tokens, AA-safe; matchesdesign/boards/swimlanes-wip.mock.html. - Component tests assert the over-limit predicate (under/at/over), the no-limit plain state, the optimistic config update, and that an over-limit drop is not blocked.
Context refs
app/(authed)/boards/_components/BoardColumn.tsx(Story 3.2.3) — the column header + reserved WIP slot +[⋯]menu this fills; the 3.2.4 drop handler (confirm it is NOT WIP-gated)- Story 3.3.3 — the
PATCH …/board/columns/[id]WIP write this calls; Story 3.3.4 — thewipLimit+ per-column count on the projection components/ui/*(the menu primitive, input,Tooltip, the warning Pill/badge) +--el-warning/--el-dangertokensdesign/boards/swimlanes-wip.mock.html+design-notes.md(3.3.1) — the WIP-config + over-limit spec; finding #35 (not colour-alone);motir-core/CLAUDE.md— token rules