3.7 Multiple boards per project (board CRUD + switcher)
Turn the project’s single auto-seeded board into many boards per project — create / rename / delete, a default board, a switcher on /boards, and per-board config (the 3.3 swimlane group-by + WIP and the 3.6 column→status mapping are already keyed by boardId, so each board carries its own config; reused, not rebuilt). Additive: the 3.1 schema + API already carry boardId and the per-project board index is non-unique, so the single-default-board path keeps working.
Verified mirror (rung 1, mistake #33). Jira has multiple boards per project (one team per board, many teams per project); a board has a type (Scrum/Kanban) + its own column/swimlane config; any member creates one, an admin deletes it. A Jira board is ultimately backed by a saved filter (so it can span projects) — but filters are Epic 6, so this story ships project-scoped boards (each shows the project’s issues under its own config); the JQL-filter backing + cross-project boards are the Epic-6 extension (the disabled [Filter] seam already reserves it; see Story 3.8’s cap note).
Default board + lifecycle. Each project has exactly one default board (the 3.1 auto-seeded board migrates to default); creating a board seeds its default columns off the project workflow (reuse the 3.1 board-bootstrap path) so it’s usable immediately; deleting a board removes only the board + its column/config rows (issues belong to the project, never a board, so none are lost). Guards: a project always keeps at least one board (the last board can’t be deleted), and deleting the default promotes another to default (never a project with no default).
Selecting a board. The board the user is viewing is URL-addressable (a ?board=<id> param on /boards, mirroring the 2.5.19 ?peek pattern — shareable / reload-safe), defaulting to the project’s default board when absent. The selected board id threads into the projection (GET), the move, the group-by/WIP config, and the column-mapping — all already board-scoped.
Permissions. Board CRUD is a project-config write — membership-gated now, with a // TODO(6.4): gate by project role note (consistent with 2.2.5 / 3.3.3); when Story 6.4 lands, board admin is gated by the project admin role. No early RBAC build.
Out of scope: the Scrum board variant (Story 4.5 — moved to Epic 4 per mistake #32); the column/status-mapping admin itself (Story 3.6 — reused, not rebuilt; made PER-BOARD by 3.7.7 design + 3.7.8 code so settings target the selected board, not only the default); filter-backed / cross-project boards (Epic 6); board sharing / favourites (Jira has them; no stated need → deferred).
Verification
- Pull the Story branch,
pnpm install,pnpm prisma migrate dev,pnpm db:seed,pnpm dev; open/boardson the seededmoooon→motirproject. - Create: use the switcher’s “New board” → name it (e.g. "Triage") → it’s created with default columns and becomes the active board; the switcher now lists both.
- Switch: pick the other board in the switcher → the board re-lays from that board’s projection + config (its own group-by / WIP / columns); the URL carries
?board=<id>and a reload keeps it. - Per-board config is independent: set group-by Assignee + a WIP limit on board A; board B is unaffected (each board’s config is its own).
- Rename / set default / delete: rename a board; set a non-default board as default → new sessions open it; delete a board → it’s gone, its issues still exist on the project (visible on the other board); deleting the default promotes another; the last board cannot be deleted (guard + disabled affordance).
pnpm test+pnpm test:e2e --grep board-crudgreen over the real stack.