Het advies-document dat als input diende voor PBI-50 is nu in docs/plans/ opgeborgen voor traceability. INDEX.md regenerated om hem op te nemen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
11 KiB
| title | status | audience | language | last_updated | ||
|---|---|---|---|---|---|---|
| Advies - SprintRun, PR en worktree lifecycle als state machines | draft |
|
nl | 2026-05-06 |
Advies - SprintRun, PR en worktree lifecycle als state machines
Context
Het combinatieplan voor F3 auto-merge worker-flow en persistente product-worktrees raakt meerdere lifecycles tegelijk:
SprintRun: queue, running, paused, done, failed, cancelled.ClaudeJob: claim, run, verify, push, done, failed, cancelled.- Worktree/lock: acquire, create/reuse, sync, ready, release.
- PR-flow: push branch, create PR, wait for scope completion, checks, merge/ready/pause.
De grootste maintainability-risico's ontstaan waar deze lifecycles elkaar kruisen. Voorbeelden:
- Een PR krijgt auto-merge terwijl latere story-tasks nog niet klaar zijn.
- Een job wordt
DONE, maar de product-worktree lock blijft hangen. - Een
SprintRunwordtPAUSED, maar de UI/server action kan alleen een failed sprint hervatten. - Per-task verificatie gebruikt
origin/main...HEAD, terwijl meerdere tasks dezelfde story- of sprint-branch hergebruiken.
Het advies is om deze lifecycles expliciet te modelleren als state machines, met centrale transition-regels en declaratieve side effects.
Kernadvies
Houd de worker zo dom mogelijk. De worker voert werk uit in de meegegeven context en rapporteert via MCP-tools. De MCP-server is eigenaar van lifecycle-transitions, cleanup, locks, PR-status, pause/resume en terminal states.
Concreet:
- Maak pure transition-modules in
scrum4me-mcp. - Laat
wait-for-job.tsenupdate-job-status.tstransitions aanroepen. - Laat transitions declaratieve effects teruggeven.
- Voer effects idempotent server-side uit.
- Persistente waarheid blijft Postgres.
Niet doen:
- Lock-release afhankelijk maken van een worker-prompt-instructie.
- PR/merge-flow deels in worker hooks en deels in MCP-tools stoppen.
- Auto-merge activeren voordat de volledige scope klaar is.
- Worktree internals rechtstreeks muteren zonder Git-commando's zoals
git rev-parse --git-path.
Voorgestelde Machines
1. WorktreeLeaseMachine
Doel: product-worktrees en locks betrouwbaar beheren.
States:
idle
-> acquiring_lock
-> creating_or_reusing
-> syncing
-> ready
-> releasing
-> released
error:
-> lock_timeout
-> sync_failed
-> stale_released
Belangrijke regels:
- Acquire lock voordat de worktree wordt aangemaakt of gesynct.
- Gebruik een lock-pad dat buiten de worktree bestaat, bijvoorbeeld:
~/.scrum4me-agent-worktrees/_locks/product-{productId}. - Release locks server-side bij:
update_job_status(done)update_job_status(failed)- job cancellation
- stale reset
- process shutdown waar mogelijk
- Gebruik
proper-lockfilealleen als single-host/single-filesystem aanname klopt. - Overweeg PostgreSQL advisory locks of een DB-lease-tabel als meerdere MCP-processen of machines dezelfde product-worktrees kunnen beheren.
2. PrFlowMachine
Doel: PR's consistent aanmaken, bijwerken, ready zetten en eventueel auto-mergen.
States:
none
-> branch_pushed
-> pr_opened
-> waiting_for_scope_done
-> waiting_for_checks
-> auto_merge_enabled
-> merged
draft path:
-> draft_opened
-> ready_for_review
failure/pause:
-> checks_failed
-> merge_conflict_paused
Regels per PrStrategy:
| Strategie | Eerste task | Tijdens scope | Laatste task |
|---|---|---|---|
STORY |
Branch push + PR openen | PR open houden, geen auto-merge | Na laatste story-task: checks groen -> auto-merge |
SPRINT |
Branch push + draft PR openen | Commits blijven op sprint-branch | Na sprint DONE: PR ready-for-review, geen auto-merge |
Belangrijke regels:
- PR openen mag vroeg; auto-merge activeren mag pas wanneer de scope klaar is.
- Gebruik GitHub branch protection, required checks en eventueel merge queue als merge-gate.
- Gebruik bij merge-acties waar mogelijk een head-SHA guard, bijvoorbeeld via
gh pr merge --match-head-commit. - Maak auto-merge/merge-fouten typed:
CHECKS_FAILEDMERGE_CONFLICTGH_AUTH_ERRORAUTO_MERGE_NOT_ALLOWEDUNKNOWN
- Alleen
MERGE_CONFLICThoort naarSprintRun.PAUSED; CI rood hoort naar task/sprint failure.
3. SprintRunMachine
Doel: sprint-run status niet verspreid over UI, server actions en MCP-tools laten ontstaan.
States:
queued
-> running
-> paused_merge_conflict
-> running
-> done
failure:
-> failed
manual:
-> cancelled
Events:
type SprintRunEvent =
| { type: 'CLAIM_FIRST_JOB' }
| { type: 'TASK_DONE'; taskId: string }
| { type: 'TASK_FAILED'; taskId: string; error: string }
| { type: 'MERGE_CONFLICT'; prUrl: string; files: string[] }
| { type: 'USER_RESUMED' }
| { type: 'USER_CANCELLED' }
PAUSED moet context hebben:
pause_reason = 'MERGE_CONFLICT'pr_url- conflict-bestanden
claude_question_id- eventueel
resume_instructions
Zonder die context wordt PAUSED lastig te onderhouden in UI, MCP en worker-flow.
Pure Transitions En Declaratieve Effects
Een transition-functie moet geen GitHub-call, Prisma-write of filesystem-operatie direct doen. Laat de functie teruggeven wat er moet gebeuren.
Voorbeeld:
type FlowEffect =
| { type: 'CREATE_CLAUDE_QUESTION'; payload: { sprintRunId: string; prUrl: string; files: string[] } }
| { type: 'SET_SPRINT_RUN_STATUS'; sprintRunId: string; status: 'PAUSED' | 'RUNNING' | 'DONE' | 'FAILED' }
| { type: 'ENABLE_AUTO_MERGE'; prUrl: string; expectedHeadSha: string }
| { type: 'MARK_PR_READY'; prUrl: string }
| { type: 'RELEASE_WORKTREE_LOCKS'; jobId: string }
type TransitionResult<State> = {
nextState: State
effects: FlowEffect[]
}
Daarna voert een executor de effects idempotent uit:
async function executeEffects(effects: FlowEffect[]) {
for (const effect of effects) {
switch (effect.type) {
case 'RELEASE_WORKTREE_LOCKS':
await releaseJobLocks(effect.jobId)
break
case 'MARK_PR_READY':
await markPullRequestReady({ prUrl: effect.prUrl })
break
// enzovoort
}
}
}
Voordelen:
- Transitions zijn unit-testbaar zonder GitHub, Git of database.
- Side effects zijn apart idempotent te testen.
- Verboden transitions worden expliciet.
- UI en tools kunnen dezelfde statusbetekenis gebruiken.
Per-Job Verificatie: Base SHA Vastleggen
De huidige verificatie met git diff origin/main...HEAD is niet geschikt wanneer meerdere jobs dezelfde story- of sprint-branch hergebruiken. Task 2 ziet dan ook wijzigingen van task 1.
Aanbevolen wijziging:
- Leg bij claim
ClaudeJob.base_shavast. - Verifieer job-scope met:
git diff <base_sha>...HEAD, ofgit diff <previous_job_head_sha>..HEADals lineaire task-commits verplicht zijn.
- Leg na succesvolle push
ClaudeJob.head_shavast. - Gebruik die SHA ook als guard voor PR/merge-acties.
Dit maakt task-verificatie, PR lifecycle en auto-merge veel voorspelbaarder.
XState, Eigen Module Of Temporal
Eigen pure TypeScript module
Beste eerste stap.
Gebruik dit wanneer:
- De workflow lokaal in MCP-tools draait.
- Postgres de persistente waarheid blijft.
- Je vooral transitions en guards wilt centraliseren.
Voordeel: weinig dependency- en runtime-complexiteit.
XState
XState is passend wanneer:
- transitions complexer worden;
- nested states nuttig zijn;
- je visualisatie of model-based tests wilt;
- meerdere lifecycles als actors gaan samenwerken.
Gebruik XState in deze fase bij voorkeur als pure transition layer, niet als long-running runtime. Persistente status blijft in Postgres.
Bron: XState docs
Temporal
Temporal pas overwegen als de orchestration echt distributed en long-running wordt.
Gebruik dit wanneer:
- workflows uren/dagen lopen;
- meerdere worker-machines betrokken zijn;
- retries, timers en signals crash-proof moeten zijn;
- je wilt dat workflow-code exact verdergaat na server crash.
Niet kiezen als eerste stap: het brengt eigen infra, deployment, determinisme-regels en workflow/activity-splitsing mee.
Bron: Temporal docs
Aanbevolen Implementatievolgorde
Stap 1 - Documenteer huidige transitions
Maak een kleine inventarisatie:
- Welke code wijzigt
SprintRun.status? - Welke code wijzigt
ClaudeJob.status? - Welke code maakt/verwijdert worktrees?
- Welke code maakt PR's, zet PR's ready of enablet auto-merge?
Stap 2 - Introduceer pure machines
Nieuwe bestanden in scrum4me-mcp:
src/flow/worktree-lease-machine.tssrc/flow/pr-flow-machine.tssrc/flow/sprint-run-machine.tssrc/flow/effects.ts
Stap 3 - Verplaats beslislogica uit tools
Laat update-job-status.ts niet zelf bepalen welke lifecycle-actie volgt, maar:
- laad context uit DB;
- stuur event naar machine;
- persist next state;
- voer effects uit;
- emit SSE.
Stap 4 - Maak effects idempotent
Elke effect moet veilig opnieuw uitvoerbaar zijn:
- PR bestaat al -> return bestaande URL.
- PR is al ready -> success.
- Lock bestaat niet meer -> success.
- SprintRun staat al terminal -> geen mutatie.
Stap 5 - Voeg transition-tests toe
Test vooral verboden of gevoelige paden:
- STORY: auto-merge niet bij eerste task.
- STORY: auto-merge pas na laatste task en groene checks.
- SPRINT: draft PR blijft draft tot sprint DONE.
- MERGE_CONFLICT: SprintRun wordt PAUSED met question/context.
- CI rood: task wordt FAILED, niet PAUSED.
- Product-worktree: lock acquire gebeurt vóór create/sync.
- Stale reset: lock release wordt altijd uitgevoerd.
Aanpassing Aan Het Combinatieplan
Het plan zou voor implementatie worden aangescherpt op deze punten:
- F3 niet implementeren als worker post-task hook, maar als MCP-owned PR-flow.
- STORY auto-merge uitstellen tot story-scope klaar is.
- Per-job
base_shaenhead_shatoevoegen voor verificatie en merge guards. - Product-worktree lock acquire vóór
getOrCreateProductWorktree. - Lock-release niet via worker-prompt, maar via server-side terminal transitions.
- PAUSED resume-path expliciet maken in server action en UI.
PLAN_CHATalleen opnemen als die jobflow end-to-end bestaat.- Delete-only verifierverwachting corrigeren: delete-only is niet
EMPTYals er daadwerkelijk bestanden zijn verwijderd.