---
title: "Herbruikbaar scripts/insert-milestone.ts"
status: done
audience: [maintainer, contributor]
language: nl
last_updated: 2026-05-03
applies_to: []
---

# Plan: herbruikbaar `scripts/insert-milestone.ts`

## Doel

Eén commando dat een specifieke milestone (PBI + stories + tasks) uit de backlog leest en idempotent toevoegt aan de DB, zónder bestaande data te raken. Voor M8 nu, en voor M9..M∞ later.

## Bron-keuze: backlog ipv plan-bestand

Twee bronnen denkbaar:
- **`.Plans/<datum>-<slug>.md`** — freeform plan-tekst, niet gestructureerd, niet gecommit
- **`docs/backlog/index.md`** — al strict gestructureerd, gecommit, single source of truth voor alle bestaande seed-pipelines

Voorstel: het script leest de **backlog**. Workflow blijft natuurlijk:

1. Plan schrijven naar `.Plans/<naam>.md` (lokaal, draft)
2. Milestone-sectie + stories formaliseren in `docs/backlog/index.md` (PR)
3. Na merge: `npm run db:insert-milestone -- M8 [--product SCRUM4ME]`

Eén canonical bron, geen ambiguïteit, en de bestaande parser doet 90% van het werk al.

## Wijzigingen

### 1. `prisma/seed-data/parse-backlog.ts` — tolerant maken

Huidige parser kent alleen M0..M6 in `MILESTONE_PRIORITY/_GOAL/_SPRINT_STATUS` + asserts ≥8 milestones / ≥60 stories. M7 en M8 worden nu stilletjes overgeslagen.

Concrete edits:
- Voeg `M7` en `M8` toe aan de drie maps (M7: priority 4, sprint COMPLETED, goal "MCP-server voor Claude Code"; M8: priority 4, sprint COMPLETED, goal "Realtime updates voor Solo Paneel")
- Voor onbekende sleutels: fallback naar `priority: 4`, `sprint_status: 'COMPLETED'`, `goal: <header-title>`. Dat maakt M9..M∞ vanzelf bruikbaar zonder code-wijziging
- Verwijder de strikte filter `KNOWN_KEYS.includes(...)` of verleg naar een "alle-M[\d.]+ headers" check
- Voeg optionele `loadBacklog(repoRoot, { strict?: boolean })` toe. `strict: true` (default) behoudt de bestaande "≥8 milestones, ≥60 stories" asserts (zodat de seed niet stilletjes anders gedraagt). Insert-milestone roept met `strict: false`

### 2. `scripts/insert-milestone.ts` (nieuw, ~90 regels)

```
Usage: tsx scripts/insert-milestone.ts <milestone-key> [--product <code>] [--dry-run]
Default product code: SCRUM4ME
```

Logica:
1. Parse args; valideer dat milestone-key matcht `^M[\d.]+$`
2. `loadBacklog(repoRoot, { strict: false })`
3. Zoek milestone op `key`; faal helder met "milestone <key> not found in docs/backlog/index.md" als ie er niet in staat
4. Lookup product via `code` (default `SCRUM4ME`); faal als niet gevonden
5. Upsert PBI:
   - `where: { product_id_code: { product_id, code: milestone.key } }`
   - sort_order = `(max(sort_order) van bestaande PBIs in product) + 1` als nieuw, anders ongemoeid
6. Voor elke story:
   - Upsert Story op `(product_id, code = story.ref)`
   - status = `'DONE'` of `'OPEN'` zoals gemarkeerd in markdown
   - sort_order, priority en pbi_id correct ingesteld
7. Voor elke task: bulk insert **alleen** als de story op dit moment 0 tasks heeft (idempotent — herhaling dupliceert niets)
8. Print samenvatting: `M8: PBI created, 6 stories upserted (1 created, 5 unchanged), 6 tasks created`
9. `--dry-run`: alle DB-calls overslaan, alleen wat het zou doen printen

Edge cases:
- Story-code conflict tussen producten: schema heeft `@@unique([product_id, code])` op Story dus dit is per-product safe
- Tasks zonder `code` veld in DB (klopt — code wordt afgeleid van story.code + index in get_claude_context)
- Demo-product: script accepteert `--product DEMO` o.i.d. — niet hardcoded SCRUM4ME

### 3. `package.json` script

```json
"db:insert-milestone": "tsx scripts/insert-milestone.ts"
```

### 4. Verificatie na implementatie

- Dry-run eerst: `npm run db:insert-milestone -- M8 --dry-run`
- Daarna echt: `npm run db:insert-milestone -- M8`
- In Prisma Studio of via SQL: zie M8 PBI, 6 stories, 6 tasks onder SCRUM4ME-product
- Tweede run: `npm run db:insert-milestone -- M8` → "0 created, 6 unchanged" — geen duplicaten
- Niet-bestaande key: `npm run db:insert-milestone -- M99` → "milestone M99 not found"
- Bestaande seed-flow blijft werken: `prisma db seed` met `strict: true` faalt nog steeds bij format-drift in de backlog

## Branch- en PR-strategie

`scripts/insert-milestone.ts` is orthogonaal aan ST-801. Twee keuzes:

- **A. Eigen mini-branch + PR** — `tooling/insert-milestone-script`, ~95 regels code, makkelijk reviewbaar, gemerged voordat M8 verder gaat. Daarna gebruiken om M8 in DB te zetten en met de implementatie door.
- **B. Aan ST-801 plakken** — voegt scope toe aan een PR die al code ↔ infra-overschrijdend is (migratie + tools).

Voorgestelde keuze: **A**. De tool is breder bruikbaar dan M8 alleen.

## Volgorde

1. Switch naar `main` (ST-801 blijft op zijn eigen branch staan)
2. Branch `tooling/insert-milestone-script`
3. Edit `parse-backlog.ts` (M7/M8 maps + tolerant + strict-mode option)
4. Schrijf `scripts/insert-milestone.ts`
5. Voeg `db:insert-milestone` toe aan `package.json`
6. Lokaal testen met M8 (dry-run + echt + tweede run)
7. Commit, push, PR
8. Na merge: tool gebruiken om M8 in DB te krijgen, daarna ST-802 oppakken op feat/ST-801-branch

## Geschatte size

- ~10 regels parser-edit
- ~95 regels nieuw script
- ~1 regel package.json
- ~25 regels test/usage doc in script-comment
- 1 commit, 1 PR
