3.7.5 Board-scoped read — resolve the selected board (`?board=`/default) through the projection
Estimate: 18m · Depends on: 3.7.3, 3.1.4
Thread the SELECTED board through the read path. Today GET /api/board resolves the active project’s single default board (3.1.6). Generalize: resolve the board from a boardId query param, defaulting to the project’s isDefault board when absent (and 404 if the id isn’t a board of the active project / workspace — never cross-tenant). The board page passes the ?board= selection through; the projection (getBoard), the move, the group-by/WIP config, and the 3.6 column-mapping all already take a boardId — this just makes the SELECTED id flow instead of the implicit default. The swimlaneGroupBy / WIP / columns the UI shows are thus the selected board’s.
Acceptance criteria
GET /api/board?boardId=returns the named board’s projection; absent → the project’s default board; a board id outside the active project/workspace → 404 (tenant-safe).- The board page resolves
?board=(default when absent) and every board-scoped call (projection, move, group-by/WIP, column-mapping) uses the selected board id; the single-default path is unchanged when no?board=. - Vitest (real Postgres): selecting board A vs B returns each board’s own columns/config; the default fallback; the cross-project/workspace 404.
Context refs
app/api/board/route.ts(3.1.6 — the active-project board resolution to generalize) +boardsService.getBoard(alreadyboardId-taking, 3.1.4);app/(authed)/boards/page.tsx+BoardContainer.tsx- Story 3.7.3 (
GET /api/boardslist) + 3.7.4 (the switcher that sets?board=)