Commit graph

57 commits

Author SHA1 Message Date
Janpeter Visser
fba2d67796
fix(update_job_status): status-gedreven lifecycle-timestamps (#51)
Een job kon CLAIMED -> done/failed/skipped gaan zonder ooit `running` te
rapporteren, waardoor started_at NULL bleef terwijl finished_at wel gezet
werd. Dat brak de invariant claimed_at <= started_at <= finished_at en
elke duur-analyse.

Nieuwe pure helper resolveJobTimestamps zet de lifecycle-timestamps
set-once op basis van de status: started_at wordt gebackfild bij een
terminale overgang, claimed_at defensief gevuld als die ontbreekt. De
running-tak is nu set-once i.p.v. bij elke call overschrijven.

Co-authored-by: Madhura68 <ID+Madhura68@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 23:21:44 +02:00
Janpeter Visser
51fc65e715
fix(update_idea_plan_reviewed): nooit stilzwijgend goedkeuren (IDEA-066) (#50)
De status-logica sprak z'n eigen tool-beschrijving tegen. De code deed:
  approved  -> PLAN_REVIEWED
  rejected  -> PLAN_REVIEW_FAILED
  else      -> PLAN_REVIEWED   // "Default to approved if not specified"

Een review die 'pending' (needs manual approval) of helemaal geen
approval_status teruggaf, markeerde het idee dus als PLAN_REVIEWED
(goedgekeurd) — precies omgekeerd aan wat de beschrijving belooft.

Fix: alleen een expliciete approval_status='approved' brengt het idee
naar PLAN_REVIEWED; 'rejected', 'pending' én een weggelaten
approval_status gaan allemaal naar PLAN_REVIEW_FAILED (mens beslist).
Nooit stilzwijgend goedkeuren.

Verder:
- Handler geextraheerd naar handleUpdateIdeaPlanReviewed + inputSchema
  geexporteerd, conform het create-sprint/update-sprint-patroon, zodat
  de logica zonder McpServer-wrapper testbaar is.
- Tool-beschrijving + header-comment aangescherpt zodat code en docs
  niet meer divergeren.
- Nieuw test-bestand: 6 tests (approved/rejected/pending/omitted
  status-transitie, not-found, log-persistentie).

Build groen, 379 tests groen.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 19:46:31 +02:00
Janpeter Visser
84c194d4e5
fix(cross-repo): per-repo worktree-branch + PR resolutie (IDEA-062) (#49)
Cross-repo sprints (sprint-product = repo X, maar een taak heeft
task.repo_url naar repo Y) faalden op twee plekken omdat sprint-brede
beslissingen werden toegepast op per-repo git-state.

1. createWorktreeForJob (src/git/worktree.ts)
   reuseBranch wordt sprint-breed bepaald in wait-for-job.ts. De eerste
   job die repo Y target krijgt reuseBranch=true terwijl de branch daar
   nooit is aangemaakt -> `git worktree add <path> <branch>` faalt met
   "invalid reference" -> job vast, worker UNHEALTHY. Idem na een
   container-recreate (clone is dan vers).
   Fix: 3-weg fallback in het reuseBranch-pad:
   - lokale branch bestaat   -> hergebruik
   - alleen op origin        -> recreate lokaal vanaf origin/<branch>
   - nergens                 -> fresh vanaf baseRef
   Lost ook het container-recreate-verlies op.

2. maybeCreateAutoPr (src/tools/update-job-status.ts)
   De sprint/story sibling-lookup voor pr_url-hergebruik filterde niet
   op repo. Een repo-Y-job erfde de pr_url van een repo-X-sibling ->
   job.pr_url wees naar de verkeerde repo en er werd nooit een PR voor
   de repo-Y-branch aangemaakt (branch wel gepusht, maar PR-loos).
   Fix: siblings groeperen per repo-bucket ((task.repo_url ?? null));
   alleen een sibling uit dezelfde bucket levert een herbruikbare
   pr_url. Geldt voor SPRINT- en STORY-mode. createPullRequest zelf was
   al repo-correct (gh pr create draait in de worktree).

Tests: 3 nieuwe in worktree.test.ts (reuse-local / recreate-from-origin
/ fresh-fallback), 2 nieuwe in update-job-status-auto-pr.test.ts
(cross-repo story + sprint). update-job-status-mock omgezet naar
findMany. Alle 373 tests groen, build groen.

package-lock.json: version 0.7.0 -> 0.8.0 (was niet mee-gesynced in de
v0.8.0-bump commit 55fa133).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 19:16:15 +02:00
Janpeter Visser
55fa133150
feat: IDEA_REVIEW_PLAN-wiring + create_story sprint_id (v0.8.0) (#48)
* feat(PBI-12 T-51): voeg create_sprint tool toe

Maakt een sprint aan met status=OPEN. Code auto-gegenereerd als
S-{YYYY-MM-DD}-{N} per product per datum als niet meegegeven, met retry
bij race-conflict op @@unique([product_id, code]). Volgt create-pbi.ts
template.

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

* feat(PBI-12 T-52): voeg update_sprint tool toe

Generieke update voor status, sprint_goal, start_date en end_date.
Géén state-machine validatie — last-write-wins. Bij status →
CLOSED/FAILED/ARCHIVED zonder expliciete end_date wordt end_date
automatisch op vandaag gezet. Minimaal één veld vereist (handmatige check
in handler i.p.v. zod-refine want McpServer.inputSchema accepteert geen
ZodEffects).

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

* feat(PBI-12 T-53): registreer sprint-tools + unit-tests

- Imports + register-calls toegevoegd in src/index.ts (groep met andere
  authoring-tools, comment "PBI-12: sprint lifecycle tools")
- Refactor: create-sprint en update-sprint exporteren nu handleX +
  inputSchema apart (pattern van set-pbi-pr.ts) zodat de logica
  zonder McpServer wrapper testbaar is
- 6 unit-tests voor create_sprint (happy path, custom code,
  auto-increment, P2002-retry, access-denied, explicit start_date)
- 11 unit-tests voor update_sprint (no-fields-error, status-only,
  auto-end_date voor CLOSED/FAILED/ARCHIVED, geen auto voor OPEN,
  expliciete end_date respect, multi-field, not-found, access-denied,
  any-status-transition)
- Defensive date-check in generateNextSprintCode tegen
  filter-veranderingen of mock-data anomalieën
- 363 tests groen (was 346 + 17 nieuwe)

DB-smoke-test (MCP-server vs dev-DB) overgeslagen want unit-coverage
dekt het gedrag volledig; mock-vrije integratie volgt automatisch bij
eerstvolgende productie-aanroep van create_sprint via een echte agent.

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

* chore: untrack .claude/worktrees gitlinks + ignore pad

Per ongeluk in adbea3f meegenomen via 'git add -A'; deze embedded worktree-
clones horen niet in de repo. Ook .gitignore aangevuld zodat dit niet
opnieuw gebeurt.

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

* fix(PBI-12): update_sprint zet completed_at op CLOSED — parity met cascade

Codex-review op #47: bij status → CLOSED werd alleen end_date gezet, niet
completed_at. Dat is divergeert van src/lib/tasks-status-update.ts dat
completed_at = new Date() zet bij automatische sluiting via task-status-
cascade. Reporting en UI die op completed_at filteren zagen handmatig
gesloten sprints als 'never completed'.

Fix:
- update_sprint zet nu data.completed_at = new Date() wanneer status === 'CLOSED'
- FAILED/ARCHIVED raken completed_at NIET (parity met bestaand patroon)
- Test-coverage uitgebreid:
  - CLOSED zet end_date EN completed_at
  - FAILED zet end_date, completed_at blijft undefined
  - ARCHIVED zet end_date, completed_at blijft undefined
  - OPEN zet noch end_date noch completed_at
  - Expliciete end_date wordt gerespecteerd, completed_at wordt nog steeds gezet
- Tool description vermeldt nu de completed_at-side-effect

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

* PBI-67 Phase 2: Add update-idea-plan-reviewed MCP tool

- Create src/tools/update-idea-plan-reviewed.ts: saves review-log and transitions idea status
- Register tool in src/index.ts
- Update Prisma schema: add plan_review_log and reviewed_at fields to Idea model
- Add PLAN_REVIEW_RESULT to IdeaLogType enum
- Add REVIEWING_PLAN, PLAN_REVIEW_FAILED, PLAN_REVIEWED to IdeaStatus enum
- Add IDEA_REVIEW_PLAN to ClaudeJobKind enum
- Build successful with all type checks passing

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* feat(PBI-67): bedraad IDEA_REVIEW_PLAN prompt + job-context

- src/prompts/idea/review-plan.md: prompt voor IDEA_REVIEW_PLAN-jobs —
  iteratieve 3-ronden plan-review met convergentie-detectie
- kind-prompts.ts: koppel IDEA_REVIEW_PLAN aan de prompt + getIdeaPromptText
- wait-for-job.ts: getFullJobContext handelt IDEA_REVIEW_PLAN-jobs af

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

* feat(create_story): optionele sprint_id om story aan sprint te koppelen

create_story accepteert nu een optionele sprint_id; bij meegeven wordt de
story aangemaakt met status=IN_SPRINT (sprint moet bij hetzelfde product
horen als de PBI). Handler geextraheerd naar handleCreateStory voor
testbaarheid; nieuwe unit-tests in __tests__/create-story.test.ts.

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

* fix(test): maak create-sprint auto-code test datum-onafhankelijk

De test hardcodede 2026-05-11-datums maar berekende "today" dynamisch,
waardoor hij alleen op die datum slaagde. Mock-codes nu relatief aan today.

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

* chore: bump version 0.7.0 -> 0.8.0

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

* chore: bump vendor/scrum4me submodule naar app-main (7bb252c)

De submodule stond 27 commits achter (3c77342, v1.0.0-147), waardoor
sync-schema.sh prisma/schema.prisma terugzette naar een versie zonder
IDEA_REVIEW_PLAN. Bumpt naar huidige app-main + re-synct het schema;
enige inhoudelijke wijziging is het nieuwe User.settings-veld.

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

---------

Co-authored-by: Madhura68 <ID+Madhura68@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 16:30:17 +02:00
Janpeter Visser
93d881318d
feat(PBI-12): create_sprint + update_sprint MCP-tools (#47)
* feat(PBI-12 T-51): voeg create_sprint tool toe

Maakt een sprint aan met status=OPEN. Code auto-gegenereerd als
S-{YYYY-MM-DD}-{N} per product per datum als niet meegegeven, met retry
bij race-conflict op @@unique([product_id, code]). Volgt create-pbi.ts
template.

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

* feat(PBI-12 T-52): voeg update_sprint tool toe

Generieke update voor status, sprint_goal, start_date en end_date.
Géén state-machine validatie — last-write-wins. Bij status →
CLOSED/FAILED/ARCHIVED zonder expliciete end_date wordt end_date
automatisch op vandaag gezet. Minimaal één veld vereist (handmatige check
in handler i.p.v. zod-refine want McpServer.inputSchema accepteert geen
ZodEffects).

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

* feat(PBI-12 T-53): registreer sprint-tools + unit-tests

- Imports + register-calls toegevoegd in src/index.ts (groep met andere
  authoring-tools, comment "PBI-12: sprint lifecycle tools")
- Refactor: create-sprint en update-sprint exporteren nu handleX +
  inputSchema apart (pattern van set-pbi-pr.ts) zodat de logica
  zonder McpServer wrapper testbaar is
- 6 unit-tests voor create_sprint (happy path, custom code,
  auto-increment, P2002-retry, access-denied, explicit start_date)
- 11 unit-tests voor update_sprint (no-fields-error, status-only,
  auto-end_date voor CLOSED/FAILED/ARCHIVED, geen auto voor OPEN,
  expliciete end_date respect, multi-field, not-found, access-denied,
  any-status-transition)
- Defensive date-check in generateNextSprintCode tegen
  filter-veranderingen of mock-data anomalieën
- 363 tests groen (was 346 + 17 nieuwe)

DB-smoke-test (MCP-server vs dev-DB) overgeslagen want unit-coverage
dekt het gedrag volledig; mock-vrije integratie volgt automatisch bij
eerstvolgende productie-aanroep van create_sprint via een echte agent.

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

* chore: untrack .claude/worktrees gitlinks + ignore pad

Per ongeluk in adbea3f meegenomen via 'git add -A'; deze embedded worktree-
clones horen niet in de repo. Ook .gitignore aangevuld zodat dit niet
opnieuw gebeurt.

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

* fix(PBI-12): update_sprint zet completed_at op CLOSED — parity met cascade

Codex-review op #47: bij status → CLOSED werd alleen end_date gezet, niet
completed_at. Dat is divergeert van src/lib/tasks-status-update.ts dat
completed_at = new Date() zet bij automatische sluiting via task-status-
cascade. Reporting en UI die op completed_at filteren zagen handmatig
gesloten sprints als 'never completed'.

Fix:
- update_sprint zet nu data.completed_at = new Date() wanneer status === 'CLOSED'
- FAILED/ARCHIVED raken completed_at NIET (parity met bestaand patroon)
- Test-coverage uitgebreid:
  - CLOSED zet end_date EN completed_at
  - FAILED zet end_date, completed_at blijft undefined
  - ARCHIVED zet end_date, completed_at blijft undefined
  - OPEN zet noch end_date noch completed_at
  - Expliciete end_date wordt gerespecteerd, completed_at wordt nog steeds gezet
- Tool description vermeldt nu de completed_at-side-effect

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

---------

Co-authored-by: Madhura68 <ID+Madhura68@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 21:37:05 +02:00
Janpeter Visser
da1fe415c4
fix(cleanup): keepBranch + sprint-scope siblings voor SPRINT pr_strategy (#45)
Symptoom: in een sprint met pr_strategy=SPRINT (5 tasks, 3 stories)
werden de eerste twee tasks SKIPPED door Claude (werk al in main na een
externe PR). De derde task crashte op:

  git worktree add /home/agent/.scrum4me-agent-worktrees/<id> feat/sprint-uhrbtc8z
  fatal: invalid reference: feat/sprint-uhrbtc8z

Root cause: cleanupWorktreeForTerminalStatus checkte op active siblings
binnen dezelfde **story** + verwijderde de branch bij keepBranch=false.
Voor SPRINT pr_strategy delen alle stories in de sprint één branch
(feat/sprint-<id>). Eerste task SKIPPED, story ST-1304 had geen actieve
siblings meer (T-807 was ook al SKIPPED), branch werd verwijderd. T-808
in story ST-1305 wilde reuse'n maar branch bestond niet meer.

Fix:
1. Sibling-check verbreden voor SPRINT pr_strategy: kijk naar alle
   actieve jobs in dezelfde sprint_run_id (niet alleen story_id).
2. keepBranch=true voor SKIPPED bij SPRINT pr_strategy: andere stories
   in dezelfde sprint hebben de branch nog nodig.

Tests: 341 passed (38 files). Typecheck OK.

Co-authored-by: Madhura68 <ID+Madhura68@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 16:29:41 +02:00
Madhura68
51533cf48e fix(attachWorktreeToJob): schrijf branch naar claudeJob.branch in DB
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>
2026-05-09 14:05:59 +02:00
Madhura68
0a18f565d2 fix(update_job_status): gebruik DB-branch ipv legacy feat/job-<8> fallback
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>
2026-05-09 13:53:43 +02:00
Madhura68
233e0ef3b6 chore: sync schema + adapt to ACTIVE→OPEN, COMPLETED→CLOSED, EXCLUDED-task
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>
2026-05-09 13:39:13 +02:00
Madhura68
96f5b0dd03 feat(PBI-4/ST-004): publieke API + KIND_DEFAULTS + per-kind prompts
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>
2026-05-08 17:15:21 +02:00
Madhura68
1c0f41687b feat(PBI-67/ST-1300/T-791): persist actual_thinking_tokens in update_job_status
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>
2026-05-08 11:12:31 +02:00
Madhura68
e2963d58fb feat(PBI-67/ST-1299/T-788): wait_for_job retourneert config
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>
2026-05-08 11:11:29 +02:00
Scrum4Me Agent
ab32a72ce0 PBI-55: update-job-status – NOTIFY payload-fix (kind/idea_id) + triggerPush on done/failed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 21:42:19 +02:00
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
f01fab8c38
feat: branch-per-story + worktree-defer + verify EMPTY edge-cases (#12)
Implementeert vier open stories uit PBI 'Veilige Claude-agent-workflow':

**Branch per story (cmon11tbe001zbortx35n155c)**
- `resolveBranchForJob`: zoek sibling-job in dezelfde story; reuse z'n
  branch (1 PR per story i.p.v. per task).
- Branch-naam: `feat/story-<8-char>` voor nieuwe stories.
- `createWorktreeForJob` kent nu `reuseBranch=true`: detecteert stale
  sibling-worktree die de branch nog vasthoudt en verwijdert die eerst.
- `attachWorktreeToJob` neemt `storyId` mee.

**PR-hergebruik (zelfde story)**
- `maybeCreateAutoPr`: als sibling-job in story al een pr_url heeft,
  hergebruik die zonder nieuwe `gh pr create`-call. PR-titel komt nu
  van de story (was task) zodat het als 'story-PR' leest.

**Worktree-cleanup uitgesteld bij actieve siblings**
- `cleanupWorktreeForTerminalStatus`: count active sibling-jobs in
  dezelfde story; defer als > 0 (volgende sub-task gebruikt branch).

**Worktree-cleanup logging (cmon0jc14001ubortjxf2a2ck)**
- Warning bij ontbrekende repoRoot, met productId + jobId in message.
- Warning bij removeWorktreeForJob-failure met keepBranch in message.

**resolveRepoRoot fallback (cmon0jc14001ubortjxf2a2ck)**
- Convention-based fallback: `~/Projects/<repo-name>` afgeleid uit
  `product.repo_url` als noch env-var noch config-bestand iets oplevert.
- `repoNameFromUrl` helper geëxporteerd voor herbruikbaarheid.

**Verify EMPTY-detection edge-case (cmon0kdq6001xbort2kgbcqmr)**
- `classifyDiffAgainstPlan`: na file-paths-check ook content-lines
  checken; als alle +/- regels alleen headers of whitespace zijn,
  return EMPTY met duidelijke reasoning.

Tests: 120/120 groen (3 nieuwe), tsc clean, build clean.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 17:04:54 +02:00
f87b20744b
feat: worker presence layer + batch-loop docs (#7)
* feat: add next_action field to update_job_status response

* docs: add Batch-loop section to README

* feat: presence layer — registerWorker, startHeartbeat, registerShutdownHandlers

* feat: bootstrap worker presence at server startup, remove inline presence from wait-for-job

* docs: document worker presence layer in CLAUDE.md

* docs: refine Batch-loop intro — add 'Hier is de flow:' per implementation plan
2026-05-01 16:39:26 +02:00
5cd792a8fe feat: DONE gate in update_job_status — reject if verify_result null or EMPTY without verify_only
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 13:58:28 +02:00
1015264558 feat(M13): auto-PR via gh CLI after successful push (auto_pr=true)
New src/git/pr.ts helper wraps 'gh pr create'; returns { url } or { error }.
maybeCreateAutoPr() in update-job-status checks product.auto_pr, builds title
from story.code + task.title, writes pr_url to DB. Non-fatal: gh failure logs
a warning and leaves DONE status intact. Also syncs schema: auto_pr on Product,
pr_url on ClaudeJob.
2026-05-01 13:30:38 +02:00
dadcbc48d6 feat(M13): cleanup_my_worktrees tool — scan + remove stale worktrees for terminal-status jobs 2026-05-01 13:22:47 +02:00
095ebc40f8 feat(M13): retry-tracking — stale CLAIMED jobs → QUEUED (retry_count++) or FAILED (≥2 retries)
resetStaleClaimedJobs now uses $queryRaw with RETURNING so it can send pg_notify
claude_job_status events per transitioned job. Jobs under the retry limit are
re-queued with retry_count incremented; jobs at ≥2 retries are marked FAILED.
2026-05-01 13:18:59 +02:00
e63ea7026b feat: verify_task_against_plan calls classifyDiffAgainstPlan + saves verify_result to DB
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 12:59:17 +02:00
8ebf4ff895 feat: integrate push into update_job_status DONE transition
On status=done, calls pushBranchForJob before DB write:
- pushed=true → DONE + pushed_at + branch set + worktree cleanup (keepBranch=true)
- no-changes → DONE without pushed_at + worktree cleanup
- push failure → FAILED with error message + worktree preserved for manual inspection

Also adds pushed_at to vendored prisma schema + regenerates client.
6 unit tests for prepareDoneUpdate covering all push outcomes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 12:00:30 +02:00
ce4afa1928 feat: cleanup worktree in update_job_status on terminal transitions
On DONE/FAILED, resolves repoRoot and calls removeWorktreeForJob (best-effort).
keepBranch=true when status=done and agent reported a branch (push assumed);
false otherwise. Cleanup failures are logged as warnings — DB status is preserved.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 11:52:16 +02:00
6ee55e79b6 feat: integrate createWorktreeForJob into wait_for_job tool
After claiming a job, resolves repoRoot (env SCRUM4ME_REPO_ROOT_<productId>
or ~/.scrum4me-agent-config.json), creates a git worktree, and returns
worktree_path + branch_name in the response. Rolls back claim to QUEUED
on failure. Tool description updated to instruct agent to work in worktree.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 11:50:51 +02:00
f51b7a6178 feat: verify_task_against_plan MCP tool
Read-only tool that compares frozen plan_snapshot against current
task.implementation_plan + story logs + commits. Returns markdown report
with per-AC ✓/✗/? keyword heuristic, drift-score, and plan diff.
Demo users allowed (readOnlyHint: true).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 19:36:37 +02:00
ddc773d20a feat: capture plan_snapshot at job claim in wait_for_job
- resetStaleClaimedJobs: also sets plan_snapshot = NULL on reset
- tryClaimJob: JOINs tasks table to read implementation_plan in the
  same atomic transaction, writes it to claude_jobs.plan_snapshot
- Empty-plan edge case: NULL becomes '' (non-null) in snapshot
- Exports both functions for unit testing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 19:27:52 +02:00
e2c86eb4d9 feat: wire story-promotion into update_task_status + filter done stories from get_claude_context
update_task_status now delegates to updateTaskStatusWithStoryPromotion
and surfaces story_status_change ('promoted' | 'demoted' | null) in the
response so Claude Code can act on story completion without a separate
read call.

get_claude_context adds an OR-filter on tasks so stories where every
task is DONE are skipped — only surfaces stories that still have work to
do (no tasks, or at least one non-DONE task).
2026-04-30 18:22:47 +02:00
d6423ffc24 feat: add wait_for_job and update_job_status tools (M13 agent worker mode)
- wait_for_job: blocks ≤600s, claims QUEUED job atomically via FOR UPDATE
  SKIP LOCKED, resets stale CLAIMED jobs (>30min), registers ClaudeWorker
  presence with heartbeat, emits worker_connected/disconnected via NOTIFY
- update_job_status: agent reports running|done|failed, validates token
  ownership (claimed_by_token_id), emits claude_job_status via NOTIFY
- auth.ts extended with tokenId so tools can set claimed_by_token_id

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 20:05:09 +02:00