Commit graph

104 commits

Author SHA1 Message Date
Scrum4Me Agent
4c476464ec PBI-55: ask-user-question – triggerPush na claudeQuestion.create
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 21:37:20 +02:00
Scrum4Me Agent
18c34b63de PBI-55: src/lib/push-trigger.ts – fire-and-forget push helper with 5s AbortController timeout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 21:34:41 +02:00
Janpeter Visser
0d6bf8dd0d
Merge pull request #36 from madhura68/feat/skipped-exit-route
PBI-57: 'skipped' no-op exit + cascade preserves original error
2026-05-07 17:37:52 +02:00
Madhura68
458b7a7d45 PBI-57: 'skipped' no-op exit + cascade preserves original error
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>
2026-05-07 17:10:02 +02:00
Janpeter Visser
8ffb680a1a
Merge pull request #35 from madhura68/feat/sprint-batch-mcp
PBI-50: SPRINT_IMPLEMENTATION single-session sprint runner (MCP-side)
2026-05-07 13:01:40 +02:00
Madhura68
98786f763f PBI-50 F5: README — verify_sprint_task, update_task_execution, job_heartbeat
Drie nieuwe tools voor SPRINT_IMPLEMENTATION-flow toegevoegd aan tool-tabel.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 12:56:22 +02:00
Madhura68
b80264c26c PBI-50 F5: tests voor SPRINT_IMPLEMENTATION-tools
- update-job-status-sprint-gate: checkSprintVerifyGate per-row
  blockers, SKIPPED-policy, finalizeSprintRunOnDone idempotentie.
- update-task-execution: token-coupling, lifecycle (RUNNING zet
  started_at, DONE/FAILED/SKIPPED zet finished_at), skip_reason.
- job-heartbeat: token-mismatch error, non-SPRINT vs SPRINT
  response-shape, tolerantie voor pause_context=null.
- verify-sprint-task: PARTIAL+summary gate-pass, PARTIAL zonder
  summary gate-fail, DIVERGENT met ALIGNED gate-fail, base_sha
  auto-fill via vorige DONE execution head_sha + persistence,
  MISSING_BASE_SHA error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 12:53:04 +02:00
Madhura68
876a7ad5d9 PBI-50 F4: SPRINT_IMPLEMENTATION DONE/FAILED-paden + quota-pause
- 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>
2026-05-07 12:48:04 +02:00
Madhura68
25ab68073a PBI-50 F3: nieuwe MCP-tools voor SPRINT_IMPLEMENTATION-flow
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>
2026-05-07 12:40:18 +02:00
Madhura68
35601e8e4b PBI-50 F2-T2/T3: SPRINT_IMPLEMENTATION-pad in getFullJobContext + lease-driven stale-reset
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>
2026-05-07 12:33:55 +02:00
Madhura68
de6bbd4edd PBI-50 F2-T1: claim-filter kind-based + lease_until persisten
Schema-sync vanaf Scrum4Me (PBI-50 F1):
- PrStrategy.SPRINT_BATCH, ClaudeJobKind.SPRINT_IMPLEMENTATION
- enum SprintTaskExecutionStatus, model SprintTaskExecution
- ClaudeJob.lease_until + status_lease_until index
- SprintRun.previous_run_id (self-relation)

tryClaimJob in src/tools/wait-for-job.ts:
- WHERE-clause refactor naar kind-based discriminatie. NULL-checks vervangen
  door expliciete `cj.kind IN (...)`. SPRINT_IMPLEMENTATION en TASK_IMPLEMENTATION
  vereisen beide actieve SprintRun (QUEUED/RUNNING). Idea-kinds blijven
  standalone claimable.
- UPDATE op claim zet `lease_until = NOW() + INTERVAL '5 minutes'`.

