Symptoom: TASK_IMPLEMENTATION jobs in een sprint-run met pr_strategy=
SPRINT kregen branch=null in claudeJob.branch, ook al maakte
attachWorktreeToJob de juiste worktree-branch (feat/sprint-<id>) aan en
returnde die in de payload-response.
Gevolg: update_job_status (na PR #43-fix) leest claudeJob.branch uit de
DB → null → valt terug op legacy `feat/job-<8>` → `git push` faalt met
"src refspec feat/job-xxx does not match any" → job FAILED → cascade-
cancel van sibling-tasks in dezelfde sprint-run. Live waargenomen voor
sprint-run cmoy9irr8000ci017fvy30lvv (T-806 FAILED, T-807-T-811
CANCELLED) ondanks dat Claude PR #174 op feat/sprint-fvy30lvv had
gemaakt.
Root cause: attachWorktreeToJob (wait-for-job.ts:205-209) update'de
alleen base_sha. Voor SPRINT_IMPLEMENTATION-kind wordt branch wel naar
DB geschreven (regel 655) maar voor TASK_IMPLEMENTATION-pad zat dat gat.
Fix: altijd branch + (indien aanwezig) base_sha schrijven naar
claudeJob in de update aan het eind van attachWorktreeToJob.
Tests: __tests__/wait-for-job-worktree.test.ts mock-prisma uitgebreid
met `claudeJob.update`. 341 tests in 38 files passed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Symptoom: TASK_IMPLEMENTATION job T-806 in een SPRINT-strategy sprint
faalde met:
push failed (unknown): error: src refspec feat/job-us3aqoup does not
match any
error: failed to push some refs to 'https://github.com/.../Scrum4Me.git'
Maar de PR was wel succesvol aangemaakt door Claude (PR #174 op
feat/sprint-fvy30lvv) — Claude commit'te in de juiste worktree-branch,
maar update_job_status's prepareDoneUpdate probeerde te pushen op een
niet-bestaande branch.
Root cause: prepareDoneUpdate(jobId, branch) accepteert een branch-arg
(meestal undefined want Claude geeft 'm niet mee) en valt terug op
`feat/job-${jobId.slice(-8)}`. Dat is het legacy pre-PBI-50 pad — voor
sprint-jobs is de werkelijke branch `feat/sprint-<id>` (PR_strategy=SPRINT)
of `feat/story-<id>` (STORY), opgeslagen in ClaudeJob.branch door
attachWorktreeToJob.
Fix:
- prepareDoneUpdate leest nu eerst ClaudeJob.branch uit de DB als de
expliciete branch-arg ontbreekt.
- Pas daarna fallback op `feat/job-<8>` (zou niet moeten voorkomen na PBI-50).
Tests: vi.mock('../src/prisma.js') toegevoegd voor de findUnique-stub.
Bestaande test "derives branchName from jobId when branch is undefined"
hernoemd naar "reads branchName from DB" met DB-mock returnt
'feat/sprint-fvy30lvv'. Plus extra test voor de legacy fallback wanneer
DB.branch ook null is.
341 tests in 38 files passed (was 340, +1).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Webapp had Prisma-schema migrated: SprintStatus.ACTIVE→OPEN,
SprintStatus.COMPLETED→CLOSED, plus new SprintStatus.ARCHIVED. Also new
TaskStatus.EXCLUDED. scrum4me-mcp Prisma client was 110 commits behind,
causing runtime errors when reading sprint.status from the live DB:
Value 'OPEN' not found in enum 'SprintStatus'
Symptom: TASK_IMPLEMENTATION jobs in QUEUED status were claimed by
tryClaimJob (raw SQL succeeds), then getFullJobContext crashed on the
findUnique with the enum error → rollbackClaim → loop forever until
UNHEALTHY (5 consecutive failures).
Fix:
- Updated vendor/scrum4me submodule to current main (3c77342).
- Re-ran sync-schema.sh → prisma/schema.prisma now has
SprintStatus { OPEN, CLOSED, ARCHIVED, FAILED } and
TaskStatus including EXCLUDED.
- src/lib/tasks-status-update.ts: ACTIVE→OPEN, COMPLETED→CLOSED.
- src/status.ts: TASK_DB_TO_API + TASK_API_TO_DB krijgen EXCLUDED entry.
- src/tools/get-claude-context.ts: status: 'ACTIVE' → status: 'OPEN'.
Tests: 340 passed (38 files). Typecheck OK.
Na merge + docker rebuild met cache-bust pakt de runner sprint-tasks weer
op zonder enum-error.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Symptoom: IDEA_GRILL en IDEA_MAKE_PLAN jobs hingen 11+ minuten zonder
update_job_status aan te roepen. Claude zag in de prompt:
- "Je bent een grill-agent voor Scrum4Me-idee {idea_code}" — letterlijke
string omdat run-one-job.ts alleen $PAYLOAD_PATH substitueert, geen
{idea_*}-vars.
- "context (meegegeven in wait_for_job-payload)" — maar Claude krijgt geen
wait_for_job-respons, want die tool zit niet meer in allowed_tools voor
idea-kinds (de runner claimt al).
- Geen instructie om $PAYLOAD_PATH te lezen — de placeholder ontbrak in
beide idea-prompts (alleen task/sprint/plan-chat hadden 'm).
Resultaat: Claude wist niet wat het te doen had, kon geen idea_id of
job_id achterhalen, en draaide tot de natuurlijke session-cap zonder
ooit de juiste tools aan te roepen.
Fix:
- grill.md en make-plan.md: vervang `wait_for_job`-references door
`scrum4me-docker/bin/run-one-job.ts` (de daadwerkelijke runner).
- Beide prompts beginnen nu met "Lees $PAYLOAD_PATH met de Read-tool"
als verplichte eerste actie. Lijst van velden die uit de payload moeten
worden bewaard (idea.id, idea.code, job_id, product.id, etc.).
- {idea_code} / {idea_title} placeholders verwijderd — alle benodigde
velden komen uit de payload, geen runner-side substitution meer nodig.
- Update_job_status-stap expliciet als "verplicht, ook bij failure".
Tests: kind-prompts.test.ts uitgebreid:
- Alle 5 kinds moeten $PAYLOAD_PATH bevatten (was alleen task/sprint/
plan-chat).
- IDEA_GRILL en IDEA_MAKE_PLAN mogen geen wait_for_job meer noemen.
- IDEA_GRILL en IDEA_MAKE_PLAN mogen geen {idea_*} placeholders meer
bevatten.
19 tests in kind-prompts.test.ts passed (was 13).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Symptoom: IDEA_GRILL job IDEA-047 werd 3x geclaimd, Claude liep telkens
succesvol (exit 0 na 600-900s) maar deed nooit update_job_status('done').
Lease verliep, retry_count >= 2 → status FAILED met "agent did not
complete job within 2 attempts".
Root cause: KIND_DEFAULTS.permission_mode='plan' voor idea-kinds en
PLAN_CHAT. In autonome batch-mode wacht plan-mode op een human "go" na
elke planning-fase — er is geen mens in de loop om te approven, dus
Claude blijft hangen en sluit netjes maar onvolledig af.
Fix:
- IDEA_GRILL.permission_mode: plan → acceptEdits
- IDEA_MAKE_PLAN.permission_mode: plan → acceptEdits
- PLAN_CHAT.permission_mode: plan → acceptEdits
De allowed_tools-lijsten doen de echte sandboxing (geen Bash, geen Edit
voor IDEA_GRILL/PLAN_CHAT, alleen Write voor IDEA_MAKE_PLAN). De
"veiligheid" van plan-mode wordt dus al door tool-allowlists geleverd —
acceptEdits is hier puur om Claude door zijn own update_job_status loop
te laten lopen zonder approval-wachttijd.
Plus: PLAN_CHAT.allowed_tools krijgt nu ook update_job_status (ontbrak,
zou het kind ook in acceptEdits-mode niet kunnen afsluiten).
Tests: KIND_EXPECTED in __tests__/job-config.test.ts bijgewerkt.
334 tests in 38 files passed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Voorbereidende wijzigingen voor de queue-loop-refactor (zie
docs/plans/queue-loop-extraction.md in Scrum4Me-repo). Maakt scrum4me-mcp
geschikt als gedeelde library voor de nieuwe scrum4me-docker runner.
- T-13: export getFullJobContext uit src/tools/wait-for-job.ts
- T-14: mapBudgetToEffort(budget) → --effort {medium,high,xhigh,max} mapping
voor Claude CLI 2.1.x (heeft geen --thinking-budget). Comment in header
documenteert dat max_turns audit-only is en de CLI-flag-mapping.
- T-15: KIND_DEFAULTS.allowed_tools van null → expliciete lijsten zonder
wait_for_job/check_queue_empty/get_idea_context. Vangrail tegen recursieve
claims. SPRINT_IMPLEMENTATION mist bewust job_heartbeat (runner doet
lease-renewal).
- T-16: src/lib/idea-prompts.ts → src/lib/kind-prompts.ts. Nieuwe export
getKindPromptText voor alle 5 kinds. Back-compat re-export
getIdeaPromptText behouden zodat wait-for-job.ts:508 ongewijzigd werkt.
- T-17: nieuwe prompts src/prompts/task/implementation.md,
sprint/implementation.md, plan-chat/chat.md. Idea-prompts (M12) ongewijzigd.
Tests: 334 passed (38 files). 27 nieuwe asserts: mapBudgetToEffort
grenswaarden (14), KIND_DEFAULTS.allowed_tools structurele checks (6),
kind-prompts loading + verboden-tool-mentions (13).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Workers kunnen voortaan het werkelijk verbruikte thinking-budget
meegeven via `actual_thinking_tokens`. Identiek aan de bestaande
input/output/cache_*-velden: optioneel + conditional update.
Backwards-compatible: oude workers zonder deze veld blijven werken.
57 update-job-status tests groen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Roept resolveJobConfig aan na het claimen van een job en voegt het
resultaat toe als `config: JobConfig` aan de response payload. Werkt
voor alle 3 return-paden (IDEA_*, SPRINT_IMPLEMENTATION, default
TASK_IMPLEMENTATION).
Schema-velden lokaal toegevoegd ter ondersteuning van het Prisma-include
(preferred_*, requires_opus, requested_*, actual_thinking_tokens). De
sync-schema.sh-flow refresht ze later vanuit het scrum4me-submodule
zodra PBI-67/ST-1297 in main is.
Pure additief — oude clients negeren `config` en blijven werken op
Claude Code defaults uit ~/.claude/settings.json.
301 tests slagen onveranderd.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When verify_task_against_plan returns EMPTY because the requested changes
already live in origin/main (parallel work, earlier PR, race between
siblings), the worker had no clean exit: update_job_status only accepted
running|done|failed. 'failed' triggered the PBI fail-cascade which then
overwrote the error column with 'cancelled_by_self' and cancelled all
sibling tasks of the PBI — see Scrum4Me job cmovkur8 / T-695 for the
reference incident.
This change introduces a fourth status and tightens the cascade:
ST-1273 — 'skipped' exit in update_job_status (T-706 + T-707)
- src/tools/update-job-status.ts: status enum + DB_STATUS_MAP +
resolveNextAction now include 'skipped'. cleanupWorktreeForTerminalStatus
signature widened to ('done'|'failed'|'skipped'); SKIPPED uses keepBranch
semantics identical to FAILED (no push, no branch keep). New input guard:
'skipped' is only valid for TASK_IMPLEMENTATION jobs and requires a
non-empty error (≥10 chars) explaining the reason — it bypasses the
verify-gate, the auto-PR, the SprintRun finalize/fail paths and the
PBI fail-cascade. Locks are still released on terminal exit.
- Tool description spells out when to pick 'skipped' so MCP clients see it.
- New __tests__/update-job-status-skipped.test.ts: resolveNextAction with
'skipped' (wait_for_job_again / queue_empty), and cleanupWorktreeForTerminalStatus
with status='skipped' (keepBranch=false even with a branch reported,
defers cleanup with active siblings).
ST-1274 — cascade ignores SKIPPED + appends trace (T-708 + T-709)
- src/cancel/pbi-cascade.ts: runCascade reads job.status, returns EMPTY
when status === 'SKIPPED' (no sibling cancel). Trace persistence now
reads the current error first and writes `${original}\n---\n${trace}`
(truncated at 1900 chars), so the original failure cause is preserved
for forensics instead of being overwritten.
- New cases in __tests__/cancel-pbi-cascade.test.ts: SKIPPED entry-guard
(no findMany / updateMany / update), original error preserved with
trace appended after '---', trace-only fallback when no original
error, 1900-char truncation keeps the head of the original.
All 282 scrum4me-mcp tests pass; tsc build clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- checkSprintVerifyGate: aggregate verify-gate via SprintTaskExecution.
Per row: DONE → checkVerifyGate met snapshot-velden, SKIPPED →
alleen toegestaan bij verify_required=ANY, FAILED/PENDING/RUNNING →
blocker. Toolerror met opsomming bij faal.
- finalizeSprintRunOnDone: idempotent SprintRun → DONE wanneer alle
stories DONE/FAILED zijn.
- maybeCreateSprintBatchPr: één draft-PR per sprint met sprint_goal
als title. Hergebruikt bestaande PR via SprintRunChain bij resume.
- DONE-pad: na update markPullRequestReady wanneer SprintRun DONE.
- FAILED-pad: detect QUOTA_PAUSE: prefix → SprintRun PAUSED met
pause_context (resume-instructions + last-completed-task); anders
→ FAILED met failure_reason + failed_task_id (uit error-string).
- cancelPbiOnFailure overslaan voor SPRINT-jobs (geen task_id).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vier nieuwe tools + propagateStatusUpwards uitbreiding:
T1 — verify_sprint_task (src/tools/verify-sprint-task.ts):
Execution-aware verify met frozen plan_snapshot. Input: execution_id +
worktree_path + optionele summary (voor PARTIAL/DIVERGENT-rationale).
Vult base_sha dynamisch voor task[1..N] op basis van vorige DONE-execution's
head_sha. Schrijft verify_result + verify_summary op execution-row.
Returns { result, reasoning, base_sha, allowed_for_done, reason? } —
allowed_for_done via standaard checkVerifyGate met snapshot-velden.
T2 — update_task_execution (src/tools/update-task-execution.ts):
Lifecycle-tool voor SprintTaskExecution: PENDING/RUNNING/DONE/FAILED/SKIPPED
+ base_sha/head_sha/skip_reason. Idempotent. Token-check via
execution.sprint_job.claimed_by_token_id. started_at/finished_at automatisch.
T3 — job_heartbeat (src/tools/job-heartbeat.ts):
Verlengt ClaudeJob.lease_until met 5 min via atomic conditional UPDATE
(token-check + status-check in WHERE). Voor SPRINT-jobs: response bevat
sprint_run_status + sprint_run_pause_reason zodat worker op UI-side cancel
of MERGE_CONFLICT-pause kan breken zonder extra query.
T4 — update_task_status sprint_run_id-arg + token-coupling
(src/tools/update-task-status.ts):
Optionele sprint_run_id-arg voor expliciete cascade. Validaties: SprintRun
bestaat + actief, task in deze sprint, current token heeft een actieve
ClaudeJob in deze run geclaimd (403 anders). Response uitgebreid met
sprint_run_status_change.
T5 — propagateStatusUpwards sprintRunId-param
(src/lib/tasks-status-update.ts):
Optionele sprintRunId-parameter. Resolve-volgorde: expliciete arg →
ClaudeJob.task_id-lookup → Story → Sprint → SprintRun.findFirst({active}).
De derde fallback dekt SPRINT_IMPLEMENTATION (geen task_id-koppeling) én
handmatige task-statuswijzigingen via UI. cancelExceptJobId voor
sibling-cancel; null voor SPRINT-job betekent geen siblings te cancellen.
src/index.ts: drie nieuwe tools geregistreerd.
Tests: 31 files, 243 passing (geen tests voor nieuwe tools nog — F5).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
F2-T2 — getFullJobContext branche voor `kind === 'SPRINT_IMPLEMENTATION'`:
- Fetch sprint_run met deep include (sprint → product + stories → pbi + tasks).
- resolveRepoRoot via product; rollbackClaim bij faal.
- Branch-resolutie: previous_run_id + branch → reuse (resume-pad), anders
verse `feat/sprint-<run_id-suffix>`. createWorktreeForJob met juiste
reuseBranch-flag.
- Capture base_sha via `git rev-parse HEAD` na worktree-add.
- Frozen scope-snapshot: SprintTaskExecution.createMany met plan_snapshot,
verify_required_snapshot, verify_only_snapshot per task in scope. Order
is PBI→Story→Task. base_sha alleen op task[0] (rest fillt verify-tool).
- Update job.branch + job.base_sha + sprint_run.branch in één transactie.
- Lookup execution_ids voor response shape.
F2-T3 — resetStaleClaimedJobs lease-driven:
- WHERE-clause uitgebreid naar `status IN ('CLAIMED','RUNNING')` met OR-clause
`lease_until < NOW() OR (lease_until IS NULL AND claimed_at < NOW() - 30min)`.
Legacy jobs zonder lease blijven via claimed_at-pad werken; nieuwe jobs
via lease_until.
- RETURNING uitgebreid met kind, sprint_run_id, branch.
- Bij stale FAILED SPRINT_IMPLEMENTATION: push branch (geen mark-ready,
geen PR-promotie) zodat werk niet verloren gaat. Vul SprintRun.failure_reason
met laatst-RUNNING execution voor diagnose.
Imports: getWorktreeRoot uit worktree-paths.js, pushBranchForJob uit push.js.
Tests: 31 files, 243 passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three findings from PBI-47 review:
P1 — primary_worktree_path scheiden van lock-volgorde
setupProductWorktrees acquired locks in alphabetical order (deadlock prevention)
but also returned worktrees in that order, so worktrees[0] could point at a
secondary product when its id sorted before the primary's. Lock-acquire stays
sorted; output now preserves caller's input order so worktrees[0] is always
the primary.
P1 — Idea-claim rollback bij worktree setup failure
setupProductWorktrees runs after tryClaimJob has already flipped the job to
CLAIMED. A failure in lock-acquire/git-fetch/reset/sync left the job hanging
until the 30-min stale-reset and the lock-map populated. Wrapped in try/catch
with releaseLocksOnTerminal + rollbackClaim mirror of the task-pad behaviour.
P2 — SPRINT mark-ready fallback when last task didn't push
The mark-ready path used updated.pr_url, which is null when the closing task
was verify-only or had no diff. Now falls back to a Prisma findFirst on the
SprintRun's earliest job with pr_url IS NOT NULL.
Tests: 31 files, 243 passing (incl. new input-order regression for setupProductWorktrees).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Schema sync vanaf upstream Scrum4Me (v77617e8): FAILED toegevoegd aan
Task/Story/Pbi/SprintStatus, nieuw SprintRunStatus + PrStrategy enums,
SprintRun model, ClaudeJob.sprint_run_id, Product.pr_strategy.
T-18 — propagateStatusUpwards in src/lib/tasks-status-update.ts.
Real-time cascade Task → Story → PBI → Sprint → SprintRun bij elke
task-statuswijziging. Bij FAILED cancelt sibling-jobs in dezelfde
SprintRun. PBI-status BLOCKED blijft handmatig. Houd deze helper bit-
voor-bit synchroon met Scrum4Me/lib/tasks-status-update.ts.
updateTaskStatusWithStoryPromotion blijft als BC-wrapper.
T-19 — wait-for-job.ts claim-filter. Task-jobs worden alleen geclaimd
als hun SprintRun status QUEUED of RUNNING heeft. Idea-jobs blijven
ongefilterd. Bij eerste claim van een QUEUED SprintRun → RUNNING
binnen dezelfde tx (race-safe).
T-20 — update-job-status.ts roept propagateStatusUpwards aan na elke
task DONE/FAILED. Bestaande cancelPbiOnFailure-aanroep blijft voor
PR-cleanup; sibling-cancellation overlap is harmless (idempotent).
T-21 — classify.ts (verifier) leest nu ook "--- a/<path>" zodat
delete-only commits niet meer als EMPTY worden geclassificeerd.
Bug had eerder geleid tot ten onrechte FAILED-status op cmotto5h en
cmotto5i (06-05-2026); zou met cascade-flow een hele sprint laten
falen.
Cleanup: create-todo.ts en open_todos in get-claude-context.ts
verwijderd (Todo-model is op main gedropt). Endpoint geeft nu
open_ideas terug — ideeën die niet PLANNED zijn.
Status-mappers (src/status.ts) uitgebreid met failed.
Tests: 184/184 groen (180 → 184; vier nieuwe delete-only classify-tests
en herwerkte propagate-status tests).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wanneer een TASK_IMPLEMENTATION-job FAILED wordt, cancelt
cancelPbiOnFailure alle queued/claimed/running siblings binnen
dezelfde PBI (over alle stories heen) en draait gepushte commits
ongedaan:
- Open PR → gh pr close --delete-branch (PR-close + remote-branch-
delete in één).
- Gemergde PR → revert-PR via git revert -m 1 <mergeSha> in een
korte worktree, gepusht naar revert/<orig>-<jobid>, gh pr create
zonder auto-merge (mens reviewed).
- Branch zonder PR → best-effort git push origin --delete.
Race-protectie: update_job_status weigert nu een statuswijziging op
een job die al CANCELLED is met een specifieke JOB_CANCELLED-error,
zodat een parallelle worker zijn lokale werk weggooit ipv een DONE
te forceren. Idempotent — een tweede cascade voor dezelfde PBI is
een no-op. Non-blocking — alle fouten worden warnings in de trace
op de oorspronkelijke failed job zijn error-veld; cascade throwt
nooit naar de caller.
Niet in scope: per-product opt-out, sprint-niveau cascade,
idea-job cascade.
11 nieuwe vitest-cases dekken DB-cascade, branch-grouping, open/
merged/no-PR paden, repo-root-mismatch en de never-throws-garantie.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bron-aanpassing in scrum4me/lib/idea-prompts/make-plan.md (PR
madhura68/Scrum4Me#130) wordt hierin gesynced naar de embedded kopie
die de worker daadwerkelijk leest via getIdeaPromptText.
Inhoudelijke wijziging: nieuwe verplichte stap-3 in de werkwijze ("Bij
removal/refactor: doe een dependency-cascade-grep") + complete sectie
met grep-protocol per type wijziging (Prisma-model, component, type,
hernoemen, veld) + eind-taak `npm run typecheck`.
Achtergrond: tijdens ST-1236 (Todo-applicatielaag verwijderen) miste het
plan de cascade naar 4 consumer-bestanden + de v3-landing. Lint en tests
slaagden, next build brak. De upstream prompt-update voorkomt dit voor
toekomstige IDEA_MAKE_PLAN-jobs — deze sync zorgt dat workers het ook
echt zien.
Verified: tsc + vitest (153/153) groen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Verlaagt het schrijfvolume naar claude_workers met factor 2. CLAUDE.md noot
toegevoegd dat de Scrum4Me NavBar-drempel (last_seen_at < now() - 15s)
bij 10s interval krap is — daar kan 25-30s een veiliger marge zijn.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
update_job_status accepts optionele model_id + 4 token-velden conform het
runbook-contract (mcp-integration.md:42). De waarden komen niet van de agent
zelf maar van scripts/persist-job-usage.ts, een PostToolUse-hook die het
lokale Claude Code transcript (~/.claude/projects/.../*.jsonl) leest en de
usage tussen de laatste wait_for_job en update_job_status optelt.
Geen Anthropic API-key nodig — alle data staat al lokaal op disk omdat
Claude Code per assistant-message het API usage-blok logt
(input_tokens, output_tokens, cache_creation_input_tokens,
cache_read_input_tokens + message.model).
Robustness:
- Subagent (isSidechain: true) lines worden geskipt om double-counting
te voorkomen tegen subagents/-subdirectory transcripts.
- Lines worden gededupliceerd op uuid (branching/resumption).
- model_id wordt genormaliseerd: claude-opus-4-7[1m] -> claude-opus-4-7-1m
zodat de [1m]-variant op een aparte model_prices-rij kan matchen.
- Hook is non-blocking: elke fout logt een warning en exit 0.
Hook-config in .claude/settings.json met SCRUM4ME_MCP_DIR-fallback zodat
de agent vanuit een product-worktree (andere cwd) ook werkt mits de user
de hook in ~/.claude/settings.json kopieert.
16 nieuwe vitest-cases voor parseTranscript, computeUsageFromTranscript,
normalizeModelId en persistJobUsage.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
T-519 — pre-flight quota-gate voor de worker-loop.
Twee nieuwe MCP-tools:
- get_worker_settings (read): retourneert User.min_quota_pct. Worker
roept dit elke iteratie aan vóór de quota-probe.
- worker_heartbeat (write): worker rapporteert last_quota_pct +
last_quota_check_at na een probe. Update ClaudeWorker en emit
pg_notify 'worker_heartbeat' op scrum4me_changes-channel zodat
NavBar stand-by-badge real-time updatet. requireWriteAccess
(demo-blok).
Schema-resync: vendor/scrum4me bijgewerkt naar 555ed8f waarmee de
M13-velden (User.min_quota_pct, ClaudeWorker.last_quota_pct +
last_quota_check_at) beschikbaar zijn voor Prisma client.
Bestaande achtergrond-heartbeat (presence/heartbeat.ts, 5s tick op
last_seen_at) blijft ongewijzigd. Worker_heartbeat is een aparte
expliciete call met quota-info.
Versie naar 0.7.0 (minor — twee nieuwe tools).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Schema heeft Task.repo_url al (override van product.repo_url voor
worktree/branch/push), maar de create_task MCP-tool exposeerde 'm
niet — gevolg: cross-repo tasks (bv. T-519 in scrum4me-mcp onder een
Scrum4Me-PBI) eindigden met repo_url=null en worker draaide ze in
het verkeerde repo.
PBI-34 introduceerde IdeaProduct (idea aan meerdere producten) als
multi-product-pattern. Voor PBI/Story is geen extensie nodig; per-task
override is genoeg om cross-repo werk correct te routeren.
Validatie: zod.string().url() — full https://github.com/owner/repo URL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>