Plan: /agent replan Directive#
Summary#
Add a replan directive that resets an active task's phase statuses, closes any open PR (deleting the branch), records a replan_triggered event, restores in-refinement labels, and restarts the pipeline from refine. Includes guardrails for already-in-refine and completed/archived tasks.
Files#
| File | Action | Description |
|---|---|---|
src/directive/dto/directive.dto.ts | modify | Add 'replan' to ALLOWED_DIRECTIVES array |
src/directive/directive.service.ts | modify | Add replan handler: validate state, close PR, delete branch, reset task, enqueue refine |
src/phase-router/phase-router.service.ts | modify | Add replan to resolvePhase() (return 'refine'), VALID_DIRECTIVES, and route handler block |
src/github/github.service.ts | modify | Add closePR(owner, repo, prNumber) and deleteBranch(owner, repo, branch) methods |
src/hooks/phase-hooks.service.ts | modify | Add cleanupLabelsForReplan() helper: remove refined, in-review, agent-blocked; add in-refinement |
src/directive/directive.service.spec.ts | modify | Add tests: normal replan, already-in-refine guard, completed-task guard |
src/phase-router/phase-router.service.spec.ts | modify | Add test: replan directive resolves to 'refine' phase |
src/github/github.service.spec.ts | modify | Add tests for closePR() and deleteBranch() |
Steps#
- Add
'replan'toALLOWED_DIRECTIVESindirective.dto.ts— theDirectiveValueunion auto-derives. - Add
closePR()anddeleteBranch()methods toGitHubServiceusing the REST API (PATCH /pulls/:numberwithstate: 'closed'andDELETE /git/refs/heads/:branch). - Add
cleanupLabelsForReplan()toPhaseHooksServicethat removesrefined,in-review,agent-blockedand addsin-refinement. Swallow 404s on label removal. - Add
replanhandler inDirectiveService.applyDirective():- Reject if
task.statusissucceeded,failed, orcomplete(terminal) — post explanatory comment. - No-op if
task.currentPhase === 'refine'andtask.status === 'active'— post explanatory comment. - If
task.prNumberexists: close PR and delete branch viaGitHubService. Catch "already closed/deleted" as success; on other errors, log warning and proceed. - Call
taskStateService.resetForRestart(task)to reset all phases and setcurrentPhase='refine'. - Clear
task.prNumberandtask.prBranch(stale PR metadata). - Save task, record
replan_triggeredevent viaEventService. - Call
cleanupLabelsForReplan()viaPhaseHooksService. - Enqueue
refinephase viaInternalAdapterService. - Post confirmation comment with details (noting if a PR was closed or a run was interrupted).
- Reject if
- Add
'replan'toVALID_DIRECTIVESarray andresolvePhase()inPhaseRouterService— return'refine'forreplan. Addreplancase in the directive handling block (similar torestartbut skip the "active process" rejection since replan should proceed regardless). - Add
'replan'entry tobuildConfirmationMessage()inDirectiveService. - Write unit tests:
directive.service.spec.ts(normal replan with PR cleanup, already-in-refine guard, completed-task rejection),phase-router.service.spec.ts(resolvePhase returns'refine'),github.service.spec.ts(closePR, deleteBranch).
Verification#
npm run lintpasses with zero warnings.npm run testpasses all existing + new unit tests.npm run buildcompiles without errors.
Risks#
- In-flight Claude run interruption: The current architecture does not track child process PIDs across the service boundary. The
replanhandler resets task state and enqueuesrefine, but the in-flight process may still be running. Mitigation: the phase-router's "phase already active" guard will prevent double-execution; the old process will eventually time out or complete to a no-op state. Document this limitation. - PR close/branch delete race: If the PR was merged between the check and the close call, the close will 404. Mitigation: treat 404/422 on close and delete as success.