fix(board): review findings in the archive cap, drag path and docs - #350
Conversation
…on#318) Findings from a review pass over the merged kanban board: - recentArchived treated a non-finite limit as zero, so the Unlimited mode rendered an empty Archived column under a badge showing the full count. Slice to tasks.length instead; the unit matrix now bridges resolveBoardArchiveLimit into recentArchived so the seam stays covered. - The drop handler merged drag-time preview ids into the drop-time task list unvalidated, so a task archived, deleted or moved project mid-drag consumed a store slot with a dead id: a live card vanished from tasks, or undefined itself did and the next render threw. The merge now lives in mergeReorderedGroup, returns null on any stale id, and the drop snaps back instead of writing. - The reorder preview was keyed by projectId alone, so every other same-project group in any lane or column applied it and rendered zero cards under a lit drop ring for the whole drag. The preview now carries lane and column, and only the origin group applies it. - Nothing cleared the preview when the pointer left the origin group, so the drop-lands-here ring outlived the gesture it described. - setDrag wrote a fresh snapshot per pointermove and nothing in the board tree was memoized, so a drag reconciled every column, group and card at input frequency. Columns, groups and cards are memoized now, ctx/workPrefs/handlers are identity-stable, and the four drop targets are module singletons, so a move that only moves the ghost re-renders BoardView itself and stops there. - The Inactive rail lit every row on any settle/createPr target; a row now lights only when the hovered command is its own column's. - boardLanes counted a task whose project left the profile, forcing agent dividers onto columns whose visible cards were one agent; it reads the filtered liveTasks now. Refs simion#318 Co-Authored-By: Claude Code <noreply@anthropic.com>
…imion#318) Number("") is 0, so clearing the custom Kanban archived-column limit field to retype it stored 0, emptied the Archived column mid-edit, and put a stuck "0" back into the controlled field. The input keeps a local draft now; the store only ever sees a parseable number, and blur returns the stored value. Refs simion#318 Co-Authored-By: Claude Code <noreply@anthropic.com>
The Kanban section still said project sub-headers hide on single-project groups (they have rendered always since the GH simion#318 feedback landed) and that exactly two drags are wired (drop-on-Settled and drop-on-In-review shipped two more commands). Both sentences now describe the code a reader will find. Refs simion#318 Co-Authored-By: Claude Code <noreply@anthropic.com>
simion
left a comment
There was a problem hiding this comment.
All nine findings are real and correctly fixed, but ctx in BoardView is still a plain object literal instead of being memoized, so it changes identity on every pointermove and defeats the memoization this PR's performance section relies on. Wrap it in useMemo over [agents, useBranchAsTaskName, workPrefs] before merging. Thanks for the contribution!
simion
left a comment
There was a problem hiding this comment.
Verified locally on top of main (9ce447e): clean merge (only docs/ui.md auto-merged), tsc -b clean, npm test 2568 passing, board spec 8/8 and settings spec 101/101 on a rebuilt e2e binary with a fresh seed.
The three findings that land in code I wrote are all real, and the rail one is the sharpest:
each row of the Inactive column carries its own data-board-cell/data-column so hiding a column never takes its command with it,
and I never checked that only the hovered row lights up.
Making each DragTarget a module singleton whose kind names exactly one column fixes the highlight and the identity churn in one move, which is the right shape.
mergeReorderedGroup returning null rather than writing a stale merge is the correct call.
Worth noting it also covers the empty-preview case: previewIds.every(...) is vacuously true on [], and inserted then stays false, so that returns null too.
Thanks for going back over your own feature this carefully, the memoization pass especially.
…rag sees #350 fixed a reorder preview keyed by projectId alone: every other group of the same project applied it, matched none of the ids, and rendered zero cards under a lit drop ring for the length of the gesture. It shipped without a test of its own, and the PR said so. The reason there was none is that the evidence does not survive the gesture. On pointerup the preview clears and the cards come back, so a whole-gesture drag, which is all `pointerDrag` could do, sees a correct board before and a correct board after. `pointerDrag` now takes `hold`, which stops one event short and leaves the pointer down over the target, and `pointerRelease` finishes it somewhere harmless. The new case holds a drag inside the fakeagent lane and asserts the same project's fakecapture lane, in the same column, still renders its card. It releases over a column that is not a drop target, so nothing is written and the case leaves the board as it found it. Verified by control, not by the suite going green: re-keying the preview on projectId alone makes this case and only this case fail, with the other lane rendering [] instead of its card. The first control attempt passed with the bug supposedly reinstated, because the patch script used str.replace with no assertion and silently matched nothing. A control that cannot fail is worth exactly as much as no control. Refs #350 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WBZ9SY1zTBpPW33QAekZC1
|
Merged. Thank you for this one in particular: going back over your own feature with this much rigour is rarer than writing it in the first place, and seven of the nine findings are things a reviewer reading the diff cold would not have caught. The rail finding is the one I am most glad you chased. I picked up the gap you flagged yourself, the same-project multi-lane blanking, in 150628d. Confirmed by control rather than by the suite going green: re-keying the preview on projectId alone makes that case, and only that case, fail with the other lane rendering One small note on Verified before merging: clean merge onto main, |
Nine findings from a review pass over the kanban board that shipped in #339.
Correctness
recentArchivedsliced a non-finite limit to zero while the badge showed the full count.tasks, orundefineditself did and the next render threw.boardLanescounted a task whose project had left the profile, forcing agent dividers onto columns whose visible cards were a single agent.Number("")is 0), emptying the Archived column mid-edit and sticking a0back into the field.docs/ui.mdsaid project sub-headers hide on single-project groups and that exactly two drags are wired; the code always renders the headers and wires four commands.Performance
setDragwrote a fresh snapshot per pointermove and nothing in the board tree was memoized, so a drag reconciled every column, group and card at input frequency in WKWebView.memo-ized now,ctx/workPrefs/handlers are identity-stable, and the four drop targets are module singletons, so a move that only moves the ghost re-rendersBoardViewitself and stops there.Testing
tsc -bclean.npm test2561 passing, including new unit cases for the Infinity slice, the resolver-to-render bridge, andmergeReorderedGroup.Refs #318
🤖 Generated with Claude Code