feat(issues): move a work item to another project#229
Conversation
Adds a "Move" action on the work-item detail page that rehomes an issue into another project in the same workspace. - store: IssueStore.MoveToProject does the move atomically in one transaction — allocates a fresh per-project sequence id, repoints project_id, resets project-scoped fields (state, parent, estimate) and drops project-scoped associations (labels, cycle/module membership, relations, both directions), and repoints links/attachments to the target project. - service/handler/route: IssueService.Move validates that the target is a different project the caller can reach in the same workspace (cross-workspace targets 404, same-project 400); exposed at POST .../issues/:pk/move/. - web: a Move button opens a project picker modal; on success it navigates to the work item's new project URL. issueService.move wraps the endpoint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughAdds a work-item move flow from the issue detail page through a new authenticated POST API route. The backend validates project and workspace access, reassigns the issue transactionally to the target project, and returns the moved issue. The page shows a move modal and navigates to the new issue URL after success. ChangesIssue move workflow
Sequence Diagram(s)sequenceDiagram
participant IssueDetailPage
participant issueService_move as issueService.move
participant IssueHandler_Move as IssueHandler.Move
participant IssueService_Move as IssueService.Move
participant IssueStore_MoveToProject as IssueStore.MoveToProject
IssueDetailPage->>issueService_move: select target project
issueService_move->>IssueHandler_Move: POST /move/ with target_project_id
IssueHandler_Move->>IssueService_Move: move issue in workspace
IssueService_Move->>IssueStore_MoveToProject: transactional move
IssueStore_MoveToProject-->>IssueService_Move: updated issue and sequence id
IssueService_Move-->>IssueHandler_Move: moved issue
IssueHandler_Move-->>issueService_move: IssueApiResponse
issueService_move-->>IssueDetailPage: navigate to moved issue URL
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 golangci-lint (2.12.2)level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies" Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
apps/api/internal/handler/issue_move_test.go (1)
26-46: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider asserting the allocated
sequence_id.The target is pre-seeded with one issue specifically so the moved item gets a fresh sequence, but the test never asserts
moved.SequenceID(expected2). Adding that assertion would lock in the per-project sequence-allocation contract this PR introduces.💚 Suggested assertion
require.Equal(t, target.ID, moved.ProjectID) require.Nil(t, moved.StateID) + require.Equal(t, 2, moved.SequenceID, "moved issue should get a fresh per-project sequence") }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api/internal/handler/issue_move_test.go` around lines 26 - 46, The move test in issue_move_test should also verify the newly allocated per-project sequence for the moved issue. After reloading the moved model in the move flow, assert that moved.SequenceID matches the expected fresh sequence value (2) in addition to the existing project/state checks. Use the existing moved Issue lookup and the pre-seeded target setup to keep this contract covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/api/internal/store/issue.go`:
- Around line 188-211: MoveToProject currently clears the moved issue’s
parent_id but leaves any child issues still pointing to the moved issue, which
can break same-project hierarchy rules. Update the MoveToProject flow in
issue.go to also handle rows whose parent_id matches issueID, using the existing
tx/model logic to either rehome those children to targetProjectID or
clear/update their parent linkage consistently before commit. Keep the fix
localized around the issue move transaction and use the existing Issue model
operations to ensure no child remains in the source project with a parent in
another project.
- Around line 212-220: `MoveToProject` updates links and attachments but leaves
`github_issue_syncs.project_id` behind, which breaks project-scoped summary
lookups after a move. Add the same project_id repointing step for the
`model.GithubIssueSync` rows in `issue.go` alongside the existing
`tx.Model(...).Where("issue_id = ?", issueID)` updates so moved issues remain
visible in project queries.
---
Nitpick comments:
In `@apps/api/internal/handler/issue_move_test.go`:
- Around line 26-46: The move test in issue_move_test should also verify the
newly allocated per-project sequence for the moved issue. After reloading the
moved model in the move flow, assert that moved.SequenceID matches the expected
fresh sequence value (2) in addition to the existing project/state checks. Use
the existing moved Issue lookup and the pre-seeded target setup to keep this
contract covered.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 34d42b9c-a5ee-49d4-9e33-140716a8a276
📒 Files selected for processing (7)
apps/api/internal/handler/issue_archive.goapps/api/internal/handler/issue_move_test.goapps/api/internal/router/router.goapps/api/internal/service/issue.goapps/api/internal/store/issue.goapps/web/src/pages/IssueDetailPage.tsxapps/web/src/services/issueService.ts
Addresses CodeRabbit review on the move feature: - detach child work items on move — rows with parent_id = the moved issue stay in the source project, so leaving them parented would point a child at a parent in another project and break the same-project hierarchy invariant. - repoint github_issue_syncs.project_id to the target project — those rows are filtered by project_id in project summary queries, so a stale value would make the moved issue's linked PRs disappear from those lookups. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What
Closes #171. Adds a Move action on the work-item detail page that rehomes an issue into another project in the same workspace.
How
IssueStore.MoveToProjectperforms the move atomically in a single transaction:project_id,state_id,parent_id,estimate_point_id),IssueService.Movevalidates the target is a different project the caller can reach in the same workspace. Cross-workspace targets → 404, same-project → 400. Exposed atPOST /api/workspaces/:slug/projects/:projectId/issues/:pk/move/.issueService.movewraps the endpoint.Fields that are not project-scoped (name, description, priority, dates, type, assignees) are preserved.
Testing
internal/handler/issue_move_test.go: success path (project_id repointed, state reset, labels cleared, row lives in target), same-project rejected (400), missing target (400), cross-workspace rejected (404). Fullgo test ./...green.AI assistance
This change was produced with the help of Claude Code (Claude Opus 4.8). AI-assisted commits carry a
Co-Authored-Bytrailer.Summary by CodeRabbit