4.7.3 At-scale Scrum interaction + complete journey E2E — drag-as-transition · snap-back · swimlanes · cross-lane reassign · WIP · complete-sprint carry-over + report
Estimate: 25m · Depends on: 4.7.1, 4.5.2, 4.5.3, 4.4.3, 4.4.4, 4.4.6, 3.2.4, 3.3.5, 3.3.6
The Playwright spec (tests/e2e/board-scrum-at-scale.spec.ts, the interaction + lifecycle half) that proves the combined Scrum journey holds at scale — the deferral 4.4.7 / 4.5.4 send here. Every interaction runs on a card inside a virtualized, bounded, sprint-scoped board (4.7.1 seed), so it doubles as proof that the 3.8 load model + the 4.5.2 sprint scope do not break the 3.2/3.3 interaction contracts, and the closing complete-sprint step proves the 4.4 carry-over holds over a LARGE set.
Drag-as-transition deep in a virtualized sprint column. Scroll a tall sprint column, pick a card not in the initial window, drag it to another column → the workflow transition applies (optimistic, then reconciled to the returned BoardCardDto); re-fetch / quick-view shows the new status and the card stays in the sprint (status changed, sprintId unchanged); counts + the header point figures on both columns update. Proves drag survives virtualization + the sprint scope (3.2.5/3.8.3 + 4.5.2).
Illegal-move snap-back (409) at scale. An illegal cross-column move under the restricted workflow → the card snaps back to its origin column and the status is unchanged on re-fetch (the 3.1.5 IllegalBoardMoveError 409 contract), even from deep in the list.
Swimlanes re-lay at scale (sprint-scoped). Switch group-by to Assignee → the Scrum board re-lays into one lane per assignee-with-cards + the "No assignee" catch-all, each lane still bounded + virtualized with NO "Load more" (3.8.5) and still sprint-scoped; switch to Epic (grouped by ancestor epic) and Priority and back to None. Collapse a lane → its cards hide, header + aggregate count remain, and the collapse persists on reload.
Cross-lane reassign + diagonal. Drag a card into another assignee lane → the assignee is reassigned (confirmed via re-fetch / quick-view), status + sprint unchanged; into "No assignee" → unassigned. A diagonal drag (different column AND different lane) applies BOTH the transition and the field reassign, each with independent reconcile (3.3.5) — one rejection does not revert the other; the card stays in the sprint throughout.
WIP soft-warning is advisory, not blocking. Set a column's WIP limit below its sprint-scoped card count (the [⋯] menu, 3.3.6) → the header shows the n/limit over-limit warning (hue + icon/label, not colour-alone, finding #35); drag another card in → the drop still succeeds and the warning persists (soft = advisory; the 3.2.4 move contract is untouched). A column exactly at the limit (n === limit) is NOT warned (strictly greater).
Complete-sprint with a LARGE carry-over + report (the lifecycle at scale). With the large active sprint still holding many unfinished (non-done-category) issues, invoke Complete sprint (the 4.4.6 modal, from the backlog active-sprint container or the scrum header): the carry-over chooser offers Backlog and the seeded planned target sprint. Choose the planned sprint → confirm → the whole large set of unfinished issues moves into it in ONE bounded transaction (the 4.4.3 contract — assert none are left behind and the done issues stay on the completed sprint), and the sprint report (4.4.4) shows the completed/incomplete lists paginated (a bounded first page + a "view all" deep-link, NOT a dump of thousands — finding #57), the committed/completed/not-completed points + the scope-change count from aggregates (not page sums), and the freed one-active slot (a new sprint can now start). Re-run the Backlog branch on a fresh seed to assert carry-over to the backlog restores backlog_rank order over a large set.
Acceptance criteria
pnpm test:e2e --grep board-scrum-at-scale(this spec) is green: a drag-as-transition on a card scrolled into view deep in a virtualized sprint column applies + reconciles and keeps the card in the sprint; an illegal cross-column move snaps back (409) with status unchanged.- Switching group-by (Assignee/Epic-ancestor/Priority/None) re-lays the Scrum board into bounded, virtualized, sprint-scoped lanes + catch-all with NO "Load more"; a collapsed lane stays collapsed on reload; a cross-lane drag reassigns the grouped field (status + sprint unchanged) and a diagonal drag applies both writes with independent reconcile.
- A column over its WIP limit shows the soft
n/limitwarning (not colour-alone) yet still ACCEPTS a dropped card (advisory, non-blocking); an at-limit column is not warned. - Completing the large active sprint moves the WHOLE unfinished set into the chosen planned sprint (or the backlog, rank restored) in one bounded transaction (none left behind, done issues retained), frees the one-active slot, and renders the sprint report with PAGINATED lists + aggregate committed/completed/not-completed points + the scope-change count (no load-all, finding #57).
- Every interaction runs against the 4.7.1 sprint-shaped large seed (cards deep in virtualized, sprint-scoped lists), reuses the real-Postgres/Playwright harness + 4.7.1 helpers, and does NOT duplicate the single-surface journeys owned by 4.4.7 (focused lifecycle) / 4.5.4 (small-scale scrum) / 3.5.3 (Kanban interaction).
Context refs
- Story 4.7.1 — the sprint-shaped large seed + at-scale helpers; Stories 4.4.7 / 4.5.4 / 3.5.3 — the single-surface journeys this extends to the at-scale Scrum case (do not duplicate)
app/(authed)/boards/_components/{BoardContainer,BoardColumn,BoardCard,SwimlaneBoard,LaneCell}.tsx(3.2.4 dnd-kit drag, 3.3.5 swimlanes, 3.3.6 WIP, 3.8.5 bounded swimlane render) + the complete-sprint modal + report view (4.4.6) — under testlib/services/boardsService.ts(moveCard, scrum scope, swimlane projection, WIP) +lib/services/sprintsService.ts(completeSprintcarry-over 4.4.3,getSprintReport4.4.4) +lib/boards/errors.ts(IllegalBoardMoveError409) +lib/sprints/errors.ts(InvalidCarryOverTargetError) — the move + reassign + complete contracts verifiedtests/e2e/_helpers/board.ts(+ 4.7.1 additions) +tests/e2e/_helpers/workflow.ts;design/boards/{scrum,swimlanes-wip}.mock.html(4.5.1/3.3.1) +design/sprints/sprint-lifecycle.mock.html(4.4.1) — the drag/lane/WIP/complete design under test