TM-2: Moving tasks between boards - #54
Open
wdembinski wants to merge 7 commits into
Open
wdembinski wants to merge 7 commits into
wdembinski wants to merge 7 commits into
Conversation
wdembinski
commented
Sep 19, 2026
Owner
- User should be able to move tasks between boards/projects (the same way as it is done in the Atlassian JIRA).
- Boards, projects should be the same feature. Currently when creating a task I see I can select a Board and select a Project - this is the same. If not in the code, fix it to be the same.
`moveTaskToBoard(taskId, toBoardId)` moves a card onto a different board in one transaction, modelled on `createTicketTx`: a destination that owns tickets always allocates a FRESH number off its own counter (never the source's old number), while a keyless destination (Personal, or any board with no prefix) freezes the key exactly as it was. `epicTaskId`/`milestoneId` are cleared (both name rows scoped to the source project), `projectTagId` follows the card to its new board, `"order"` becomes the destination's own next slot, and `task_events`/ `task_activity` are re-pointed so a card's timeline never splits across two projects. store.ts has zero vitest coverage (better-sqlite3's addon is built for Electron's ABI, confirmed failing under plain Node here), so the scenarios live in scripts/verify-move-task-to-board.mjs, run under ELECTRON_RUN_AS_NODE like every other store verification script. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
`task:setBoard(taskId, boardId)` moves a card onto a different board — the sibling of `task:setProject`, but where that one only tags a card `setBoard` actually moves `Task.projectId`. Guards mirror `task:assignAgent`'s style: refuses a card mid-run, one resting on a plan-driven board (its cards come from the plan file, not a hand move), and one synced from JIRA or GitHub (its board is recomputed from its Project tag on every sync, so a move here would not survive the next poll). The destination just has to exist — no `ownsBoard` gate, since every project is a valid board now, Personal included. A no-op if the destination is already the card's own board. Delegates to `store.moveTaskToBoard` (added in the prior step) and fires `task:changed` plus `project:tasksChanged` for BOTH the source and destination boards, so the source drops the card and the destination gains it on the same tick. Registered in `IpcApi` and marked `'relay'` in `ipcRelay.ts`, same as `task:setProject`. `ipc.ts` has no test file (`registerIpcHandlers` needs a real Electron app/BrowserWindow), so the handler's guards are mirrored — line for line, same shape `verify-jira-move.mjs` uses for `task:move` — in a new section of `verify-move-task-to-board.mjs`, against the real store: the running-card, plan, JIRA and GitHub refusals, the unknown-destination refusal, and that a successful move fires exactly the three events (one `task:changed`, two `project:tasksChanged`) naming the right boards. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
board:scopes (desktop) and its web mirror in BoardScreen.tsx now return Personal plus every other project, dropping the ownsBoard(project) filter. A bare repo or a keyless personal-space project is a board too now; its cards simply carry no ticket key. Plan-driven projects also surface as boards as a consequence — their plan-step cards remain unmovable per Phase 2's refusal. Extends verify-move-task-to-board.mjs with a section mirroring board:scopes, asserting the plan-driven and keyless projects earlier sections created (previously dropped by ownsBoard) are now included. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Repurposes TaskDetailsCell's filing "Project" dropdown into a single "Project / Board" control that calls task:setBoard, removing the old redundancy between filing a card and moving it between boards. The control is disabled with an explanatory tooltip for a plan/jira/github card or while a run is live, mirroring task:setBoard's own guards, and shows a short "Re-keyed to <newKey>" caption when a move gives the card a new ticket key. Threaded a boards: BoardScope[] prop down from TaskDetail.tsx to both the desktop (MyTasks.tsx) and web (BoardScreen.tsx) call sites, replacing the old projects prop there. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Collapse the Add-task dialog's separate "Board" and "Project (optional)" selects into one "Project / Board" picker: Personal (default) plus every project. addTaskPlan now routes on the destination's ownsTickets capability (BoardScope.ownsTickets, newly exposed by board:scopes) rather than on "is it Personal" — a keyless non-Personal board writes an ordinary card (task:create, tagged with the board itself), a ticket-owning board writes a native ticket (ticket:create), regardless of which one is named Personal. Drops the now-redundant filingProjects/selectFilingProjects plumbing in MyTasks.tsx and BoardScreen.tsx that only ever fed the dialog's old Project field — it excluded repo-less ticket projects by construction, so it could never have reached ticket:create for them anyway. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… end Adds section 7 to verify-move-task-to-board.mjs: one card, walked through the exact flow plan step 7 describes — an adhoc card moves onto a freshly created prefixed board (asserting the fresh key + source:'ticket' from step 2), then onto a freshly created keyless board (asserting the key freezes at what the FIRST move gave it, not what it was born with). Sections 1 and 3 each already covered half of this on separate cards but never chained the two moves on one, which is the specific regression this section guards. Both hops go through the setBoard mirror so project:tasksChanged is re-checked for both boards on each hop, and board:scopes is re-read afterwards to confirm both new boards appear. Proved live by mutating moveTaskToBoardTx three ways and reverting each time: inverting the destPrefix guard reddens sections 1-2 (then crashes the run); freezing with null instead of the old key/number reddens section 3 twice and section 7's chained-freeze check; dropping the task_events/task_activity project follow reddens both history checks in sections 1 and 3. Findings recorded in the script's docstring. Ran the monorepo gates fresh (turbo --force, no cache): format:check, typecheck (9/9 tasks, including @tm/web:typecheck against the new `boards` prop and relay entry), vitest (3720 tests / 222 files), and build (6/6 tasks) all green. No lint script exists in this repo (no eslint config anywhere) so that gate has nothing to run. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…enter Step 8 of "moving tasks between boards" — the plan's last step. Updates the "Project tag vs agent project" entry, written before this branch and now stale: it didn't say what a board move (task:setBoard/store.moveTaskToBoard) does to projectId vs projectTagId vs agentProjectId, and nothing warned against ever merging projectId/projectTagId back into one column. Records three things for whoever touches this next: the colour stripe keeps reading projectTagId unchanged because a board move keeps it in sync with projectId in the same transaction; delegation (agentProjectId) is untouched by a board move — still written only by Assign Agent, so a card can still live on Personal while delegated elsewhere; and why the projectId/projectTagId split can't be collapsed — it's load-bearing for delegation, the JIRA/GitHub reconcilers' Personal-only fallback (resolveOwningBoardProject), and every board-scope query. Also updates "My Tasks" to say a card's board is no longer fixed to Personal. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.