Scrum4Me/docs/runbooks/worker-idempotency.md
Janpeter Visser b39c3ec2e1
docs(cleanup): archief verouderde plannen, backlog en root-duplicaten (#191)
* docs(cleanup): archief verouderde plannen, backlog en root-duplicaten

- 6 plans naar docs/old/plans/ (PBI-11/75/78, user-settings-store, Local github setup, lees-de-readme — laatste was verkeerde repo)
- docs/backlog/ naar docs/old/backlog/ (pre-MCP statische registry; live werk loopt via Scrum4Me-MCP)
- 6 root-level duplicaten naar docs/old/ (functional, {pbi,story,task}-dialog, product-backlog, backlog)
- 2 landing plans (niet uitgevoerd) krijgen archived: true frontmatter — blijven op plek maar uit INDEX
- scripts/generate-docs-index.mjs: skip docs/old/** + skip archived: true
- CLAUDE.md: rijen docs/backlog/, docs/plans/<key>-*.md, docs/manual/ weg; Track B-sectie verwijderd
- README.md / CHANGELOG.md / docs/plans/v1-readiness.md: link-fixes naar nieuwe locaties

Verify groen (lint + typecheck + 718 tests). docs/INDEX.md geregenereerd.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(cleanup): registreer handmatige verplaatsingen en fix referenties

- 4 plans verplaatst naar docs/old/plans/ (M10-qr-pairing-login, auto-pr-deploy-sync, docs-restructure-ai-lookup, v1-readiness)
- 3 archive-plans verplaatst naar docs/old/plans/ (archive-map nu leeg)
- ST-1114-copilot-reviews + 3 research-docs naar nieuwe docs/Ideas/ map
- Duplicaat docs/old/2026-04-27-m8-realtime-solo.md verwijderd (origineel zit in docs/old/plans/)
- Link-fixes naar nieuwe locaties:
  - CHANGELOG.md → docs/old/plans/v1-readiness.md
  - docs/runbooks/deploy-control.md → docs/old/plans/auto-pr-deploy-sync.md (2x)
  - docs/runbooks/worker-idempotency.md → docs/old/plans/auto-pr-deploy-sync.md
  - docs/plans/docs-restructure-pbi-spec.md → docs/old/plans/docs-restructure-ai-lookup.md (4x text + 2x href)
- docs/INDEX.md geregenereerd (96 docs, was 100)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:46:00 +02:00

194 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: "Worker idempotency & job-status protocol"
status: active
audience: [ai-agent, contributor]
language: nl
last_updated: 2026-05-09
when_to_read: "Vóór het implementeren of debuggen van Claude-CLI-worker logica die `update_job_status` aanroept."
---
# Worker idempotency & job-status protocol
Beschrijft hoe de Scrum4Me-worker `ClaudeJob.status` moet zetten op basis
van `VerifyResult` × git-diff-staat × branch-staat. Doel: voorkom
status-divergentie zoals geconstateerd in de **PBI-33 batch (5-5-2026
22:22)** waarin werk dat al gemerged was via PR #102/#103/#104 leidde
tot inconsistente combinaties van `verify=EMPTY → FAILED` en
`verify=DIVERGENT → DONE`.
---
## Beslissingsboom
Aan het einde van een story-job, ná `verify`-pass:
| `verify_result` | netto diff t.o.v. `origin/main` | branch al gemerged | → `ClaudeJob.status` | `Task.status` |
|---|---|---|---|---|
| `ALIGNED` of `PARTIAL` | nieuwe commit aanwezig | n.v.t. | **`DONE`** | `DONE` |
| `EMPTY` | leeg (niets gewijzigd) | werk zit al op `origin/main` | **`SKIPPED`** | `DONE` |
| `EMPTY` | leeg, maar werk staat **niet** op origin | n.v.t. | **`FAILED`** (`error: "verify produced no output"`) | `IN_PROGRESS` (handmatig onderzoeken) |
| `DIVERGENT` | aanwezig, maar identiek aan al-gemergde branch | ja (PR closed/merged) | **`SKIPPED`** | `DONE` |
| `DIVERGENT` | aanwezig, niet matchend met main | nee | **`FAILED`** (`error: "verify divergent — handmatige review"`) | `IN_PROGRESS` |
| (compile-fail, test-fail, push-fail, exception) | n.v.t. | n.v.t. | **`FAILED`** met concrete `error` | `IN_PROGRESS` |
| (gebruiker drukt cancel) | n.v.t. | n.v.t. | **`CANCELLED`** | `TO_DO` |
### Vuistregels
- **`SKIPPED`** = "geen netto-output, maar geen fout" — werk was al
gedaan vóór deze job draaide. Task mag op `DONE` omdat het beoogde
resultaat in main aanwezig is.
- **`FAILED`** is gereserveerd voor échte fouten: code-fouten,
test-failures, push-fouten, onverklaarde diff. Niet voor
"implementatie was al gedaan".
- **`DONE`** alleen bij `ALIGNED`/`PARTIAL` mét nieuwe commit op de
feature-branch. Een lege `DIVERGENT` op een al-gemergde branch is
géén `DONE`.
---
## StoryLog-verplichting
Tijdens elke job moet de worker `story_logs`-entries schrijven via de
MCP-tools, anders is de Sync-tab leeg:
| Wanneer | MCP-tool | Inhoud |
|---|---|---|
| Bij claim | `log_implementation` | "Start implementatie van T-XXX. Branch X. Plan: …" |
| Per commit | `log_commit` | hash + message + samenvatting van wijzigingen |
| Na verify | `log_test_result` | status `PASSED` of `FAILED` + samenvatting van checks |
In **PBI-33 batch** zijn deze tools **niet** aangeroepen — `story_logs`
voor ST-1208/1209/1210 is leeg. Worker MAG geen job afronden zonder
minimaal één `log_implementation` (start) en één `log_test_result`
(eind).
---
## Idempotency-protocol (vóór schrijven)
Bij claim van een job:
1. Lees `Task.implementation_plan` — beschrijft expliciet welke files
gewijzigd moeten worden.
2. Vergelijk de huidige `origin/main`-staat met die plan-instructies:
- Bestaat het bestand al met de beoogde inhoud?
- Bestaat de migratie al?
- Bevat de relevante codepad de nieuwe symbolen/types?
3. Bij **volledige hit**: roep `log_implementation` met inhoud "Werk
reeds aanwezig op origin/main vanaf commit X (Y)." Sla
verify-stap over en zet `JobStatus.SKIPPED`. Task naar `DONE`.
4. Bij **gedeeltelijke hit**: log de bevindingen via
`log_implementation` en doe alleen het resterende werk. Eindig met
`DONE` (`ALIGNED` of `PARTIAL`) als je netto-output hebt.
Dit voorkomt dubbele commits op al-gemergde branches en houdt
`pushed_at` semantisch correct (alleen gevuld als er werkelijk
gepusht is).
---
## Case-study: PBI-33 (5-5-2026 22:22)
PBI-33 ("PLAN_CHAT — gebruikersvragen over plan") werd opnieuw aangemaakt
nadat de feature al via een eerdere batch was gemerged onder cuid-style
story-codes (`ST-bsjoqjnr`, `ST-p6d1odh0`, …). De worker draaide om
22:22 en zag:
- **T-533** (`ST-1208` schema-werk): diff = leeg → `verify=EMPTY`
`Job.FAILED` met error "Implementatie reeds voltooid en gemerged".
Volgens het nieuwe protocol had dit **`SKIPPED`** moeten zijn.
- **T-534…538**: diff niet leeg op feature-branches `feat/story-7pl4dsb6`
en `feat/story-0vtnydpi` (al-gemergde branches uit eerdere PR's) →
`verify=DIVERGENT``Job.DONE` met `pushed_at=now()`. Volgens het
nieuwe protocol had dit ook **`SKIPPED`** moeten zijn — branch was
al closed/merged, geen nieuwe commit.
- **`story_logs` voor ST-1208/1209/1210 is leeg** — geen
`log_implementation`, geen `log_commit`, geen `log_test_result`.
Drie protocol-overtredingen die we met deze runbook + de nieuwe
`SKIPPED`-status aanpakken.
---
## Config doorgeven aan Claude Code (PBI-67)
`wait_for_job` levert sinds PBI-67 een `config`-object mee in de
response. **De runner** (`scrum4me-docker/bin/run-one-job.ts`) leest deze
config en bouwt per geclaimde job de juiste Claude CLI-flags. Eén
Claude-invocation per job — niet één lange sessie die zelf claimt.
```bash
claude \
-p "$PROMPT" \
--model "$MODEL" \
--permission-mode "$PERMISSION_MODE" \
${EFFORT:+--effort $EFFORT} \
--allowedTools "$ALLOWED_TOOLS" \
--mcp-config /opt/agent/mcp-config.json \
--add-dir /opt/agent \
--output-format text
```
Waar:
| Variabele | Bron in response | Voorbeeld |
|---|---|---|
| `PROMPT` | `getKindPromptText(kind)` met `$PAYLOAD_PATH` vervangen | (kind-prompt-md) |
| `MODEL` | `config.model` | `claude-sonnet-4-6` |
| `PERMISSION_MODE` | `config.permission_mode` | `bypassPermissions` |
| `EFFORT` | `mapBudgetToEffort(config.thinking_budget)` (null = vlag weg) | `medium` |
| `ALLOWED_TOOLS` | `config.allowed_tools.join(',')` | `Read,Edit,…,mcp__scrum4me__update_task_status,…` |
### Claude CLI 2.1.x flag-correctie
De Claude CLI 2.1.x heeft géén numerieke `--thinking-budget` en géén
`--max-turns`. Mapping:
| `config.thinking_budget` | CLI-flag |
|---|---|
| 0 | (geen `--effort` flag) |
| 1-6000 | `--effort medium` |
| 6001-12000 | `--effort high` |
| 12001-24000 | `--effort xhigh` |
| >24000 | `--effort max` |
`config.max_turns` blijft **audit-only** — wordt gesnapshot in
`ClaudeJob.requested_*` voor cost-attribution maar niet doorgegeven aan
Claude. De resolver in `lib/job-config.ts` exporteert
`mapBudgetToEffort(budget)` voor deze mapping.
### Verwachte CLI-aanroep per kind (defaults zonder overrides)
| Kind | Model | thinking_budget | --effort | permission_mode |
|---|---|---|---|---|
| `IDEA_GRILL` | sonnet-4-6 | 12000 | high | acceptEdits |
| `IDEA_MAKE_PLAN` | opus-4-7 | 24000 | xhigh | acceptEdits |
| `PLAN_CHAT` | sonnet-4-6 | 6000 | medium | acceptEdits |
| `TASK_IMPLEMENTATION` | sonnet-4-6 | 6000 | medium | bypassPermissions |
| `SPRINT_IMPLEMENTATION` | sonnet-4-6 | 6000 | medium | bypassPermissions |
### Wie doet wat in de runner-architectuur
| Component | Verantwoordelijkheid |
|---|---|
| `bin/run-agent.sh` | Daemon-loop, exponential backoff, UNHEALTHY-marker, log-rotation, TOKEN_EXPIRED-detectie via exit-code 3 of stdout-regex |
| `bin/run-one-job.ts` | **Claim** (tryClaimJob + LISTEN-fallback 270s), config-resolve (getFullJobContext), payload schrijven, **CLI-flags bouwen**, spawn `claude`, lease-renewal voor SPRINT (setInterval 60s), rollbackClaim bij Claude exit≠0 zonder update_job_status, cleanup |
| `claude` (per invocation) | Voert exclusief de geclaimde job uit. **Mag geen** `wait_for_job`, `check_queue_empty`, of `job_heartbeat` aanroepen — zit niet in `allowed_tools` |
Volledige resolver-uitleg + override-cascade staat in
[job-model-selection.md](./job-model-selection.md). Refactor-plan:
[queue-loop-extraction.md](../plans/queue-loop-extraction.md).
---
## Referenties
- Enum: `prisma/schema.prisma``enum ClaudeJobStatus`
- Mapping: `lib/job-status.ts` (DB↔API) en
`components/shared/job-status.ts` (label + kleur)
- Status-data-cleanup: `app/api/cron/cleanup-agent-artifacts/route.ts`
- KPI-aggregatie: `lib/insights/agent-throughput.ts` (terminal_7d
inclusief SKIPPED)
- Gerelateerd plan: `docs/old/plans/auto-pr-deploy-sync.md` Deel D
- PBI-67 resolver: `scrum4me-mcp/src/lib/job-config.ts` + `lib/job-config.ts`
(Sync-tab toont per-Story job-status incl. SKIPPED)