Commit graph

19 commits

Author SHA1 Message Date
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
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
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
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
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
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
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
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
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
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
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
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