Tests: 19 wait-for-job tests groen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 12:27:48 +02:00
Janpeter Visser
7dbc9fe249
Merge pull request #34 from madhura68/feat/sprint-worker
PBI-49: review-fixes (primary_worktree order, idea rollback, sprint mark-ready fallback)
2026-05-07 12:19:45 +02:00
Madhura68
d2f43fe8e6 PBI-49: review-fixes — primary_worktree order, idea-claim rollback, sprint mark-ready fallback
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>
2026-05-07 11:02:23 +02:00
Janpeter Visser
eccc75ca56
Merge pull request #33 from madhura68/feat/sprint-worker
PBI-9 + PBI-47: worktree foundation, product-worktrees, P0 fixes, PAUSED flow
2026-05-06 21:35:47 +02:00
Madhura68
f7f5a487ec PBI-9 + PBI-47: worktree foundation, product-worktrees, P0 fixes, PAUSED flow
Adds two interlocking PBIs:

PBI-9 — Worktree foundation + persistent product-worktrees for idea-jobs
  - src/git/worktree-paths.ts: centralised root + skip-set + lock-path helpers
  - src/git/file-lock.ts: proper-lockfile wrapper, deadlock-safe ordered acquire
  - src/git/product-worktree.ts: detached-HEAD worktree per product, .scratch/
    excluded via git rev-parse --git-path (handles linked .git file)
  - src/git/job-locks.ts: setupProductWorktrees + releaseLocksOnTerminal
  - wait-for-job.ts: idea-branch wires product-worktrees for IDEA_GRILL/MAKE_PLAN
  - update-job-status.ts + pbi-cascade.ts + stale-reset: release on all four
    server-side terminal transitions (DONE/FAILED/CANCELLED/stale)
  - cleanup-my-worktrees: skip _products/ + *.lock
  - README: worktrees section with single-host invariant + advisory-lock path

PBI-47 — Sprint-flow P0 corrections + PAUSED flow with rich pause_context
  - prisma schema: ClaudeJob.{base_sha,head_sha} + SprintRun.pause_context
  - tryClaimJob captures base_sha; prepareDoneUpdate captures head_sha
  - verify-task-against-plan diffs vs base_sha (no more origin/main fallback);
    rejects with MISSING_BASE_SHA when null — fixes per-task verify-scope P0
  - pr.ts: createPullRequest enableAutoMerge default false; new
    enableAutoMergeOnPr with --match-head-commit guard + 5-category typed
    EnableAutoMergeResult — fixes STORY auto-merge timing P0
  - src/flow/{effects,worktree-lease,pr-flow,sprint-run}.ts: pure transition
    modules + idempotent declarative effects executor
  - update-job-status: STORY auto-merge fires only on the last task of the
    story (story.status === DONE), with head_sha as merge guard; MERGE_CONFLICT
    routes to sprint-run flow which produces CREATE_CLAUDE_QUESTION +
    SET_SPRINT_RUN_STATUS effects with rich pause_context

Tests: 31 test files, 242 passing. Pure-transition tests cover STORY 3-tasks
auto-merge timing, SPRINT draft→ready, MERGE_CONFLICT pause/resume, file-lock
deadlock prevention, worktree-lease lifecycle, delete-only verify (ALIGNED),
per-job verify scope (base_sha isolation), 5-category auto-merge errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 21:09:48 +02:00
Janpeter Visser
4598efde58
Merge pull request #32 from madhura68/feat/sprint-worker
PBI-8 (worker): sprint-aware branch + SPRINT-mode draft-PR
2026-05-06 17:18:07 +02:00
Madhura68
454d96ee04 PBI-8 (vervolg): Sprint-aware branch + SPRINT-mode draft-PR
T-22 — sprint-aware branch-resolutie (resolveBranchForJob):
  - SPRINT-mode  → feat/sprint-<sprint_run_id-suffix> (één branch voor hele run)
  - STORY-mode   → feat/story-<story_id-suffix>      (één per story)
  - Legacy (zonder sprint_run_id): bestaand gedrag
  Sibling-detection herbruikt branch wanneer een eerdere job in dezelfde
  scope al de branch heeft.

T-24 — SPRINT-mode draft-PR + ready-bij-DONE:
  - createPullRequest accepteert nu draft + enableAutoMerge flags
  - Nieuwe markPullRequestReady-helper voor draft → ready transitie
  - maybeCreateAutoPr in SPRINT-mode: opent één draft-PR per SprintRun met
    sprint_goal als titel; geen auto-merge; sibling-tasks hergebruiken de
    PR
  - update-job-status detecteert sprint-DONE via PropagationResult en zet
    de draft-PR via markPullRequestReady ready-for-review (mens reviewt en
    mergt zelf)

