Skip to content

feat(issues): move a work item to another project#229

Merged
martian56 merged 2 commits into
mainfrom
feat/move-issue
Jun 25, 2026
Merged

feat(issues): move a work item to another project#229
martian56 merged 2 commits into
mainfrom
feat/move-issue

Conversation

@martian56

@martian56 martian56 commented Jun 25, 2026

Copy link
Copy Markdown
Member

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

  • storeIssueStore.MoveToProject performs the move atomically in a single transaction:
    • allocates a fresh per-project sequence id in the target project,
    • repoints project_id,
    • resets project-scoped fields (state_id, parent_id, estimate_point_id),
    • drops project-scoped associations (labels, cycle/module membership, relations in both directions),
    • repoints links and attachments to the target project.
  • service / handler / routeIssueService.Move validates the target is a different project the caller can reach in the same workspace. Cross-workspace targets → 404, same-project → 400. Exposed at POST /api/workspaces/:slug/projects/:projectId/issues/:pk/move/.
  • web — a "Move" button opens a project-picker modal; on success the page navigates to the work item's new project URL. issueService.move wraps the endpoint.

Fields that are not project-scoped (name, description, priority, dates, type, assignees) are preserved.

Testing

  • New 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). Full go test ./... green.
  • Browser-verified end-to-end: moved a work item from one project to another — breadcrumb + sequence id update to the new project, state/labels/parent cleared, priority/due date preserved, no console errors.

AI assistance

This change was produced with the help of Claude Code (Claude Opus 4.8). AI-assisted commits carry a Co-Authored-By trailer.

Summary by CodeRabbit

  • New Features
    • Added an issue “Move” action from the issue detail page, including a destination project picker and confirmation flow that navigates to the moved issue.
    • Introduced a new authenticated API endpoint to move issues between projects within the same workspace.
  • Bug Fixes
    • Moving an issue now updates its project and ordering, resets project-specific fields, detaches child issues, and removes incompatible associations/links.
    • Clearer handling for invalid requests (e.g., missing target project or attempting to move within the same project).
  • Tests
    • Added integration coverage for successful moves and key failure scenarios.

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>
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 12ec1973-9b15-4a37-a2e8-c4c7aea25935

📥 Commits

Reviewing files that changed from the base of the PR and between 728f5cc and e9d2a90.

📒 Files selected for processing (3)
  • apps/api/internal/handler/issue_move_test.go
  • apps/api/internal/store/issue.go
  • apps/web/src/pages/IssueDetailPage.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/api/internal/handler/issue_move_test.go
  • apps/web/src/pages/IssueDetailPage.tsx

📝 Walkthrough

Walkthrough

Adds 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.

Changes

Issue move workflow

Layer / File(s) Summary
API route and handler
apps/api/internal/router/router.go, apps/api/internal/handler/issue_archive.go
Adds the authenticated POST move route and handler that reads target_project_id, calls IssueService.Move, and maps same-project, access, and generic errors to HTTP responses.
Move orchestration
apps/api/internal/service/issue.go
Adds ErrMoveSameProject and IssueService.Move, which loads the issue, checks target-project access, delegates the move to storage, reloads the issue, and records the project change activity.
Transactional reparenting
apps/api/internal/store/issue.go
Adds IssueStore.MoveToProject, which reassigns the issue to the target project in one transaction, allocates a new sequence id, clears project-scoped fields, removes source-project associations, and repoints links, attachments, and sync rows.
Handler move tests
apps/api/internal/handler/issue_move_test.go
Adds success and error-case coverage for the move endpoint, including project updates, cleared state and labels, same-project rejection, missing target validation, and cross-workspace rejection.
Web move action and modal
apps/web/src/pages/IssueDetailPage.tsx, apps/web/src/services/issueService.ts
Adds the issue detail move action, project-picker modal, move request submission, and navigation to the moved issue after a successful API response.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

enhancement, API, UI

Poem

🐇 I hopped an issue to a new green tree,
With labels tucked and state set free.
A modal blinked, “Pick your patch!”
Then off it went—no fence to catch.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, conventional-commit compliant, and clearly describes moving a work item to another project.
Description check ✅ Passed The description includes the linked issue, summary, implementation, testing, and AI disclosure; only optional template sections are missing.
Linked Issues check ✅ Passed The PR implements the move workflow in store, service, route, and UI, matching the linked issue's requirements.
Out of Scope Changes check ✅ Passed The changes stay focused on the move feature and its tests, with no unrelated scope detected.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/move-issue

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 @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
apps/api/internal/handler/issue_move_test.go (1)

26-46: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider 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 (expected 2). 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

📥 Commits

Reviewing files that changed from the base of the PR and between 6a9b740 and 728f5cc.

📒 Files selected for processing (7)
  • apps/api/internal/handler/issue_archive.go
  • apps/api/internal/handler/issue_move_test.go
  • apps/api/internal/router/router.go
  • apps/api/internal/service/issue.go
  • apps/api/internal/store/issue.go
  • apps/web/src/pages/IssueDetailPage.tsx
  • apps/web/src/services/issueService.ts

Comment thread apps/api/internal/store/issue.go
Comment thread apps/api/internal/store/issue.go
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>
@martian56 martian56 merged commit b04404d into main Jun 25, 2026
9 checks passed
@martian56 martian56 deleted the feat/move-issue branch June 25, 2026 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] Allow moving a work item to another project

1 participant