Skip to content

feat: delete worktrees when Linear issue moves to Done or Cancelled#980

Open
cyrusagent wants to merge 3 commits intocypack-910from
cypack-958
Open

feat: delete worktrees when Linear issue moves to Done or Cancelled#980
cyrusagent wants to merge 3 commits intocypack-910from
cypack-958

Conversation

@cyrusagent
Copy link
Copy Markdown
Contributor

@cyrusagent cyrusagent commented Mar 13, 2026

Assignee: @PaytonWebber (payton)

Summary

When a Linear issue transitions to "Done" or "Cancelled" state, automatically clean up associated worktrees. This replaces the previous external CYHOST-triggered cleanup flow, which no longer works with multi-repo sessions (CYPACK-910).

  • Detect Linear issue state changes via new IssueStateChangeMessage type and isIssueStateChangeWebhook type guard
  • Implement GitService.deleteWorktree() supporting both single-repo and multi-repo layouts
  • Wire EdgeWorker to stop active sessions and delete worktrees when issues are completed/canceled

Implementation

New message type (packages/core)

  • Added "issue_state_change" to the MessageAction union
  • Created IssueStateChangeMessage interface with newStateType field
  • Created LinearIssueStateChangePlatformData platform data type
  • Added isIssueStateChangeMessage() type guard

Webhook detection (packages/core)

  • Created IssueStateChangeWebhook type for Issue/update webhooks where stateId changed in updatedFrom
  • Created isIssueStateChangeWebhook() type guard — checked before isIssueTitleOrDescriptionUpdateWebhook to avoid conflicts

Message translation (packages/linear-event-transport)

  • Added translateIssueStateChange() to LinearMessageTranslator
  • Extracts new state type from data.state.type (e.g., "completed", "canceled")

Worktree deletion (packages/edge-worker)

  • GitService.deleteWorktree(workspaceBaseDir, issueIdentifier):
    • Detects worktrees under the path (single-repo: root dir is worktree; multi-repo: subdirs are worktrees)
    • Runs git worktree remove --force for each before rm -rf
    • Graceful no-op when directory doesn't exist
    • Prunes stale worktree references
  • EdgeWorker.handleIssueStateChangeMessage():
    • Only acts on completed and canceled states
    • Stops all active sessions before worktree deletion
    • Searches across all repositories if cached repository not found

Edge cases handled

  • Issue reopened after Done — worktree already deleted, new session re-creates it (existing behavior)
  • Session still running — stopped before deletion
  • Worktree already deleted manually — graceful no-op with info log
  • git worktree remove fails — warns and continues with directory deletion

Testing

  • 6 type guard tests (isIssueStateChangeWebhook)
  • 4 translator tests (completed, canceled, precedence over title change, non-state updates)
  • 4 GitService deletion tests (missing dir, single-repo, multi-repo, error handling)
  • All 545 edge-worker tests passing
  • TypeScript type checking clean
  • Linting clean

Links

cyrusagent and others added 2 commits March 13, 2026 15:35
Add IssueStateChangeMessage type and isIssueStateChangeWebhook type guard
to detect when Linear issues transition to completed/canceled states.
Implement GitService.deleteWorktree() for single-repo and multi-repo
layouts. Wire EdgeWorker to stop active sessions and clean up worktrees
on state change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@cyrusagent cyrusagent marked this pull request as ready for review March 13, 2026 22:37
The current branch still uses the Map<string, AgentSessionManager>
pattern. The consolidation to a single instance happens on cypack-910
and will apply at merge time.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

1 participant