Scrum4Me/docs/plans/queue-loop-extraction.md
Janpeter Visser 00c5045558
feat(PBI-4/ST-006): mirror job-config naar webapp + runbook-fix CLI-flags (#171)
Spiegelt de scrum4me-mcp wijzigingen naar de Scrum4Me web-app zodat
enqueue-laag (lib/job-config-snapshot.ts) en claim-laag dezelfde
defaults gebruiken. Plus runbook-correctie van een eerder gedocumenteerde
maar niet-bestaande Claude CLI-flag.

- T-25: lib/job-config.ts — mapBudgetToEffort export + KIND_DEFAULTS
  .allowed_tools voor TASK/SPRINT/IDEA_GRILL/IDEA_MAKE_PLAN omgezet
  naar expliciete lijsten zonder wait_for_job/check_queue_empty/
  get_idea_context. Comment-block over CLI-flag-mapping en sync met
  scrum4me-mcp.
- T-26: docs/runbooks/worker-idempotency.md sectie "Config doorgeven aan
  Claude Code (PBI-67)" herschreven. --thinking-budget vervangen door
  --effort (mapping-tabel toegevoegd); --max-turns geschrapt (CLI heeft
  die flag niet — audit-only). Sectie "Wie doet wat in de runner-
  architectuur" toegevoegd.
- T-27: docs/runbooks/job-model-selection.md — notes over max_turns,
  thinking_budget en allowed_tools onder de matrix. Nieuwe sectie
  "Runner-architectuur" met verwijzing naar plan + worker-idempotency.
- T-28: __tests__/lib/job-config.test.ts (nieuw) — 22 tests:
  mapBudgetToEffort grenswaarden + KIND_DEFAULTS.allowed_tools structurele
  checks + cascade regression.

Plus: docs/plans/queue-loop-extraction.md (geschreven in plan-mode,
nu gepubliceerd in repo).

Verify: lint OK, typecheck OK, 587 tests in 78 files passed.
Build niet lokaal uitgevoerd (vereist DATABASE_URL voor "Collecting page
data" — diff raakt geen API-route).

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

22 KiB

Queue-loop verplaatsen van Claude naar runner

Context

Vandaag draait scrum4me-docker/bin/run-agent.sh één lange claude -p-sessie met de seed-prompt "draai queue leeg". Claude roept zelf herhaaldelijk wait_for_job aan binnen die ene CLI-invocation. Het probleem: alle jobs in zo'n run gebruiken dezelfde CLI-flags — terwijl PBI-67 (lib/job-config.ts) per job een ander model, permission-mode, thinking-budget en allowed-tools voorschrijft. Een IDEA_MAKE_PLAN job moet met Opus + plan-mode draaien, een TASK_IMPLEMENTATION met Sonnet + bypassPermissions; binnen één Claude-proces is die switch niet te maken.

Doel: één Claude-invocation per geclaimde job. De runner (buiten Claude) claimt, leest job.config, bouwt de juiste CLI-flags, spawnt claude -p voor precies die ene job, wacht op exit, claimt de volgende. Claude zelf doet niet meer aan claim-management.

Kritische CLI-correctie (vóór alles)

claude --version op het host-systeem en in de Docker base = 2.1.132. Geverifieerde flag-set:

Beschreven in runbook Bestaat Vervanging
--thinking-budget <int> --effort {low,medium,high,xhigh,max}
--max-turns <int> géén equivalent (gebruik --max-budget-usd als budget-cap of laat cosmetisch)
--model, --permission-mode, --allowedTools, --mcp-config, --output-format ongewijzigd

docs/runbooks/worker-idempotency.md:113-150 documenteert flags die niet bestaan. PBI-67-velden thinking_budget en max_turns zijn vandaag al cosmetisch (de seed-prompt-loop geeft ze ook niet door). Deze refactor is het natuurlijke moment om dat goed te zetten.

Beslissing: voeg mapBudgetToEffort(budget: number): string | null toe in beide job-config.ts-spiegels:

  • 0null (flag niet meegeven)
  • 1..6000"medium"
  • 6001..12000"high"
  • 12001..24000"xhigh"
  • >24000"max"

max_turns blijft audit-only — comment in KIND_DEFAULTS toevoegen, runner negeert het.

Architectuur in één pagina

run-agent.sh   (daemon, backoff, health, log-rotation, token-expiry detectie)
   └── tsx /opt/agent/bin/run-one-job.ts          ← één iteratie = één job
          ├── 1. quota-probe (was Claude's verantwoordelijkheid)
          ├── 2. resetStaleClaimedJobs(userId)
          ├── 3. tryClaimJob(userId, tokenId)
          │      └── null? LISTEN scrum4me_changes met deadline 270s; bij timeout → exit 0
          ├── 4. getFullJobContext(jobId)         ← public export uit scrum4me-mcp
          ├── 5. attachWorktreeToJob (alleen TASK_IMPLEMENTATION)
          ├── 6. Schrijf payload → /tmp/job-<id>/payload.json
          ├── 7. Bouw CLI-args uit config + map effort
          ├── 8. setInterval(60s) lease-renewal   ← alleen SPRINT_IMPLEMENTATION
          ├── 9. spawnSync('claude', [...]); cwd = worktree_path
          ├── 10. try/finally rollbackClaim + releaseLocksOnTerminal als spawn faalt
          ├── 11. clearInterval; await prisma.$disconnect()
          └── 12. exit met claude's code (3 = TOKEN_EXPIRED)

Claude zelf:

  • krijgt geen wait_for_job in --allowedTools — vangrail tegen recursieve claims.
  • krijgt geen "draai queue leeg"-prompt meer — per kind een eigen prompt-template.
  • doet alleen job-uitvoering: tool-calls voor logging, status-updates, verify, en update_job_status aan het einde.

Hoe run-one-job.ts aan jobId + config komt

Vier stappen, allemaal binnen het Node-proces — geen aparte CLI-call, geen MCP-stdio-roundtrip.

1. Wie ben ik (auth)

import { getAuth } from '/opt/scrum4me-mcp/src/auth.js'
const { userId, tokenId } = await getAuth()

getAuth() (scrum4me-mcp/src/auth.ts:11-40) hashed process.env.SCRUM4ME_TOKEN (SHA-256), zoekt in ApiToken op token_hash, en returnt { userId, tokenId, username, isDemo }. Token komt uit Docker compose .env.

2. Welke job (claim)

import { tryClaimJob, resetStaleClaimedJobs } from '/opt/scrum4me-mcp/src/tools/wait-for-job.js'

await resetStaleClaimedJobs(userId)              // requeu/FAIL stale CLAIMED-jobs (lease verlopen)
const jobId: string | null = await tryClaimJob(userId, tokenId)

tryClaimJob (scrum4me-mcp/src/tools/wait-for-job.ts:358-447) doet één atomic transactie:

  1. SELECT cj.id FROM claude_jobs cj LEFT JOIN tasks t ... LEFT JOIN sprint_runs sr ... WHERE user_id = $userId AND status = 'QUEUED' AND (kind IN idea-kinds OR (kind IN task/sprint AND sprint_run.status IN QUEUED|RUNNING)) ORDER BY created_at ASC LIMIT 1 FOR UPDATE OF cj SKIP LOCKED
  2. UPDATE claude_jobs SET status='CLAIMED', claimed_by_token_id=$tokenId, claimed_at=NOW(), plan_snapshot=..., lease_until=NOW()+INTERVAL '5 minutes' WHERE id=$jobId
  3. Optioneel: SprintRun QUEUED → RUNNING bij eerste claim.

FOR UPDATE SKIP LOCKED garandeert dat parallelle workers nooit dezelfde job pakken — concurrency-safety op DB-niveau.

Bij null: LISTEN scrum4me_changes met deadline 270s; bij notify op claude_job_enqueued-event opnieuw tryClaimJob. Bij timeout exit 0 (run-agent.sh sleept 2s en herstart).

3. Welke config (resolve op claim-moment)

import { getFullJobContext } from '/opt/scrum4me-mcp/src/tools/wait-for-job.js'  // export-fix in Fase 1
const ctx = await getFullJobContext(jobId)

getFullJobContext (scrum4me-mcp/src/tools/wait-for-job.ts:449-788) doet één Prisma-findUnique met joins (task → story → pbi/sprint, idea, product met preferred_*-velden), en roept dan resolveJobConfig(...) aan:

const config = resolveJobConfig(
  {
    kind: job.kind,
    requested_model: job.requested_model,                // snapshot bij enqueue
    requested_thinking_budget: job.requested_thinking_budget,
    requested_permission_mode: job.requested_permission_mode,
  },
  {
    preferred_model: job.product.preferred_model,        // product-override
    thinking_budget_default: job.product.thinking_budget_default,
    preferred_permission_mode: job.product.preferred_permission_mode,
  },
  job.task ? { requires_opus: job.task.requires_opus } : undefined,
)

resolveJobConfig (lib/job-config.ts:97-124) past de override-cascade toe (eerste match wint):

  1. task.requires_opus === truemodel = 'claude-opus-4-7'
  2. job.requested_* (al ingevuld bij enqueue door lib/job-config-snapshot.ts in de Next.js webapp)
  3. product.preferred_*
  4. KIND_DEFAULTS[kind]

Resultaat zit in ctx.config:

ctx.config = {
  model: 'claude-sonnet-4-6',
  thinking_budget: 6000,
  permission_mode: 'bypassPermissions',
  max_turns: 50,                    // audit-only, geen CLI flag
  allowed_tools: ['Read','Edit','Write','Bash','Grep','Glob','mcp__scrum4me__update_task_status', ...],
}

Plus de kind-specifieke velden: task, story, pbi, sprint, idea, product, worktree_path, branch_name, task_executions[] (sprint), prompt_text (idea).

4. Bouw CLI-args en spawn

import { mapBudgetToEffort } from '/opt/scrum4me-mcp/src/lib/job-config.js'
import { getKindPromptText } from '/opt/scrum4me-mcp/src/lib/kind-prompts.js'

const promptText = getKindPromptText(ctx.kind).replace('$PAYLOAD_PATH', payloadPath)

const args = [
  '-p', promptText,
  '--model',           ctx.config.model,
  '--permission-mode', ctx.config.permission_mode,
  '--allowedTools',    ctx.config.allowed_tools.join(','),
  '--mcp-config',      '/opt/agent/mcp-config.json',
  '--add-dir',         '/opt/agent',
  '--output-format',   'text',
]
const effort = mapBudgetToEffort(ctx.config.thinking_budget)
if (effort) args.push('--effort', effort)

spawnSync('claude', args, {
  stdio: 'inherit',
  cwd: ctx.worktree_path ?? ctx.primary_worktree_path ?? '/opt/agent',
})

Twee resolver-passages: bewust ontwerp

[Webapp enqueue]                          [Runner claim]
actions/createJob                         tryClaimJob(jobId)
   ↓                                         ↓
snapshotFromConfig (lib/job-config-snapshot.ts)
                                          getFullJobContext
                                             ↓
                                          resolveJobConfig
                                          (leest requested_* terug)
   ↓                                         ↓
DB: claude_jobs.requested_*               ctx.config → CLI flags

De enqueue-resolver legt de keuze vast als snapshot in ClaudeJob.requested_* (audittrail). De claim-resolver leest die snapshot terug — als een operator handmatig requested_model heeft gewijzigd tussen enqueue en claim (bv. "ad-hoc Opus voor deze ene story"), wint die wijziging. Bewust ontwerp.

Implementatie — 3 fases, 3 PR's

Fase 1 — scrum4me-mcp (publieke API + prompts + KIND_DEFAULTS)

Bestanden:

  • scrum4me-mcp/src/tools/wait-for-job.ts — regel 449 async function getFullJobContextexport async function getFullJobContext. Niets anders aan de body wijzigen.
  • scrum4me-mcp/src/lib/idea-prompts.ts → hernoemen naar src/lib/kind-prompts.ts. Nieuwe export getKindPromptText(kind: ClaudeJobKind): string met cases voor alle vijf kinds. Behoud getIdeaPromptText als re-export voor back-compat (wait-for-job.ts roept 'm aan).
  • scrum4me-mcp/src/lib/job-config.ts:
    • Voeg mapBudgetToEffort(budget: number): string | null toe.
    • Update KIND_DEFAULTS:
      • TASK_IMPLEMENTATION.allowed_tools = expliciete lijst zonder wait_for_job/check_queue_empty/get_idea_context. Inhoud: ['Read','Edit','Write','Bash','Grep','Glob', 'mcp__scrum4me__get_claude_context','mcp__scrum4me__update_task_status','mcp__scrum4me__update_task_plan','mcp__scrum4me__log_implementation','mcp__scrum4me__log_test_result','mcp__scrum4me__log_commit','mcp__scrum4me__verify_task_against_plan','mcp__scrum4me__update_job_status','mcp__scrum4me__ask_user_question','mcp__scrum4me__get_question_answer','mcp__scrum4me__list_open_questions','mcp__scrum4me__cancel_question','mcp__scrum4me__worker_heartbeat']
      • SPRINT_IMPLEMENTATION.allowed_tools = bovenstaande + mcp__scrum4me__update_task_execution, mcp__scrum4me__verify_sprint_task. Géén mcp__scrum4me__job_heartbeat — de runner verlengt de lease (zie Fase 2). Claude hoeft hier niet aan te denken.
      • IDEA_GRILL.allowed_tools = bestaand + mcp__scrum4me__update_idea_grill_md, mcp__scrum4me__log_idea_decision, mcp__scrum4me__update_job_status, mcp__scrum4me__ask_user_question, mcp__scrum4me__get_question_answer.
      • IDEA_MAKE_PLAN.allowed_tools = bestaand + mcp__scrum4me__update_idea_plan_md, mcp__scrum4me__log_idea_decision, mcp__scrum4me__update_job_status.
    • Comment toevoegen: max_turns is audit-only (Claude CLI 2.1.x mist --max-turns). thinking_budget mapt via mapBudgetToEffort.
  • Nieuwe prompts in src/prompts/:
    • task/implementation.md — single-task workflow, payload via $PAYLOAD_PATH, expliciet géén wait_for_job, instructie voor verify-gate + update_job_status aan het einde.
    • sprint/implementation.md — sprint-workflow, Claude verwerkt task_executions[] sequentieel. Géén heartbeat-instructie nodig: de runner verlengt de lease via setInterval.
    • plan-chat/chat.md — placeholder voor PLAN_CHAT.
  • Tests in __tests__/ of vergelijkbaar: snapshot-test voor mapBudgetToEffort en de nieuwe KIND_DEFAULTS.allowed_tools-lijsten.

Verificatie van Fase 1:

cd /Users/janpetervisser/Development/scrum4me-mcp
npm run typecheck && npm test

Fase 2 — scrum4me-docker (de runner)

Bestanden:

  • Nieuw: scrum4me-docker/bin/run-one-job.ts — implementeert de stappen uit het architectuur-diagram. Imports uit /opt/scrum4me-mcp/src/:

    • getAuth (auth.ts)
    • tryClaimJob, resetStaleClaimedJobs, attachWorktreeToJob, releaseLocksOnTerminal, rollbackClaim, getFullJobContext (tools/wait-for-job.ts)
    • prisma (prisma.ts)
    • mapBudgetToEffort, type JobConfig (lib/job-config.ts)
    • getKindPromptText (lib/kind-prompts.ts)
  • LISTEN-loop: kopie van wait-for-job.ts:838-889 (270s deadline ipv 300s — ruim binnen MAX_WAIT_SECONDS).

  • Quota-probe verhuist hierheen: roep bin/worker-quota-probe.sh (bestaat al) en getWorkerSettings direct via prisma; sleep tot reset bij beneden-quota. Was voorheen Claude's verantwoordelijkheid in CLAUDE.md stappen 0.1-0.5.

  • Lease-renewal voor SPRINT_IMPLEMENTATION: setInterval(60_000, () => prisma.$executeRaw\UPDATE claude_jobs SET lease_until = NOW() + INTERVAL '5 minutes' WHERE id = ${jobId}`). Stop op spawn-exit (in finally-block). Werkt onafhankelijk van Claude's tool-call-cadans, dus ook tijdens lange synchrone Bash-calls (zoals npm install`) blijft de lease vers.

  • Token-expiry: detecteer Anthropic-auth-errors uit Claude's output én uit eigen Prisma-fouten; exit 3 → run-agent.sh schrijft TOKEN_EXPIRED marker.

  • Cleanup: prisma.$disconnect() in process.on('SIGTERM'/'exit') zodat connection-pool niet blijft hangen tussen iteraties.

  • Refactor: scrum4me-docker/bin/run-agent.sh

    • Verwijder regels 43-44 (SEED_PROMPT) en 46-49 (ALLOWED_TOOLS).
    • Vervang regels 73-79 (claude -p ... aanroep) door:
      set +e
      tsx /opt/agent/bin/run-one-job.ts > "${run_log}" 2>&1
      exit_code=$?
      set -e
      
    • Behoud: pre-flight token-check, exponential backoff (regels 106-126), UNHEALTHY na 5 fouten, log-rotation, state.json updates.
    • Pas regel 87 (token-expiry regex) aan: detecteer ook exit_code == 3 als trigger naast de stdout-regex.
  • Update: scrum4me-docker/CLAUDE.md

    • Verwijder de "operationele loop"-sectie (Claude doet die niet meer).
    • Korte sectie toevoegen: "deze container draait runner-loop in run-one-job.ts; per geclaimde job spawnt 'ie één Claude-invocation met kind-specifieke flags + prompt".
    • Behoud: project-conventions die Claude in de worktree-cwd nodig heeft.
  • Dockerfile: niets wijzigen — tsx@4 global is al geïnstalleerd (regel 39), scrum4me-mcp wordt al gecloned (regel 59-63).

Verificatie van Fase 2:

cd /Users/janpetervisser/Development/scrum4me-docker
docker compose build
docker compose up -d
# Trigger een TASK_IMPLEMENTATION via de webapp (Sonnet + bypassPermissions verwacht)
# Trigger een IDEA_MAKE_PLAN via de webapp (Opus + plan-mode verwacht)
# Logs: docker compose logs -f
# Verifieer dat per job een nieuwe Claude-invocation logt met de juiste --model en --permission-mode

Fase 3 — Scrum4Me web-app (lib + runbook)

Bestanden:

  • lib/job-config.ts — spiegel mapBudgetToEffort en de KIND_DEFAULTS-updates uit Fase 1. Comment toevoegen over CLI-mapping.
  • docs/runbooks/worker-idempotency.md — herschrijf sectie "Config doorgeven aan Claude Code" (regels 113-150): vervang --thinking-budget door --effort met mapping-tabel, schrap --max-turns, voeg toe dat de runner (bin/run-one-job.ts) verantwoordelijk is voor flag-bouw en heartbeat (niet Claude meer).
  • docs/runbooks/job-model-selection.md — voeg note toe dat max_turns audit-only is en dat de runner per job spawnt.
  • Tests: vitest snapshot voor mapBudgetToEffort (zelfde als in scrum4me-mcp Fase 1).

Verificatie van Fase 3:

npm run verify && npm run build

Logging-contract van run-one-job.ts

Alle log-regels gaan naar stdout met format <ISO-8601-UTC> [run-one-job] <message>. run-agent.sh redirect dit al naar /var/log/agent/runs/<timestamp>.log. Eén regel per event, key=value-format zodat het grep-baar blijft.

Verplichte events:

Moment Voorbeeld-regel
Quota-probe start/eind 2026-05-08T13:11:40Z [run-one-job] quota probe ok used_pct=42 limit_pct=90
Claim attempt start 2026-05-08T13:11:41Z [run-one-job] claim attempt user_id=usr_… token_id=tok_…
Claim resultaat 2026-05-08T13:11:42Z [run-one-job] claimed job_id=cl_abc kind=TASK_IMPLEMENTATION task_id=t_xyz product_id=prd_… of claim timeout after 270s — exiting 0
Resolved config (verplicht) 2026-05-08T13:11:42Z [run-one-job] config job_id=cl_abc model=claude-sonnet-4-6 permission_mode=bypassPermissions thinking_budget=6000 effort=medium max_turns=50 allowed_tools_count=20 source=kind_default (waar sourcekind_default / product_override / task_requires_opus / job_snapshot — bepaald door welke override-laag gewonnen heeft)
Worktree + payload 2026-05-08T13:11:42Z [run-one-job] worktree path=/home/agent/.scrum4me-agent-worktrees/cl_abc branch=feat/story-12345678 base_sha=abc123ef
Payload-pad 2026-05-08T13:11:42Z [run-one-job] payload written path=/tmp/job-cl_abc/payload.json size_bytes=2456
Claude spawn start (verplicht) 2026-05-08T13:11:43Z [run-one-job] spawn claude job_id=cl_abc cwd=/home/agent/.scrum4me-agent-worktrees/cl_abc args="--model claude-sonnet-4-6 --permission-mode bypassPermissions --effort medium --allowedTools <…> --mcp-config /opt/agent/mcp-config.json --add-dir /opt/agent --output-format text"
Lease-renewal tick (alleen SPRINT) 2026-05-08T13:12:43Z [run-one-job] heartbeat tick job_id=cl_abc lease_until=2026-05-08T13:17:43Z (bij errors: heartbeat error: <message>)
Claude spawn end (verplicht) 2026-05-08T13:14:21Z [run-one-job] claude done job_id=cl_abc exit_code=0 duration_ms=158234 wall_clock_seconds=158
Cleanup 2026-05-08T13:14:21Z [run-one-job] cleanup payload_removed=true prisma_disconnected=true heartbeat_stopped=true
Process exit 2026-05-08T13:14:21Z [run-one-job] exit code=0 job_id=cl_abc

Foutpaden ook expliciet:

  • claim error <message> (DB-fout vóór claim)
  • getFullJobContext error job_id=cl_abc <message> → triggert rollbackClaim + log rollback claim job_id=cl_abc reason=context_fetch_failed
  • attachWorktreeToJob error job_id=cl_abc <message> → idem
  • spawn error <errno> → process kon claude niet starten
  • Detected token-expiry: 2026-05-08T13:11:43Z [run-one-job] TOKEN_EXPIRED detected pattern="<matched-string>" exiting code=3

Implementatie-helper in run-one-job.ts:

const log = (msg: string) => console.log(`${new Date().toISOString()} [run-one-job] ${msg}`)
const logError = (msg: string) => console.error(`${new Date().toISOString()} [run-one-job] ERROR ${msg}`)

Geen JSON-logger of structured logging library — run-agent.sh parsed niets. Plain text houdt het grep-baar en consistent met de bestaande _lib.sh-log() shell-helper.

Crash-veiligheid

Failure-mode Detectie Recovery
Claim gelukt, runner crasht vóór spawn lease_until < NOW() resetStaleClaimedJobs (5 min) — automatisch
Spawn faalt (exit ≠ 0 vóór update_job_status) exit code try/finally rollbackClaim + releaseLocksOnTerminal in run-one-job
Claude crasht mid-run exit code rollbackClaim uit run-one-job; update_job_status('failed') is dan optional retry door operator
Token-expiry tijdens run regex op claude-stdout + exit 3 runner exit 3 → run-agent.sh schrijft TOKEN_EXPIRED marker → container blijft hangen voor diagnose
Runner-heartbeat faalt (DB onbereikbaar tijdens setInterval-tick) error log + lease_until verstrijkt resetStaleClaimedJobs requeu't (PBI-50 lease-driven recovery, 5 min). Mitigatie: log de Prisma-error in run-one-job zodat het opvalt in run-logs

Verificatie end-to-end

# 1. Build alle drie de repos
(cd ~/Development/scrum4me-mcp && npm run typecheck && npm test)
(cd ~/Development/Scrum4Me/.claude/worktrees/festive-jackson-78c3ff && npm run verify && npm run build)
(cd ~/Development/scrum4me-docker && docker compose build)

# 2. Lokale Docker-run
docker compose up -d
docker compose logs -f agent &

# 3. Smoke-test scenario's (via webapp of direct via prisma seed):
#    a. enqueue IDEA_GRILL  → verifieer log toont --model=claude-sonnet-4-6 + --permission-mode=plan + --effort=high
#    b. enqueue IDEA_MAKE_PLAN → verifieer --model=claude-opus-4-7 + --effort=max
#    c. enqueue TASK_IMPLEMENTATION → verifieer --model=claude-sonnet-4-6 + --permission-mode=bypassPermissions
#    d. enqueue task met requires_opus=true → verifieer --model=claude-opus-4-7
#    e. enqueue product met preferred_permission_mode='acceptEdits' → verifieer dat override doorkomt

# 4. Verifieer in DB na elke run:
#    SELECT id, kind, status, requested_model, model_id, requested_permission_mode FROM claude_jobs ORDER BY created_at DESC LIMIT 5;
#    requested_model en model_id moeten matchen (tenzij Claude zelf een ander rapporteert)

# 5. Verifieer queue-loop met meerdere jobs:
#    Vul de queue met 3 verschillende kinds; observeer in logs dat per job een nieuwe spawn gebeurt met andere flags.

Niet-doelen

  • Geen wijzigingen aan de MCP-tool-set (wait_for_job blijft beschikbaar voor handmatige dev-mode; alleen niet meer in Claude's allowedTools voor docker-runs).
  • Geen herstructurering van het ClaudeJob Prisma-schema.
  • Geen wijzigingen aan lib/job-config-snapshot.ts (enqueue-laag) — die werkt al goed; deze refactor zit volledig aan de claim/exec-kant.
  • Geen migratie van de wait_for_job-tool naar HTTP/REST — direct-import is voldoende.

Critical files

  • /Users/janpetervisser/Development/scrum4me-mcp/src/tools/wait-for-job.ts (export-fix + ref)
  • /Users/janpetervisser/Development/scrum4me-mcp/src/lib/job-config.ts (KIND_DEFAULTS + mapBudgetToEffort)
  • /Users/janpetervisser/Development/scrum4me-mcp/src/lib/idea-prompts.ts (rename → kind-prompts.ts)
  • /Users/janpetervisser/Development/scrum4me-mcp/src/prompts/task/implementation.md (nieuw)
  • /Users/janpetervisser/Development/scrum4me-mcp/src/prompts/sprint/implementation.md (nieuw)
  • /Users/janpetervisser/Development/scrum4me-docker/bin/run-one-job.ts (nieuw)
  • /Users/janpetervisser/Development/scrum4me-docker/bin/run-agent.sh (refactor)
  • /Users/janpetervisser/Development/scrum4me-docker/CLAUDE.md (operationele loop sectie weg)
  • lib/job-config.ts (spiegel)
  • docs/runbooks/worker-idempotency.md (CLI-flag fix)