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>
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.
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.
Runs git push -u origin <branch> in the worktree. Detects no-changes
(HEAD = origin/main) before pushing. Classifies push failures into
no-credentials, conflict, or unknown via stderr pattern matching.
5 unit tests covering all paths.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
Removes worktree dir via `git worktree remove --force` and deletes
the local branch by default; keepBranch=true preserves the branch.
Returns { removed: false } when the worktree path doesn't exist.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Points to the merge commit of PR #23 on Scrum4Me/main so the submodule
tracks origin instead of the local-only pre-merge commit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
23 unit tests covering parseAcceptanceCriteria, extractKeywords,
checkACStatus, computeDriftScore, lineDiff, and 4 end-to-end scenarios
(plan unchanged, edited, AC missed, no baseline). README documents the
tool with example output and heuristic limitations.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
Keeps prisma/schema.prisma in sync with vendor/scrum4me so
prisma:generate produces the updated ClaudeJob client types.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
Points submodule to Scrum4Me a3af2dd which adds ClaudeJob.plan_snapshot
field; regenerated Prisma client includes the new column.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
tasks-status-update.test.ts: 7 tests covering promote, idempotent
promote, partial-siblings no-promote, demote, no-demote-when-not-done,
task-always-updated, and custom tx client pass-through.
get-claude-context-filter.test.ts: 3 tests verifying the OR tasks
filter is passed, sprint_id/status filter is present, and no story
query fires when there is no active sprint.
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).
Ported transactional story-promotion logic from Scrum4Me app. Promotes
parent story to DONE when all sibling tasks transition to DONE; demotes
back to IN_SPRINT when a task is re-opened on a DONE story. Accepts an
optional tx client to support existing transaction contexts.
- 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>
Tot nu toe konden MCP-tools alleen bestaande stories/tasks bewerken
(update_task_status, log_*). Met deze drie tools kan Claude Code een
volledige backlog vanaf nul opbouwen — handig voor nieuwe projecten waar
het Scrum4Me-product nog leeg is.
- create_pbi: { product_id, title, description?, priority, sort_order? }
Auto sort_order = last+1 binnen prio-groep. Code-veld blijft null
(Scrum4Me-app genereert auto-codes via UI/seed).
- create_story: { pbi_id, title, description?, acceptance_criteria?, priority,
sort_order? } — product_id afgeleid uit PBI (denormalized FK conform CLAUDE.md
convention; nooit op client-input vertrouwen). Status='OPEN' default →
landt in product-backlog, niet auto in een sprint.
- create_task: { story_id, title, description?, implementation_plan?, priority,
sort_order? } — sprint_id geërfd van story. Status='TO_DO' default.
Alle drie achter `requireWriteAccess` (PERMISSION_DENIED voor demo) +
`userCanAccessProduct` op de relevante parent-product. Mirror van het
create-todo-patroon.
scripts/smoke-test.ts: tool-count check 13 → 16. README.md: tool-tabel
uitgebreid.
Quality gates: typecheck clean, build success, smoke-test toont 16 tools.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Was: 79367dd (M11 ST-1101 op feature-branch — niet meer reachable na squash-merge)
Nu: 9587ff4 (M11 squash-merge op origin/main — stabiele referentie)
prisma/schema.prisma is byte-identical (geen schema-drift; alleen submodule-
pointer gewijzigd zodat de pin verwijst naar een commit die op origin reachable
blijft).
npm run sync-schema + npm run typecheck beide clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vier nieuwe tools voor het Claude vraag-antwoord-kanaal:
- ask_user_question (write): post een gestructureerde vraag aan de actieve
Scrum4Me-gebruiker over een story; default async (returnt direct met
question_id + status='open'); optionele wait_seconds (max 600) polt elke 2s
tot het antwoord er is of timeout — daarna status='pending' zodat Claude met
get_question_answer later kan ophalen
- get_question_answer (read): huidige status + antwoord van een eerder
gestelde vraag
- list_open_questions (read): eigen vragen met status open/answered, max 50,
meest recente eerst
- cancel_question (write, asker-only): atomic UPDATE WHERE asked_by + status=
'open' zodat alleen eigen open vragen geannuleerd worden
Allemaal achter access-check via userCanAccessStory/Product en demo-blok via
requireWriteAccess (volgt patroon van create-todo en bestaande log-tools).
Submodule vendor/scrum4me bumpt naar Scrum4Me commit 79367dd (M11 ST-1101) —
bevat het ClaudeQuestion-model en notify_question_change-trigger waar deze
tools tegen werken.
scripts/smoke-test.ts: 13 tools verwacht (was 9); list_open_questions
toegevoegd als read-tool-coverage. Build + tools/list groen — verdere e2e via
MCP Inspector na PR-merge omdat de seed een nieuwe API-token heeft
gegenereerd en .env een nieuwe waarde nodig heeft.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps vendor/scrum4me submodule from 43a4294 (ST-509/511/512/513) naar 7461643
en daarmee picks de schema-wijzigingen op uit:
- M8 (ST-801): notify_solo_change-trigger op tasks/stories
- M9 (ST-901): User.active_product_id + UserActiveProduct relation
- M10 (ST-1001): LoginPairing model + login_pairings_notify-trigger op
scrum4me_pairing channel; User.login_pairings back-relation
Verificatie: npm run sync-schema + npm run prisma:generate + npm run typecheck
allemaal groen. Sluit de wekelijkse drift-check (trig_015FFUnxjz9WMuhhWNGBQKFD)
voor deze drie milestones.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #2 merged, so the StoryLog.metadata JSONB column is live. Sync
the vendored schema and wire `metadata` through to prisma.create in
log_implementation, log_test_result and log_commit. Cast via
Prisma.InputJsonValue because Zod parses the input as a generic
record while Prisma's JSON input type is invariant.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The MCP SDK rejects tools/call results where structuredContent is not a
record — array returns from list_products triggered an MCP error code
-32602. toolJson now wraps arrays/primitives as { result: <value> }.
scripts/smoke-test.ts spawns the built server over stdio, calls each
read-side tool against the live DB and asserts shape — surfaces this
bug class before regressions ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Documents the 9 tools, 1 prompt, environment variables, schema-sync
workflow, mcp_servers.json snippet and known risks.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
End-to-end workflow prompt for Claude Code: fetch context, log a plan,
walk the tasks (in_progress → done), run tests, log result, commit.
Takes product_id as the only argument.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a todo for the authenticated user with optional description (max
2000) and optional product scope. Verifies product access if a
product_id is given. Demo accounts get PERMISSION_DENIED.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three write tools that append StoryLog entries:
- log_implementation: type=IMPLEMENTATION_PLAN
- log_test_result: type=TEST_RESULT, status PASSED|FAILED
- log_commit: type=COMMIT with hash and message
All accept optional metadata in input but skip writing it for now —
the StoryLog.metadata JSONB column lands with Scrum4Me PR #2.
After that PR merges, run sync-schema and add `metadata` to each
prisma.create's data field.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- src/access.ts: shared product/story/task access checks via product
ownership or membership
- update_task_status accepts lowercase API values, converts to DB
enum, rejects unknown values
- update_task_plan replaces implementation_plan on a task
- Both call requireWriteAccess() so demo accounts get
PERMISSION_DENIED before any DB write
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- health: pings DB via SELECT 1 and returns status/version/time
- list_products: active products owned or shared with the auth user
- get_claude_context: bundled product + active sprint + next story
(with tasks, status mapped to lowercase) + 50 open todos
prisma.ts switches to a lazy proxy so the server bootstrap doesn't
crash before tools fire when DATABASE_URL is unset.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- src/status.ts: bidirectional Task/Story status mappers — DB stays
UPPER_SNAKE, MCP tools expose lowercase (matches REST API contract)
- src/errors.ts: formatZodError, toolError, toolJson and the
withToolErrors() wrapper so each tool turns thrown exceptions
(PermissionDenied, ZodError, generic) into structured MCP errors
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>