T-23 — STORY-mode dekking: bestaande createPullRequest + auto-merge gedrag
ongewijzigd. Tests uitgebreid met sprint-aware mocks; 6 nieuwe
branch-resolution tests + 2 sprint-mode auto-pr tests + 4 markPullRequest
Ready/draft-PR tests.

Tests: 195/195 groen (180 → 195; 15 nieuwe scenario's voor sprint-aware
branch + SPRINT-mode draft-PR + markPullRequestReady).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 17:15:04 +02:00
Janpeter Visser
7b135e12dd
Merge pull request #31 from madhura68/feat/sprint-flow
PBI-8: Sprint-flow MCP-orkestratie + verifier delete-only fix
2026-05-06 17:02:27 +02:00
Madhura68
5c5ae20f10 PBI-8: Sprint-flow MCP-orkestratie + verifier-fix
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>
2026-05-06 16:59:58 +02:00
Janpeter Visser
c63e2c6730
Merge pull request #29 from madhura68/feat/pbi-fail-cascade
feat: PBI fail-cascade — cancel siblings + undo commits
2026-05-06 10:10:26 +02:00
Janpeter Visser
b2318aa910
Merge pull request #28 from madhura68/chore/sync-idea-prompts
chore: sync make-plan.md prompt from scrum4me upstream
2026-05-06 10:10:09 +02:00
Madhura68
70e58f8b28 feat: PBI fail-cascade — cancel siblings + undo commits
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>
2026-05-06 10:08:31 +02:00
Madhura68
3bee6e5080 chore: sync make-plan.md prompt from scrum4me upstream
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>
2026-05-06 10:05:03 +02:00
Janpeter Visser
066a9acc48
Merge pull request #27 from madhura68/chore/heartbeat-10s
chore(presence): heartbeat interval 5s -> 10s
2026-05-06 08:07:28 +02:00
Madhura68
7d5fcde10c chore(presence): heartbeat interval 5s -> 10s
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>
2026-05-06 08:03:14 +02:00
Janpeter Visser
800f4135d1
Merge pull request #26 from madhura68/claude/hardcore-shtern-3d6db0
feat: per-job token-usage capture via PostToolUse hook
2026-05-06 07:55:42 +02:00
Madhura68
25bd3dd62a feat: per-job token-usage capture via PostToolUse hook
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>
2026-05-06 07:53:36 +02:00
Janpeter Visser
f5887da1f5
Merge pull request #25 from madhura68/feat/worker-quota-tools
feat(M13): get_worker_settings + worker_heartbeat tools (v0.7.0)
2026-05-06 04:24:47 +02:00
Madhura68
d50075d960 feat(M13): get_worker_settings + worker_heartbeat tools (v0.7.0)
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>
2026-05-06 04:23:31 +02:00
Janpeter Visser
edb1da7081
Merge pull request #24 from madhura68/feat/create-task-repo-url
feat(create_task): optionele repo_url voor cross-repo tasks
2026-05-06 04:21:39 +02:00
Madhura68
f600237c8c feat(create_task): optionele repo_url voor cross-repo tasks
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>
2026-05-06 04:16:31 +02:00
Janpeter Visser
b48f2a5c74
Merge pull request #23 from madhura68/feat/auto-merge-after-pr
feat: enable auto-merge (squash) after gh pr create
2026-05-06 00:44:04 +02:00
Madhura68
c1abcb8f82 feat(pr): enable auto-merge (squash) na pr create
Best-effort gh pr merge --auto --squash direct na succesvolle
gh pr create. PR mergt zodra alle vereiste CI-checks groen zijn,
zonder handmatige actie van de gebruiker.

