feat(board): multi-select cards with cmd/shift-click + stack-drag #420
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!420
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "dev/411"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Multi-select stack-drag for the Kanban board: cmd/ctrl-click toggles per-card selection, shift-click extends a contiguous range within the same column, dragging any selected card carries the full stack.
Test plan
board.test.tsx— cmd-click togglesdata-selected; deselects on second clickboard.test.tsx— shift-click extends range 1..3; cross-column shift resets to card 4 onlyboard.test.tsx— drag of 3 selected cards passes all 3 toonAssignin one callboard.test.tsx— batch rollback: one rejectedassignCardrestores only the failed card, successful cards stay in targetboard.test.tsx— Esc clears selection;cmd-aafter mouseenter selects all in dev, not reviewerCloses #411
CI green. Three issues to fix before merge.
behavior
board.tsx:159,162—setAnchorKeyis called inside thesetSelectedKeysfunctional updater. React requires updater functions to be pure (no side effects); in StrictMode the updater runs twice, so the anchor double-toggles on every cmd-click. Move thesetAnchorKeycall outside the updater:test-gap
board.test.tsx— The AC says "drag of a non-selected card while selection exists drags only the dragged card (does not implicitly select)." No test covers this path. Add a test: cmd-click two cards to build a selection, then drag a third (unselected) card —onAssignshould receive only that third card.test-gap
board.test.tsx— The batch-rollback test usesrunBatchAssign, which strips theshowToastcall fromplanner.board.tsx. The AC requiresFailed to assign N of M cards: <reason>. Either mockshowToastin the test and assert it, or pull the toast assertion into an integration test that mountsBoardRoutewith a mockedassignCard.(Note for awareness, not a blocker: the "Drop on Unassigned" AC item is currently gated behind B1; the code comment acknowledges it as a follow-up. Fine to land as-is once the three items above are addressed.)
3446318baca575a01ccfCI green. None of the three round-1 findings were addressed — re-raising them verbatim.
behavior
board.tsx(cmd-click handler,setSelectedKeyscall) —setAnchorKeyis still invoked inside the functional updater passed tosetSelectedKeys. React requires updater functions to be pure; StrictMode double-invokes them, so the anchor double-toggles on every cmd-click. Move bothsetAnchorKeycall-sites outside the updater:test-gap
board.test.tsx— Still no test for "drag of non-selected card while selection exists drags only that card." TheDraggableCardcorrectly uses the single-card branch whenisSelectedis false, but the AC mandates a test. Add: cmd-click cards 1 and 2 to build a selection, then drag card 3 (unselected) to reviewer —onAssignshould receive[card3]only, not the full stack.test-gap
board.test.tsx— The batch-rollback describe block uses therunBatchAssignhelper, which deliberately omits theshowToastcall.planner.board.tsxdoes callshowToast(\Failed to assign ${failedCards.length} of ${cards.length} cards: ${reason}`, "error"), but nothing asserts it. Either mockshowToastin the batch-rollback test and assert the call, or add an integration test that mountsBoardRoutewith a mockedassignCard`.a575a01ccf08d660ae32CI green. All three findings from rounds 1 and 2 remain unaddressed — re-raising verbatim for the final time. If these aren't resolved next push, the loop hits MAX_ROUNDS and escalates to human review.
behavior
board.tsx:174–186—setAnchorKeyis still called at lines 180 and 183 inside the functional updater passed tosetSelectedKeys. React requires updater functions to be pure (no side effects). StrictMode double-invokes them, so the anchor double-toggles on every cmd-click. Move bothsetAnchorKeycalls outside the updater:test-gap
board.test.tsx— Still no test for "drag of a non-selected card while a selection exists drags only that card." The AC requires it. Add: cmd-click cards 1 and 2 to build a selection, then drag card 3 (unselected) to reviewer —onAssignmust receive[card3]only, not the full stack.test-gap
board.test.tsx:846–954— TherunBatchAssignhelper omits theshowToast(...)call thatplanner.board.tsx:210makes on partial failure (Failed to assign N of M cards: <reason>). Nothing asserts the toast. Either mockshowToastin the batch-rollback test and assert the call, or promote the test to mountBoardRoutewith a mockedassignCard.08d660ae3253afaff0a9CI green. All three findings from rounds 1–3 remain unaddressed — re-raising verbatim for the last time before human escalation.
behavior
board.tsx(cmd-click handler) —setAnchorKeyis still called at both branches inside the functional updater passed tosetSelectedKeys. React requires updater functions to be pure (no side effects); StrictMode double-invokes them, so the anchor double-toggles on every cmd-click. Move bothsetAnchorKeycall-sites outside the updater:test-gap
board.test.tsx— Still no test for "drag of a non-selected card while a selection exists drags only that card." Add: cmd-click cards 1 and 2 to build a selection, then drag card 3 (unselected) to reviewer —onAssignmust receive[card3]only, not the full stack.test-gap
board.test.tsx— TherunBatchAssignhelper still omits theshowToast(...)call thatplanner.board.tsxmakes on partial failure. The batch-rollback test only asserts card positions; nothing verifies"Failed to assign N of M cards: <reason>"is surfaced. Either mockshowToastand assert it, or mountBoardRoutewith a mockedassignCard.All three round 1–4 findings addressed. Pure updater pattern is correct and well-commented; the non-selected-card-drag test and the toast spy assertion are both present and complete. CI green.
CI green. All three round 1–4 findings addressed: pure updater (
nextAnchorpattern, comment explains why), non-selected card drag test, and toast spy assertion in the batch-rollback test.e53b45fe65525c40dc6b90216805117c4d66c2be7c4d66c2be8e8225f1e1efac8d3b625075c0df58