Scrum4Me/docs/runbooks/plan-to-pbi-flow.md
Janpeter Visser ff22196714
Sprint: Stories en taken krijgen één voorspelbare volgorde gekoppeld aan hun code; drag-and-drop herordening voor stories/taken verdwijnt, priority wordt puur label. (#201)
* feat(code): add parseCodeNumber helper to lib/code.ts

Pure helper that extracts the trailing numeric sequence from a code string
(ST-007 → 7, T-42 → 42). Non-conforming codes fall back to Number.MAX_SAFE_INTEGER
so they sort to the end. Includes 5 unit tests.

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

* feat(tasks): add code field to BacklogTask type and all task selects

Adds `code: string | null` to BacklogTask interface and includes it in
all Prisma task.findMany selects (backlog API, stories tasks API, page
hydration routes). Updates coerceTaskPayload and test fixtures to match.

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

* feat(sort-order): derive story/task sort_order from parseCodeNumber(code)

All create paths (createStoryAction, saveTask, createTaskAction,
materializeIdeaPlanAction) and code-edit paths (updateStoryAction, saveTask
update) now set sort_order = parseCodeNumber(code) instead of last+1.
Removes stale last-record queries from create paths.

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

* fix(sort-order): decouple sprint membership actions from sort_order

createSprintAction and addStoryToSprintAction no longer write sort_order
when adding stories to a sprint. sort_order is derived from code via
parseCodeNumber, so membership should only set sprint_id + status.

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

* refactor(ordering): remove priority from all story/task orderBy

Story- en taak-ordering is nu puur sort_order asc (created_at als
tiebreaker). PBI-ordering (priority + sort_order) blijft ongewijzigd.

Gewijzigd: backlog/route, pbis/stories/route, claude-context/route,
next-story/route, workspace/route, tasks/route, sprint-runs (query +
in-memory sort), solo-workspace-server, page.tsx (app + mobile + sprint),
store compareStory, actions/sprints story-query, next-story test.

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

* refactor(dnd): remove drag-and-drop reorder for stories and tasks

- Remove reorderStoriesAction, reorderTasksAction, reorderSprintStoriesAction
- Delete REST route app/api/stories/[id]/tasks/reorder/route.ts
- Remove DnD from backlog story-panel and task-panel (flat list)
- Remove reorder-within-sprint branch from sprint-board-client handleDragEnd
- Switch SortableSprintRow to plain SprintRow using useDraggable (membership drag kept)
- Remove all DnD from task-list (status toggle + edit kept)
- Remove story-order/task-order/sprint-story-order/sprint-task-order mutation types and store handlers
- Remove related tests for deleted reorder route; fix sprint store tests

* feat(backlog): toon code-badge op backlog-taakkaarten

Geeft code={task.code} door aan <BacklogCard> in TaskCard (task-panel.tsx).
BacklogCard rendert de CodeBadge al conditionally — alleen de prop ontbrak.

* feat(migration): backfill story/task sort_order from code numeric suffix

One-time Prisma migration that sets sort_order = trailing numeric part
of code for all existing stories and tasks, consistent with
parseCodeNumber (fallback = Number.MAX_SAFE_INTEGER for non-conforming
codes). PBIs are intentionally excluded.

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

* docs+tests(sort-order): update for code-binding order on stories/tasks

- Rewrite docs/patterns/sort-order.md: float-insertion PBI only; story/task
  sort_order = parseCodeNumber(code), never drag/membership mutated
- Update plan-to-pbi-flow.md: sort_order auto, sprint_id param, priority=label
- Update make-plan.md: priority=label, array order = execution order
- Update rest-contract.md: fix sprint-tasks ordering, remove reorder endpoint
- Add ADR-0011: code is bindende volgordesleutel voor stories/taken
- Regenerate docs/INDEX.md via npm run docs
- Remove reorderStoriesAction/reorderTasksAction mocks from backlog tests
- Remove dnd-kit mocks from task-panel test (panel no longer uses dnd)
- Extend materializeIdeaPlanAction test: assert sort_order=parseCodeNumber(code)

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 19:02:36 +02:00

10 KiB
Raw Blame History

title status audience language last_updated when_to_read
Plan → Sprint/PBI/Story/Task workflow active
ai-agent
maintainer
nl 2026-05-11 Wanneer de gebruiker een plan goedkeurt en je het werk via Scrum4Me-MCP wilt vastleggen — inclusief sprint-lifecycle.

Plan → Sprint / PBI / Story / Task workflow

Hoe je een goedgekeurd plan omzet naar een hiërarchie van Sprint + PBI + Story + Task(s) via de Scrum4Me-MCP, zónder de taken meteen uit te voeren. Eén PBI = één increment = één sprint.

Dit is de creatie-kant van het werk. De uitvoer-kant staat in CLAUDE.md → "Hoe werk vinden" en docs/runbooks/mcp-integration.md → Batch-loop.

Sprint-tools create_sprint en update_sprint zijn live in scrum4me-mcp (PBI-12). Tool-reference: mcp-integration.md.


Wanneer wel — wanneer niet

Type werk Sprint + PBI maken?
Nieuwe feature, refactor, UX-aanpassing, performance-fix Ja
Bug-fix die meer dan een trivial-edit vereist Ja
Doc-only edit (CLAUDE.md, runbook, README) Nee — direct edit, commit, klaar
Typo, format-fix, dead-code verwijdering (<10 regels) Nee
Spike / verkenning zonder concrete output Nee — log eventueel als Idea (M12)

Sprint volgt PBI: geen PBI → geen sprint. Twijfel? Vraag het.


De vier-laagse flow

plan goedgekeurd
   │
   ├─ create_sprint        → Sprint-record (status=OPEN, start_date=vandaag)
   │     │
   │     └─ create_pbi     → PBI-record onder dat product
   │            │
   │            └─ create_story  → Story-record, koppelt aan de actieve sprint
   │                   │
   │                   └─ create_task (× N)  → sprint_id geërfd van story
   │
   ├─ stop — wachten op uitvoer-instructie
   │
   ├─ … execution-fase via "Hoe werk vinden" …
   │
   └─ PR merged + verify groen  →  update_sprint(status=CLOSED, end_date=vandaag)

0. create_sprint (vóór alle andere create-calls)

{ product_id, code, sprint_goal, status: 'OPEN', start_date? }
  • code — kort label, max 30 chars. Suggestie: S-{YYYY-MM-DD}-{kebab-PBI-titel} of een lopende teller (S-2026-05-11-web-push).
  • sprint_goal — één regel, het increment in mensen-taal (komt uit de "Context" van het goedgekeurde plan).
  • status — start op OPEN.
  • start_date — vandaag; leeg laten als de server dit zelf invult.
  • Geen reuse: altijd nieuw record. Bestaande OPEN-sprints van eerder werk blijven naast deze nieuwe leven; niet automatisch sluiten.

Eén PBI per sprint is de afgesproken één-op-één-koppeling. Als een plan logisch in meerdere onafhankelijke PBI's uiteenvalt, maak je ook meerdere sprints.

1. create_pbi

{ product_id, title, description?, priority, sort_order? }
  • title — korte feature-naam, geen PBI-nummer als prefix (DB kent al een id)
  • description — markdown, het "wat & waarom" uit de Context-sectie van het plan
  • priorityLOW | NORMAL | HIGH (default NORMAL); pas op HIGH zetten als de gebruiker het zelf zegt
  • sort_order — leeg laten; server zet last + 1 binnen de priority-groep
  • Status start automatisch op OPEN

2. create_story

{ pbi_id, sprint_id, title, description?, acceptance_criteria?, priority }
  • title — concreet, in user-story stijl als dat past ("Als developer wil ik …")
  • description — technische context, scope-grenzen, niet-doelen
  • acceptance_criteria — markdown checklist (- [ ] …); bepaalt wanneer de Story DONE is
  • product_id wordt afgeleid uit de PBI — niet meegeven
  • sprint_id — geef de zojuist aangemaakte sprint mee. Als er meerdere OPEN-sprints bestaan: bevestig eerst met de gebruiker welke sprint geldt.
  • sort_order — NIET meegeven. De server berekent sort_order = parseCodeNumber(code) automatisch. De volgorde van stories = de volgorde van hun codes (= aanroep-volgorde); niet priority.
  • Status start op OPEN

Eén story per PBI is de gebruikelijke verhouding. Splits alleen op in meerdere stories als het plan logisch in onafhankelijk-shipbare delen valt — let op dat dit dan ook meerdere sprints betekent.

3. create_task (één call per taak)

{ story_id, title, description?, implementation_plan?, priority }
  • title — werkwoord-vorm: "Implementeer …", "Verplaats …", "Voeg test toe voor …"
  • description — wat de taak afdekt, in 1-3 zinnen
  • implementation_planbelangrijk: markdown met de daadwerkelijke stappen + file-paths + reuse-pointers; dit is wat de Implementation-agent later inleest
  • sort_order — NIET meegeven. De server berekent sort_order = parseCodeNumber(code) automatisch. De uitvoervolgorde = de aanroep-volgorde (= code-volgorde); niet priority.
  • sprint_id wordt geërfd van de Story — niet meegeven
  • Status start op TO_DO

De uitvoervolgorde van taken is gelijk aan de aanroep-volgorde van create_task (= code-volgorde). priority is een label (urgentie), géén sorteerkriteria. Zet voorbereidende taken (data-model, types) vóór UI-taken; tests komen ná de feature-implementatie tenzij TDD expliciet is afgesproken.


Hardstop — wachten op uitvoer-instructie

Na create_task (de laatste): stop. Niet:

  • Branch aanmaken
  • Code wijzigen
  • update_task_status naar IN_PROGRESS zetten
  • get_claude_context aanroepen om "vast te beginnen"

De gebruiker leest de aangemaakte items, eventueel via de UI, en geeft expliciet de instructie "voer de taken uit" / "pak deze story" / "begin met taak 1". Pas dan schakelt de flow over naar de execution-loop uit CLAUDE.md → "Hoe werk vinden".


Sprint sluiten — update_sprint

Na de execution-fase (laatste taak DONE → branch gepusht → PR aangemaakt) wordt de sprint pas CLOSED als beide condities waar zijn:

  1. PR merged op main — detecteer met gh pr view <num> --json mergedAt (niet-leeg = merged) of een GitHub-merge-webhook
  2. Verify groengh pr checks <num> allemaal , óf de bestaande mcp__scrum4me__verify_sprint_task tool slaagt voor de laatste taak

Pas dan:

update_sprint({ sprint_id, status: 'CLOSED', end_date: today })
   → status = CLOSED

Wat als één van beide rood is?

Situatie Handmatige flow Cron-flow (auto)
PR merged, verify rood Sprint blijft OPEN. Hot-fix taak/PR, daarna close-check herhalen. Cron mag de sprint na N mislukte verify-runs (drempel TBD) op FAILED zetten.
Verify groen, PR niet merged Sprint blijft OPEN. Wacht op review/merge. Cron mag na X dagen zonder merge op FAILED (stale-detectie).
PR gesloten zonder merge Sprint blijft OPEN totdat gebruiker beslist. Cron mag direct op FAILED zetten — PR-closed && !merged is een eindstatus.
Werk geannuleerd door gebruiker Sprint → ARCHIVED (handmatig). Niet door cron — vereist gebruikersactie.

Cron-trigger: een geplande job mag dus zowel CLOSED zetten (happy-path: merge + verify groen) als FAILED (sad-path: stale PR, blijvend rode verify, PR-closed zonder merge). De drempels (N, X) en transitie-policy komen in het vervolg-PBI voor de MCP-tools — zie docs/plans/sprint-mcp-tools.md.


Verhouding tot de planning-agent

docs/plans/tweede-claude-agent-planning.md (status: proposal) beschrijft een automatische planning-agent die deze flow uit een PLANNING-job zelfstandig uitvoert. Tot die agent er is, doet de Claude-Code-sessie het handmatig zoals hierboven. De toolchain (create_sprint/create_pbi/create_story/create_task + update_sprint) blijft identiek — de agent zal dezelfde MCP-tools gebruiken.


Korte voorbeeld-sessie

Gebruiker: "plan goedgekeurd"
Claude:    create_sprint({ product_id, code: "S-2026-05-11-web-push",
                           sprint_goal: "Web-Push end-to-end voor open vragen",
                           status: "OPEN" })
           → sprint_id: 73

           create_pbi({ product_id, title: "Web-Push notifications voor open vragen",
                        description: "<plan-context>", priority: "NORMAL" })
           → pbi_id: 142

           create_story({ pbi_id: 142,
                          title: "PBI-142: web-push end-to-end",
                          description: "<scope>",
                          acceptance_criteria: "- [ ] Service worker geregistreerd\n- [ ] …",
                          priority: "NORMAL" })
           → story_id: 988  (gekoppeld aan sprint 73)

           create_task({ story_id: 988, title: "Voeg VAPID-keys toe aan env-schema",
                         implementation_plan: "1. lib/env.ts uitbreiden …",
                         priority: "NORMAL" })
           create_task({ story_id: 988, title: "Server action: subscribe-endpoint",
                         implementation_plan: "…", priority: "NORMAL" })
           create_task({ story_id: 988, title: "Vitest: subscribe-endpoint smoke",
                         implementation_plan: "…", priority: "NORMAL" })

           "Sprint 73 (OPEN), PBI 142, Story 988 met 3 taken aangemaakt.
            Klaar om uit te voeren zodra je 'voer uit' zegt."

Gebruiker: "voer uit"
Claude:    <execution-loop volgens 'Hoe werk vinden' — branch, code, commit, push, PR>

…na PR-merge + verify groen…

Claude:    update_sprint({ sprint_id: 73, status: "CLOSED", end_date: "2026-05-12" })
           → sprint 73 status = CLOSED
           "Sprint 73 gesloten. Increment shipped."

Verwante docs