feat(tasks): add archive on merge#2441
Conversation
Greptile SummaryAdds an opt-in "Archive on merge" setting that automatically archives a task when its PR is detected as merged, and redirects away from archived/missing task views to prevent stale UI states. The feature is off by default.
Confidence Score: 5/5Safe to merge — the core archive logic is well-guarded against double-archiving and retroactive archiving, navigation fires only after the archive RPC resolves, and the view guard cleanly prevents stale task views. The previously flagged concerns (double-archive race, premature navigation, retroactive archiving of old merged tasks, unhandled rejection in the sync path) are all addressed in this revision. The one remaining note — that a PR first observed as already-merged is invisible to the batch-sync archive path — is a minor edge case with limited user impact, not a defect in the happy path. No files require special attention; task-manager.ts carries the most logic complexity but it is sound.
|
| Filename | Overview |
|---|---|
| apps/emdash-desktop/src/renderer/features/tasks/stores/task-manager.ts | Core archive-on-merge logic: double-archive guard added, navigation moved after archive RPC, newlyMergedPrs filter prevents retroactive archiving, error handling added — but newly-added already-merged PRs are invisible to the sync path. |
| apps/emdash-desktop/src/renderer/features/tasks/view.tsx | Adds archivedAt guard to canActivate so navigating to an archived task redirects to the project view; also guards auto-provision from running on archived tasks. |
| apps/emdash-desktop/src/renderer/features/tasks/hooks/useTaskSettings.ts | Extends TaskSettingsModel and the hook return with archiveOnMerge, updateArchiveOnMerge, and resetArchiveOnMerge — mirrors existing field patterns correctly. |
| apps/emdash-desktop/src/renderer/features/settings/components/TaskSettingsRows.tsx | Adds ArchiveOnMergeRow component following the same pattern as other settings rows (reset button + toggle switch), no issues. |
| apps/emdash-desktop/src/renderer/features/settings/components/SettingsPage.tsx | Registers the new ArchiveOnMergeRow in the task settings section — mechanical wiring, no issues. |
| apps/emdash-desktop/src/main/core/settings/schema.ts | Adds archiveOnMerge: z.boolean() to taskSettingsSchema — straightforward schema addition, no issues. |
| apps/emdash-desktop/src/main/core/settings/settings-registry.ts | Adds archiveOnMerge: false as default — correctly defaults the feature off, consistent with other boolean task settings. |
Sequence Diagram
sequenceDiagram
participant E as Event Bus
participant TM as TaskManagerStore
participant RPC as rpc
participant Nav as Navigation
Note over E,Nav: Path 1 — real-time prUpdatedChannel
E->>TM: "prUpdatedChannel({ pr })"
TM->>TM: update task.prs (runInAction)
TM->>TM: _archiveTaskIfMerged(task, [pr])
TM->>TM: check task.archivedAt (guard 1)
TM->>RPC: appSettings.get('tasks')
RPC-->>TM: settings
TM->>TM: check task.archivedAt + archiveOnMerge (guard 2)
TM->>RPC: tasks.archiveTask(projectId, taskId)
RPC-->>TM: success
TM->>Nav: _leaveTaskViewAfterArchive(taskId)
Note over E,Nav: Path 2 — prSyncProgressChannel (batch sync)
E->>TM: "prSyncProgressChannel({ status: 'done' })"
TM->>RPC: pullRequests.getPullRequestsForTask(...)
RPC-->>TM: prs (current state)
TM->>TM: snapshot previousPrStatuses
TM->>TM: update task.prs (runInAction)
TM->>TM: compute newlyMergedPrs (transition filter)
TM->>TM: _archiveTaskIfMerged(task, newlyMergedPrs)
TM->>RPC: tasks.archiveTask(projectId, taskId)
RPC-->>TM: success
TM->>Nav: _leaveTaskViewAfterArchive(taskId)
Note over E,Nav: canActivate guard (on navigation)
Nav->>TM: "canActivate({ projectId, taskId })"
TM->>TM: check taskStore exists + archivedAt
TM-->>Nav: redirect to project if archived/missing
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
apps/emdash-desktop/src/renderer/features/tasks/stores/task-manager.ts:286-291
**Newly-added merged PRs bypass archive in the sync path**
`newlyMergedPrs` requires `previousPrStatuses.get(pr.url) !== undefined`, so a PR that is first observed in `merged` state (e.g., the user manually links an already-merged PR, or the app starts up and the first sync response already shows it as `merged`) will not trigger auto-archive from the `_reloadPrsForTask` path. The `prUpdatedChannel` path has no such restriction and would handle it there, but only if a live push event is received for that PR after it was linked. If the PR was merged before it was first tracked, the merge is permanently invisible to the sync path.
Reviews (3): Last reviewed commit: "fix(tasks): handle PR sync archive error..." | Re-trigger Greptile
Description
Screenshot/Recording (if applicable)
https://streamable.com/co0i1k
Checklist
messages and, when possible, the PR title