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

207 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: "Plan → Sprint/PBI/Story/Task workflow"
status: active
audience: [ai-agent, maintainer]
language: nl
last_updated: 2026-05-11
when_to_read: "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"](../../CLAUDE.md) en [docs/runbooks/mcp-integration.md → Batch-loop](./mcp-integration.md).
> Sprint-tools `create_sprint` en `update_sprint` zijn live in scrum4me-mcp (PBI-12). Tool-reference: [mcp-integration.md](./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
- **`priority`** `LOW | 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_plan`** **belangrijk**: 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"](../../CLAUDE.md).
---
## 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 groen** `gh pr checks <num>` allemaal ✅, óf de bestaande [`mcp__scrum4me__verify_sprint_task`](./mcp-integration.md) 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](../plans/sprint-mcp-tools.md).
---
## Verhouding tot de planning-agent
[docs/plans/tweede-claude-agent-planning.md](../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
- [docs/runbooks/mcp-integration.md](./mcp-integration.md) volledige MCP-tool reference + execution-loop
- [docs/runbooks/branch-and-commit.md](./branch-and-commit.md) git-discipline bij uitvoer
- [docs/runbooks/worker-idempotency.md](./worker-idempotency.md) job-status protocol (uitvoer-fase)
- [docs/plans/tweede-claude-agent-planning.md](../plans/tweede-claude-agent-planning.md) toekomstige planning-agent (proposal)