fix(rollback): clean worktree + sprint_task_executions on rollbackClaim #19

Merged
janpeter merged 1 commit from fix/rollback-cleans-worktree-and-executions into main 2026-05-27 14:04:48 +02:00
Owner

Probleem

rollbackClaim in src/tools/wait-for-job.ts resetten alleen de claude_jobs row naar QUEUED. Niets cleant:

  • de worktree (/home/agent/.scrum4me-agent-worktrees/<jobId>/)
  • de sprint_task_executions rows die getFullJobContext net had aangemaakt voor SPRINT-jobs
  • lease_until op claude_jobs

Resultaat: één transient failure = permanent stuck job. Elke retry hit Worktree path already exists of Unique constraint failed on (sprint_job_id, task_id).

Live observed (2026-05-27 13:05 CEST)

SPRINT_IMPLEMENTATION job cmpnyil26000pvi7rjjncv5sz:

  1. Claimed door worker -5
  2. Worktree + 11 sprint_task_executions aangemaakt
  3. Claude sub-agent dispatch: API Error: Overloaded (Anthropic 529, infra-capacity)
  4. Exit 1 → rollback claim ... reason=claude_exit_1_without_status_update
  5. Worktree + executions bleven hangen
  6. 5× retry → alle 5 falen op Worktree path already exists
  7. Both worker replicas marked UNHEALTHY → run-agent.sh halted
  8. Job stuck tot handmatige cleanup (DELETE sprint_task_executions + rm -rf worktree)

Is niet eenmalig — élke transient claude-fout (network blip, OOM, rate-limit) leidt tot dezelfde cascade. Op single-worker pre-multi-worker viel het minder op (geen 2e replica om de mess te zien); op multi-worker is dit production-blokkerend.

Fix

rollbackClaim doet nu, in volgorde:

  1. Lookup job (kind + product_id) vóór de UPDATE — nodig voor cleanup-decisions
  2. UPDATE claude_jobs: zelfde als voorheen + extra lease_until = NULL (was stuk in pre-fix)
  3. SPRINT-only: prisma.sprintTaskExecution.deleteMany({ where: { sprint_job_id }}) — verse retry kan dan createMany zonder unique-violation
  4. Worktree-cleanup voor élke kind: resolveRepoRoot(product_id) + bestaande removeWorktreeForJob({repoRoot, jobId}) — die helper doet git worktree remove --force (cleant bare-repo registration in /var/cache/repos/<repo>.git/worktrees/) + git branch -D voor de niet-gepushte branch

Alle cleanup-stappen zijn best-effort (try/catch, log via claimLog, nooit throw). Een gefaalde cleanup mag de rollback zelf niet blokkeren — claude_jobs.status = QUEUED heeft prioriteit zodat de queue niet vastloopt.

Testplan na merge

  1. Trigger update_mcp_worker flow op scrum4me-srv (cache-busted rebuild)
  2. Kunstmatige test:
    • Enqueue een SPRINT-job
    • Simuleer Claude-fail: docker exec <worker> pkill -9 -f claude tijdens execution
    • Verifieer in DB: claude_jobs.status='QUEUED', lease_until=NULL, 0 rows in sprint_task_executions WHERE sprint_job_id=<id>
    • Verifieer worktree-dir weg: docker exec <worker> ls /home/agent/.scrum4me-agent-worktrees/
    • Volgende worker-poll moet de job kunnen re-claimen zonder errors
  3. End-to-end: laat een echte sprint draaien onder normale belasting; geen stuck-state bij eerste transient Anthropic fail

Niet in scope

  • 529-detectie + langere backoff in run-agent.sh — separate PR op scrum4me-docker (voorkomt UNHEALTHY-cascade bij capaciteitspiek-momenten, zelfs met deze fix)
  • AGENT_MAX_FAILURES bump (cosmetisch zodra deze fix + 529-backoff staan)
  • Backfill voor historische stuck rows (handmatige cleanup case-by-case)

Rollback

Revert de commit + update_mcp_worker flow opnieuw → rollbackClaim valt terug op pre-fix gedrag (UPDATE-only). Geen schema-changes, geen migraties, geen data-impact.