Faal-tolerant: als auto-merge niet werkt (repo heeft "Allow
auto-merge" uit, of token-scope ontbreekt), wordt alleen een
warning gelogd. createPullRequest blijft de PR-URL teruggeven —
auto-merge kan handmatig aangezet worden.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 00:34:30 +02:00
Janpeter Visser
35460b09de
Merge pull request #22 from madhura68/fix/idea-job-done-skip-verify
fix: idea-jobs done blocked by verify-gate (v0.6.2)
2026-05-05 13:27:20 +02:00
Madhura68
536a27592c fix: idea-jobs cannot mark done — skip verify-gate (v0.6.2)
T-505 in v0.6.0 wired the idea-failure side-effects but missed the
'skip verify-gate for IDEA_*-kinds on done' branch from the M12 plan.

Reproduced live on IDEA-002: agent answered 5 questions, called
update_idea_grill_md (status → GRILLED, grill_md persisted), but
update_job_status('done') was rejected by the verify-gate because
idea-jobs have no task → no plan_snapshot → verify_task_against_plan
cannot run. Job got marked FAILED + idea reverted to GRILL_FAILED
even though the grill itself succeeded.

Fix: in update_job_status, when status='done' AND kind in
[IDEA_GRILL, IDEA_MAKE_PLAN]: skip checkVerifyGate AND
prepareDoneUpdate (no git push, no branch). The idea-status was
already moved to GRILLED/PLAN_READY by update_idea_*_md; the job
just needs to flip to DONE.

Tests: 153/153 still green.

Bump 0.6.1 → 0.6.2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 13:25:32 +02:00
Janpeter Visser
1ac87deb0e
Merge pull request #21 from madhura68/fix/idea-job-claim-leftjoin
fix: idea-jobs never claimed — LEFT JOIN tasks (v0.6.1)
2026-05-05 12:46:22 +02:00
Madhura68
dc43351831 fix: idea-jobs never claimed — JOIN tasks → LEFT JOIN (v0.6.1)
T-505 added the kind-discriminator to wait_for_job's response payload but
missed the claim-SQL: tryClaimJob does INNER JOIN tasks ON cj.task_id,
which matches NO rows for IDEA_*-jobs (task_id IS NULL by design — M12
schema). Result: idea-jobs sit forever in QUEUED, never picked up.

Reproduced live: IDEA-002 (cmoshh2ne...) had a IDEA_GRILL job queued at
10:26 that 2 active workers ignored for 14+ minutes.

Fix: LEFT JOIN tasks. plan_snapshot stays empty for idea-jobs (no
verify-flow needed for grill/make-plan).

Bump to 0.6.1 since 0.6.0 production deploy has the broken claim-SQL.

Tests: 153/153 still green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 12:45:18 +02:00
Janpeter Visser
63e095f756
Merge pull request #20 from madhura68/feat/m12-idea-jobs
M12 — Idea-job support (v0.6.0)
2026-05-05 12:02:31 +02:00
Madhura68
fa6e393465 vendor: bump scrum4me to main (post-M12) + re-sync schema
Scrum4Me PR #91 (feat/m12-ideas) merged at 09:58 UTC. Vendor pointer now
tracks origin/main (commit 2893573, includes the canonical M12 schema and
all M12 server/UI/REST/realtime work).

Re-synced prisma/schema.prisma from vendor as the authoritative source
(was previously synced from a local Scrum4Me feature-branch worktree).
Diff vs vendor: only the erd-generator block (vendored has it, mcp does
not — same as before M12).

Tests: 153/153 green; tsc + build clean. No tool-code changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 12:01:13 +02:00
Madhura68
fdf3dc4471 feat: M12 idea-job support — version 0.6.0
Adds the 4 new MCP-tools for the Scrum4Me M12 Idea-entity flow + extends
3 existing tools to handle the new ClaudeJobKind discriminator.

New tools:
- get_idea_context: full idea + product + open questions + recent logs
- update_idea_grill_md: save grill-result + status → GRILLED + IdeaLog
- update_idea_plan_md: server-side yaml parser validates frontmatter;
  ok → PLAN_READY, fail → PLAN_FAILED + line-info errors
- log_idea_decision: DECISION/NOTE entries on the timeline

Extended tools:
- ask_user_question: xor schema (story_id | idea_id); idea-questions are
  user-private with productId derived from idea.product_id
- wait_for_job: returns \`kind\` discriminator; IDEA_* payloads include
  idea + prompt_text (from src/prompts/idea/) and skip worktree creation
- update_job_status: failed on IDEA_* auto-transitions idea-status to
  GRILL_FAILED / PLAN_FAILED + IdeaLog{JOB_EVENT}; auto-PR + worktree-
  cleanup skipped for idea-jobs

Other changes:
- Health version now read dynamically from package.json (was hardcoded
  '0.1.0' which caused deploy-sync confusion)
- Schema synced to Scrum4Me M12 (Idea + IdeaLog + enums + ClaudeJob/
  Question nullable-FKs + check-constraints + pg_notify-trigger update)
- New @scrum4me-mcp/lib/idea-plan-parser duplicates Scrum4Me's parser
  (drift detected by vendor schema-watchdog)
- Embedded grill+make-plan prompts copied to src/prompts/idea/
- New userOwnsIdea access helper

Tests: 153/153 green; tsc + build clean.

Migration: requires Scrum4Me M12 migration (20260504172747_add_ideas_and_grill_jobs)
applied on the target DB. See vendor/scrum4me/docs/runbooks/mcp-integration.md
for the updated batch-loop with kind-switch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 22:12:36 +02:00
Madhura68
79eb13a210 MCP version bump 2026-05-04 16:45:48 +02:00
Madhura68
49defa9686 feat: auto-generate codes for PBI/Story/Task on create
Code field became required in schema (feat/entity-codes-required).
All three create tools now generate PBI-N / ST-001 / T-N via the same
SELECT-MAX + retry pattern used in the Scrum4Me app. Also bumps vendor
submodule to v1.0.0 and regenerates prisma/schema.prisma.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 16:14:36 +02:00
Janpeter Visser
85111f6dc7
Merge pull request #19 from madhura68/feat/story-o3ti9khw
feat(mcp): check_queue_empty tool — synchrone non-blocking queue-poll
2026-05-03 19:41:46 +02:00
Scrum4Me Agent
7db9881d85 docs: add lege-queue example to check_queue_empty README section 2026-05-03 17:58:52 +02:00
Scrum4Me Agent
d4522e8e53 feat: add check_queue_empty tool (v0.3.0)
Synchronous, non-blocking count of active ClaudeJobs per product or across
all accessible products. Registers check_queue_empty MCP tool with optional
product_id scope, productAccessFilter AuthZ, tests, and README docs.
2026-05-03 17:57:17 +02:00
Janpeter Visser
3ce2c044c4
feat(mcp): set_pbi_pr + mark_pbi_pr_merged tools voor PBI-PR-gating (#18)
* feat(ST-mhj9f2la): add set_pbi_pr MCP tool

- Add pr_url and pr_merged_at fields to Pbi model in schema
- Implement set_pbi_pr tool: writes pr_url, clears pr_merged_at (idempotent)
- AuthZ via requireWriteAccess + userCanAccessProduct through pbi.product_id
- 10 tests: happy path, not-found, no-access, demo-denied, schema validation
- Update README tools table and bump version to 0.2.0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ST-mhj9f2la): add mark_pbi_pr_merged MCP tool

- Implement mark_pbi_pr_merged: sets pr_merged_at = now() on a PBI
- Requires pr_url to be set; returns error if not (geen gekoppelde PR)
- Idempotent: re-calling overwrites the timestamp
- AuthZ via requireWriteAccess + userCanAccessProduct through pbi.product_id
- 6 tests: happy path, no-pr_url, idempotent, no-access, not-found, demo-denied
- Update README tools table with mark_pbi_pr_merged entry

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(ST-mhj9f2la): expand README with set_pbi_pr + mark_pbi_pr_merged docs

Add full signature/input/output/error documentation sections for both
new tools, following the verify_task_against_plan pattern.
Version already bumped to 0.2.0 in earlier commit.
Tag + MCP_GIT_REF pin in scrum4me-docker to be done by maintainer after merge.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 16:25:53 +02:00
2c85f4d239
feat(routing): cross-repo task routing + orphan-branch cleanup (#17)
Two related fixes for the agent-workflow defects exposed by the
2-May-2026 batch:

1. **Cross-repo task routing** (`task.repo_url` override).
   `resolveRepoRoot` now consults `task.repo_url` first; matches against
   per-repo env-var (`SCRUM4ME_REPO_ROOT_REPO_<name>`),
   `~/.scrum4me-agent-config.json` `repoRoots[<name>]`, and finally
   `~/Projects/<name>/.git`. Falls back to product-level resolution
   when null. Tasks tracked under one product but targeting another
   repo (e.g. MCP-server tasks under the main product's PBI) now work.
   `getFullJobContext` exposes `task.repo_url` to the agent.
   `attachWorktreeToJob` accepts and forwards it.

2. **Orphan-branch cleanup** in `createWorktreeForJob`.
   Previously a name-collision suffixed with a timestamp, leaving the
   agent on an unpredictable `feat/story-XXX-<ms>`-name. Worse, in the
   2-May batch the agent ended up reusing an orphan branch from an
   earlier story (`feat/story-x35n155c`) and pushed to a remote ref
   that did not exist, causing 'src refspec does not match any'.

   Now: detect orphan, attempt to remove its (stale) worktree if any,
   delete the local branch, and recreate with the predictable name.
   Timestamp-suffix is the last resort.

Vendor submodule bumped to pick up `Task.repo_url` from Scrum4Me #54.

Tests: 129/129 — `suffixes branch name with timestamp` updated to
`removes orphan branch and reuses the predictable name`.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 18:07:57 +02:00
1fe6ccf609
feat(gate): verify_required levels — ALIGNED/ALIGNED_OR_PARTIAL/ANY (#16)
Sluit story 'Verify-gate uitbreiden' in PBI 'Agent verify-flow hardening' af.

The previous gate weighed only EMPTY: any PARTIAL or DIVERGENT verify
slipped through. The Insights batch (2 May 2026) showed why that's
weak — agent-jobs claiming DONE while only delivering helpers, not
the requested UI components, with verify=DIVERGENT/PARTIAL accepted.

New decision matrix:

  null                       → reject (run verify_task_against_plan)
  EMPTY  + !verify_only      → reject
  EMPTY  + verify_only       → allowed
  ALIGNED                    → always allowed
  PARTIAL/DIVERGENT
    required=ALIGNED         → reject (strict task)
    required=ALIGNED_OR_PARTIAL (default) → allowed only if summary
                                            ≥20 chars (acknowledge drift)
    required=ANY             → allowed (refactor escape hatch)

`update_job_status('done')` now reads `task.verify_required` from the DB
(field added in Scrum4Me PR #53) and passes it + `summary` to the gate.
Tool description updated with the new rules.

Vendor submodule synced to pick up the schema enum.

Tests: 129/129 (was 120 + 9 new combinatorial gate tests).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 17:55:06 +02:00
0bcca15235
docs: branch-per-story flow + heartbeat self-heal in CLAUDE.md (#15)
- Worktree-flow section now describes resolveBranchForJob (sibling
  reuse), feat/story-<id> naming, deferred cleanup while siblings are
  active, and the 1-PR-per-story result.
- File table corrects the heartbeat description (PR #14 made it
  self-healing instead of self-terminating).

Closes the docs task in story 'Voorkom doublure-PRs' under PBI 'Veilige
Claude-agent-workflow'.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 20:26:21 +02:00
657f7a80c0
fix(presence): heartbeat self-heals when worker record disappears (#14)
Previously, if the ClaudeWorker record vanished (deleted by
prisma_workers_cleanup, manual cleanup, or a race during shutdown of a
parallel worker), the heartbeat would log a warning and stop itself
permanently. From that moment the NavBar showed 'Geen agent' for the
rest of the MCP-server process lifetime — even though the agent was
still alive and serving tools.

Fix: on result.count === 0, call registerWorker again so the record is
re-created. Heartbeat keeps ticking. Self-healing instead of self-
terminating.

startHeartbeat now also accepts userId (needed for re-registration);
caller in index.ts updated.

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