AI Agents SDE Task Viewer
      • Context
      • Plan
      • Prd
  1. Home
  2. AgentSDE
  3. agent-core
  4. gh-458
  5. plan
  6. plan.md
plan.md(2.8 KB)· Apr 13, 2026· 2 min read
  • Summary
  • Files
  • Steps
  • Verification
  • Risks

Plan: Verify PhaseRouterService.route() Idempotency for BullMQ Retry#

Summary#

Audit and harden PhaseRouterService.route() and executePhase() so that BullMQ stalled-job retries produce no duplicate side effects — no duplicate comments, DB rows, or Claude invocations. This unblocks AW-13 (BullMQ migration).

Files#

FileActionDescription
src/phase-router/phase-router.service.tsmodifyAdd idempotency guards for createTask() race and comment dedup
src/hooks/phase-hooks.service.tsmodifyGuard onPhaseComplete() hooks against duplicate comment posting
src/task-state/task-state.service.tsmodifyConvert createTask() to upsert or wrap with try/catch for unique constraint
src/phase-router/phase-router.service.spec.tsmodifyAdd retry-idempotency tests: call route() twice, assert no duplicates
src/hooks/phase-hooks.service.spec.tsmodifyAdd tests for duplicate-call safety on hook methods

Steps#

  1. Guard createTask() against unique-constraint race: Wrap createTask() call in route() with a try/catch that falls back to findByIssueAndRepo() on duplicate key error, or convert to upsert pattern in TaskStateService.
  2. Guard onPhaseComplete() hooks against duplicate comments: Before posting phase-completion comments in onPlanComplete(), onDeliverComplete(), and onCompoundComplete(), check if a matching comment already exists on the issue/PR, or check task phase status is not already 'complete' before calling the hook.
  3. Guard onPhaseBlocked() against duplicate blocked comments: Add a check in PhaseHooksService.onPhaseBlocked() to skip posting if an agent-blocked comment was already posted for the current phase.
  4. Verify invoke() cannot double-enqueue: Confirm the phase-status 'active' guard in route() (line ~272) prevents a second executePhase() call, and add a defensive early-return in executePhase() if phase is already 'active' when re-entered.
  5. Write retry-idempotency unit tests for route(): Call route() twice with identical event; assert createTask() called once, invoke() called once, hooks called once, no duplicate comments.
  6. Write retry-idempotency unit tests for hooks: Call onPhaseComplete() twice for the same phase; assert comment posted once, labels applied once.

Verification#

  • npm run test passes with new retry-idempotency test cases
  • npm run lint passes with zero warnings
  • npm run build compiles cleanly

Risks#

  • ⚠️ The createTask() upsert must preserve existing task state on retry — must not reset phase columns if the task already exists.
  • ⚠️ Comment-existence checks add GitHub API calls; use task-state flags instead where possible to avoid rate-limit pressure.
ContextPrd