## Probleem `rollbackClaim` in `src/tools/wait-for-job.ts` resetten alleen de `claude_jobs` row naar QUEUED. Niets cleant: - de worktree (`/home/agent/.scrum4me-agent-worktrees/<jobId>/`) - de `sprint_task_executions` rows die `getFullJobContext` net had aangemaakt voor SPRINT-jobs - `lease_until` op `claude_jobs` Resultaat: **één transient failure = permanent stuck job**. Elke retry hit `Worktree path already exists` of `Unique constraint failed on (sprint_job_id, task_id)`. ## Live observed (2026-05-27 13:05 CEST) SPRINT_IMPLEMENTATION job `cmpnyil26000pvi7rjjncv5sz`: 1. Claimed door worker -5 2. Worktree + 11 sprint_task_executions aangemaakt 3. Claude sub-agent dispatch: `API Error: Overloaded` (Anthropic 529, infra-capacity) 4. Exit 1 → `rollback claim ... reason=claude_exit_1_without_status_update` 5. Worktree + executions **bleven hangen** 6. 5× retry → alle 5 falen op `Worktree path already exists` 7. Both worker replicas marked UNHEALTHY → run-agent.sh halted 8. Job stuck tot handmatige cleanup (DELETE sprint_task_executions + rm -rf worktree) Is niet eenmalig — élke transient claude-fout (network blip, OOM, rate-limit) leidt tot dezelfde cascade. Op single-worker pre-multi-worker viel het minder op (geen 2e replica om de mess te zien); op multi-worker is dit production-blokkerend. ## Fix `rollbackClaim` doet nu, in volgorde: 1. **Lookup job** (kind + product_id) vóór de UPDATE — nodig voor cleanup-decisions 2. **UPDATE claude_jobs**: zelfde als voorheen + extra `lease_until = NULL` (was stuk in pre-fix) 3. **SPRINT-only**: `prisma.sprintTaskExecution.deleteMany({ where: { sprint_job_id }})` — verse retry kan dan `createMany` zonder unique-violation 4. **Worktree-cleanup voor élke kind**: `resolveRepoRoot(product_id)` + bestaande `removeWorktreeForJob({repoRoot, jobId})` — die helper doet `git worktree remove --force` (cleant bare-repo registration in `/var/cache/repos/<repo>.git/worktrees/`) + `git branch -D` voor de niet-gepushte branch Alle cleanup-stappen zijn **best-effort** (try/catch, log via `claimLog`, nooit throw). Een gefaalde cleanup mag de rollback zelf niet blokkeren — `claude_jobs.status = QUEUED` heeft prioriteit zodat de queue niet vastloopt. ## Testplan na merge 1. Trigger `update_mcp_worker` flow op scrum4me-srv (cache-busted rebuild) 2. Kunstmatige test: - Enqueue een SPRINT-job - Simuleer Claude-fail: `docker exec <worker> pkill -9 -f claude` tijdens execution - Verifieer in DB: `claude_jobs.status='QUEUED'`, `lease_until=NULL`, 0 rows in `sprint_task_executions WHERE sprint_job_id=<id>` - Verifieer worktree-dir weg: `docker exec <worker> ls /home/agent/.scrum4me-agent-worktrees/` - Volgende worker-poll moet de job kunnen re-claimen zonder errors 3. End-to-end: laat een echte sprint draaien onder normale belasting; geen stuck-state bij eerste transient Anthropic fail ## Niet in scope - 529-detectie + langere backoff in `run-agent.sh` — separate PR op `scrum4me-docker` (voorkomt UNHEALTHY-cascade bij capaciteitspiek-momenten, zelfs met deze fix) - AGENT_MAX_FAILURES bump (cosmetisch zodra deze fix + 529-backoff staan) - Backfill voor historische stuck rows (handmatige cleanup case-by-case) ## Rollback Revert de commit + `update_mcp_worker` flow opnieuw → rollbackClaim valt terug op pre-fix gedrag (UPDATE-only). Geen schema-changes, geen migraties, geen data-impact.
Pre-fix `rollbackClaim` only reset the claude_jobs row to QUEUED. The
worktree at `/home/agent/.scrum4me-agent-worktrees/<jobId>` and (for
SPRINT_IMPLEMENTATION) the just-created `sprint_task_executions` rows
were left in place. Any retry then hit:
  - `Error: Worktree path already exists. Call removeWorktreeForJob first.`
  - `Unique constraint failed on (sprint_job_id, task_id)` from createMany

= permanent stuck job after ANY transient failure.

Live observed 2026-05-27 ~13:05 CEST: SPRINT job claimed, claude
returned `API Error: Overloaded` (Anthropic 529 capacity), exit 1,
rollback fired, then 5 retry attempts all failed on stale-state, both
worker replicas marked UNHEALTHY → manual cleanup required.

Fix:
- Look up job's kind + product_id BEFORE the UPDATE
- After UPDATE (now also clears lease_until):
  - SPRINT_IMPLEMENTATION → deleteMany sprint_task_executions
  - Any kind → resolveRepoRoot + removeWorktreeForJob (already exists,
    does `git worktree remove --force` so bare-repo registration is
    cleaned too, plus branch -D for unpushed branches)
- Best-effort: cleanup errors logged via claimLog but never block the
  rollback itself
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
janpeter/scrum4me-mcp!19
No description provided.