3.3.2 Schema — `board.swimlaneGroupBy` enum + migration (WIP column already exists)
Estimate: 12m · Depends on: 3.1.1
Persist the swimlane group-by on the board entity (the stub: "Config persisted on the board entity"). This is the ONLY schema change the story needs — the per-column WIP limit reuses the board_column.wipLimit Int? column Story 3.1.1 already shipped explicitly for 3.3, so there is NO WIP migration.
The change. Add a Prisma enum BoardSwimlaneGroupBy { none, assignee, epic, priority } and a board.swimlaneGroupBy BoardSwimlaneGroupBy @default(none) column, in ONE migration. none is the flat 3.2 board (the default, so every existing/seeded board is unchanged). The enum values are exactly the stub-specified dimensions; adding more later (e.g. a custom query lane) is a non-breaking enum addition. No new table, no RLS change (the column lives on the already-RLS-forced board table from 3.1.1; tenant isolation is inherited).
What this does NOT do: the config write service/route (3.3.3); the projection grouping (3.3.4); any UI (3.3.5/3.3.6). It also does NOT touch board_column.wipLimit (already present) or add a per-lane-WIP column (out of scope — per-column WIP only).
Acceptance criteria
BoardSwimlaneGroupByenum +board.swimlaneGroupBy @default(none)added in ONE Prisma migration;prisma migrate devapplies cleanly against a fresh DB and is idempotent on re-run.- Existing/seeded boards default to
none(the flat board) with no data backfill needed. - No new table and no RLS policy change (the column inherits the 3.1.1
boardRLS);prisma generatetypes the new field. - A vitest assertion (real Postgres) confirms a board defaults to
noneand accepts each enum value.
Context refs
prisma/schema.prisma— theboardmodel +BoardTypeenum (3.1.1) to mirror for the new enum;board_column.wipLimit(already present — confirm, do not re-add)- Story 3.1.1 — the board tables + RLS pattern this extends
motir-core/CLAUDE.md— migration conventions (one migration, application-seeded data, RLS-forced tables)