---
title: "M12 — Drie-fase agent-pipeline voor feature-ideatie"
status: proposal
audience: [maintainer, contributor]
language: nl
last_updated: 2026-05-03
applies_to: [M12]
related:
  - docs/adr/0009-three-phase-feature-pipeline.md
  - docs/plans/M11-claude-questions.md
  - docs/plans/ST-1111-claude-job-trigger.md
  - docs/obsidian-authoring.md
---

# M12 — Drie-fase agent-pipeline voor feature-ideatie

Implementatie van [ADR-0009](../adr/0009-three-phase-feature-pipeline.md): één geïntegreerde flow van een idee op een product naar een geïmplementeerde feature, in drie agent-fasen met menselijke checkpoints. Bouwt verder op M11 (vraagkanaal) en ST-1111 (job queue). Breidt `scrum4me-mcp` uit met docs-tools zodat alle agent-context — DB en docs — via één kanaal loopt.

**Hard dependencies:** M11 + ST-1111 moeten afgerond zijn voor ST-1204.

**Gekozen kaders:**
- Drie aparte job-types — `FEATURE_BRAINSTORM`, `DETAIL_PLAN`, en de bestaande `STORY_EXECUTE`. Geen auto-advance tussen fasen; user triggert fase 2 en 3 expliciet.
- Plan-content blijft file-based (`docs/plans/`); execution-state blijft DB.
- Docs-context loopt via nieuwe MCP-tools (`list_docs`, `read_doc`, `search_docs`) — niet via directe filesystem-read door de worker.
- **`ClaudeBrainstorm` is een aparte entiteit** naast `Story`. Brainstorm-Q&A loopt via `ClaudeBrainstormQuestion` parallel aan M11's `ClaudeQuestion`. M11 wordt **niet** aangeraakt.
- **Workers draaien onder een dedicated `agent_writer`-account** per environment, niet onder een persoonlijke user.
- **Plan-templates** in `docs/plans/templates/` zorgen voor reproduceerbare structuur in fase 2.
- **In-app draft-preview** ship't in v1 (naast `obsidian://`-link) zodat Cowork-only users niet afhankelijk zijn van Obsidian.

---

## ST-1201 — MCP docs-tools (in `scrum4me-mcp`-repo)

**Bestanden**
- `scrum4me-mcp/src/tools/list-docs.ts` — nieuw
- `scrum4me-mcp/src/tools/read-doc.ts` — nieuw
- `scrum4me-mcp/src/tools/search-docs.ts` — nieuw
- `scrum4me-mcp/src/lib/docs-root.ts` — nieuw (env `DOCS_ROOT` resolver + path-safety)
- `scrum4me-mcp/src/lib/front-matter.ts` — nieuw, dependency-vrij parser (mirror `scripts/generate-docs-index.mjs`)
- `scrum4me-mcp/src/index.ts` — registreer drie tools
- `scrum4me-mcp/scripts/smoke-test.ts` — uitbreiden met docs-roundtrip
- `scrum4me-mcp/README.md` — tool-tabel + `DOCS_ROOT` env doc
- `scrum4me-mcp/.env.example` — `DOCS_ROOT` toevoegen

**Stappen**

1. **`docs-root.ts`** — resolver: leest `DOCS_ROOT` env var (absolute path), throw bij ontbreken. Helper `safeJoin(rel)` blokkeert `..`-traversal en symlinks-buiten-root.

2. **`list_docs`** (read-tool) — input `{ category? }`, output `{ path, title, status?, last_updated?, category }[]`. Skip `_*.md` sidecars. Sorteer op `last_updated desc`.

3. **`read_doc`** (read-tool) — input `{ path }` relatief vanaf `DOCS_ROOT`, output `{ path, content, frontMatter, lastModified }`. Cap 200 KB.

4. **`search_docs`** (read-tool) — input `{ query, max_results? }` (default 10, max 30). Substring scan v1; switch naar `lunr`/`fuse.js` als v2 nodig blijkt. Excerpt 200 chars rond eerste match.

5. **Smoke-test**: `list_docs({category:'pattern'})` ≥10 items; `read_doc({path:'patterns/server-action.md'})` bevat "Server Action"; `search_docs({query:'iron-session'})` bevat ADR-0005.

**Aandachtspunten**
- Géén write-tools voor docs in deze story. `write_plan` (fase 2) komt in ST-1206 met striktere path-rules.
- `DOCS_ROOT` is per-environment. Documenteer in `obsidian-authoring.md` (gebeurt in ST-1208).
- Front-matter parser dependency-vrij — geen `gray-matter`.

**Verificatie**
- MCP Inspector toont 3 nieuwe tools
- Smoke-test groen
- Path-traversal test: `read_doc({path:'../package.json'})` → error
- `tsc --noEmit` clean op `scrum4me-mcp`

---

## ST-1202 — Schema: `ClaudeBrainstorm` + worker-account

**Bestanden**
- `prisma/schema.prisma` — `JobType`-enum uitbreiden, `Role`-enum uitbreiden, `ClaudeBrainstorm` + `ClaudeBrainstormQuestion`-models
- `prisma/migrations/<ts>_add_brainstorm_pipeline/migration.sql`
- `prisma/seed-data/agent-writer.ts` — nieuw, seedt service-account per environment
- `prisma/seed.ts` — invoke nieuwe seed
- `vendor/scrum4me`-submodule sync in `scrum4me-mcp` ná merge

**Stappen**

1. **Job-type uitbreiden**:
   ```prisma
   enum JobType {
     STORY_EXECUTE       // bestaand uit ST-1111
     FEATURE_BRAINSTORM  // nieuw — fase 1
     DETAIL_PLAN         // nieuw — fase 2
   }
   ```

2. **`Role`-enum uitbreiden** (bestaand: `OWNER`, `MEMBER`, `DEMO`):
   ```prisma
   enum Role {
     OWNER
     MEMBER
     DEMO
     AGENT_WRITER  // nieuw — alleen voor service-accounts die door workers gebruikt worden
   }
   ```

   Toegangslogica: `requireWriteAccess` accepteert `AGENT_WRITER` net zoals `OWNER`/`MEMBER`. Demo-blok blijft alleen op `DEMO`. UI-rendering hoort `AGENT_WRITER`-users te verbergen uit member-lijsten (eigen filter, niet via role-omittance).

3. **`ClaudeBrainstorm`-model** (vervangt eerdere voorstel `BrainstormSession`):
   ```prisma
   model ClaudeBrainstorm {
     id              String   @id @default(cuid())
     product_id      String
     product         Product  @relation(fields: [product_id], references: [id], onDelete: Cascade)
     created_by      String
     creator         User     @relation("BrainstormCreator", fields: [created_by], references: [id])
     idea_text       String   @db.Text
     status          String   // 'brainstorming' | 'draft_ready' | 'detailing' | 'plan_ready' | 'cancelled' | 'failed'
     draft_path      String?  // bv. 'plans/_draft-feature-foo.md'
     plan_path       String?  // gevuld door fase 2
     pbi_id          String?
     brainstorm_job_id String?
     detail_job_id   String?
     created_at      DateTime @default(now())
     updated_at      DateTime @updatedAt
     questions       ClaudeBrainstormQuestion[]

     @@index([product_id, status])
     @@map("claude_brainstorms")
   }
   ```

4. **`ClaudeBrainstormQuestion`-model** (parallel aan M11's `ClaudeQuestion`, geen wijziging op M11):
   ```prisma
   model ClaudeBrainstormQuestion {
     id             String   @id @default(cuid())
     brainstorm_id  String
     brainstorm     ClaudeBrainstorm @relation(fields: [brainstorm_id], references: [id], onDelete: Cascade)
     product_id     String   // gedenormaliseerd voor SSE-filter (zelfde patroon als M11)
     product        Product  @relation(fields: [product_id], references: [id], onDelete: Cascade)
     asked_by       String
     asker          User     @relation("BrainstormQuestionAsker", fields: [asked_by], references: [id])
     question       String   @db.Text
     options        Json?
     status         String   // 'open' | 'answered' | 'cancelled' | 'expired'
     answer         String?  @db.Text
     answered_by    String?
     answerer       User?    @relation("BrainstormQuestionAnswerer", fields: [answered_by], references: [id])
     answered_at    DateTime?
     created_at     DateTime @default(now())
     expires_at     DateTime

     @@index([brainstorm_id, status])
     @@index([product_id, status])
     @@map("claude_brainstorm_questions")
   }
   ```

5. **Postgres-triggers** op beide tabellen, mirror M11 ST-1101 — emit op `scrum4me_changes` met `entity: 'brainstorm'` resp. `entity: 'brainstorm_question'`. Solo-route filtert beide weg (zelfde update als M11 deed voor `'question'`).

6. **Service-account seed** in `prisma/seed-data/agent-writer.ts`:
   - Maakt één user `agent-writer@scrum4me.local` met role `AGENT_WRITER` als die nog niet bestaat
   - Wachtwoord-hash van een random secret uit env `AGENT_WRITER_SECRET` (zo kan workers' iron-session-vergelijkbare auth via bcrypt-compare valideren)
   - User heeft géén product-membership by default — wordt per `ClaudeBrainstorm.product_id` runtime gemachtigd via een aparte `ProductAgentGrant`-laag (zie ST-1207)

7. `npx prisma migrate dev --name add_brainstorm_pipeline`

**Aandachtspunten**
- M11's `ClaudeQuestion` blijft volledig ongewijzigd. Alleen brainstorm-Q&A gebruikt de nieuwe tabel.
- `entity: 'brainstorm'` en `entity: 'brainstorm_question'` zijn twee nieuwe SSE-payload-waarden — solo-route en M8/M11-routes moeten ze filteren of afhandelen
- `AGENT_WRITER_SECRET` is een nieuwe env var — toevoegen aan `.env.example` + `lib/env.ts`
- `ProductAgentGrant`-tabel komt in ST-1207 (apart) zodat dit story behapbaar blijft

**Verificatie**
- `npx prisma validate` clean
- Seed-run maakt agent-writer user; herhaalde run idempotent (geen duplicate)
- `psql $DIRECT_URL -c "LISTEN scrum4me_changes;"` toont nieuwe entity-waarden bij INSERT
- M11-vraag-flow nog steeds werkend (regressie-check)
- Submodule-sync drift-check ná merge

---

## ST-1203 — MCP brainstorm-question-tools (in `scrum4me-mcp`-repo)

**Bestanden**
- `scrum4me-mcp/src/tools/ask-brainstorm-question.ts` — nieuw
- `scrum4me-mcp/src/tools/get-brainstorm-answer.ts` — nieuw
- `scrum4me-mcp/src/tools/list-open-brainstorm-questions.ts` — nieuw
- `scrum4me-mcp/src/index.ts` — registreer

**Stappen**

1. **Spec-mirror van M11's question-tools**, maar gebonden aan `brainstorm_id` i.p.v. `story_id`:
   - `ask_brainstorm_question({ brainstorm_id, question, options?, wait_seconds? })`
   - `get_brainstorm_answer({ question_id })`
   - `list_open_brainstorm_questions({ brainstorm_id? })`

2. **Limit-enforcement**: `ask_brainstorm_question` weigert als `count(claude_brainstorm_questions WHERE brainstorm_id=… AND status≠'cancelled') ≥ 12`. Returnt `{ error: 'limit_reached' }` zodat de grill-me-skill afrondt.

3. **Auth**: `requireWriteAccess`-check; `userCanAccessProduct(brainstorm.product_id, auth.userId)`. `AGENT_WRITER`-account passeert deze check via `ProductAgentGrant` (ST-1207).

4. **Smoke-test** uitbreiden: ask + answer roundtrip op een test-brainstorm.

**Aandachtspunten**
- Code-duplicatie met M11-tools is aanvaardbaar (twee onafhankelijke flows). Als duplicatie pijn doet, factor in v2 een gemeenschappelijke `question-tool-factory` uit.
- `cancel_brainstorm_question` niet in v1 — alleen agent-asker zou cancellen, en de caps maken het zelden nodig

**Verificatie**
- MCP Inspector toont 3 nieuwe tools (totaal docs+brainstorm = 6 nieuwe in M12)
- Smoke-test groen — ask-with-wait + parallel answer
- Limit-enforcement test: 13e vraag retourneert `limit_reached`

---

## ST-1204 — Fase 1: `FEATURE_BRAINSTORM`-worker

**Bestanden**
- `lib/jobs/handlers/feature-brainstorm.ts` — nieuw
- `lib/jobs/dispatcher.ts` — uitbreiden met nieuwe type
- `lib/agents/brainstorm-prompt.ts` — system-prompt template
- `lib/agents/worker-session.ts` — nieuw — opent een iron-session voor `AGENT_WRITER`-account

**Stappen**

1. **Worker-session helper** — `getAgentWriterSession(productId)`:
   - Authenticate `agent-writer@scrum4me.local` met `AGENT_WRITER_SECRET`
   - Return een MCP-bearer-token gebonden aan deze user voor de duur van het subprocess
   - Audit-log entry: "agent_writer accessed product X for brainstorm Y"

2. **Job-handler** ontvangt `ClaudeBrainstorm.id`:
   - Laad brainstorm + product
   - Genereer slug uit `idea_text` (lowercase, dash, max 40 chars)
   - Bepaal `_draft-feature-<slug>.md`-pad. Update brainstorm: `draft_path`, `status: 'brainstorming'`
   - Spawn Claude Code subprocess met env: `MCP_BEARER` (van worker-session), `DOCS_ROOT`, `BRAINSTORM_ID=<id>`
   - Bij subprocess-exit: status → `draft_ready` (success) of `failed` (non-zero exit)

3. **Prompt-template** — `brainstorm-prompt.ts`:
   - Statisch met `{productName}`, `{ideaText}` placeholders
   - "Je bent in fase 1. Tools: `get_product`, `list_pbis`, `list_docs`, `search_docs`, `read_doc` voor context. `ask_brainstorm_question` om vragen te stellen (max 12). Trigger de `grill-me`-skill. Schrijf het resultaat naar `<draft_path>` via `write_doc_draft` (komt in ST-1206) zodra je voldoende context hebt."

4. **Caps**: max 30 min wall-time → SIGTERM → na 5s SIGKILL.

**Aandachtspunten**
- `write_doc_draft`-tool komt in ST-1206 — tot dan tijdelijke mock die naar stdout dumpt
- grill-me wordt niet hier ingeladen; dat doet Claude Code's eigen skill-systeem zodra de prompt 'm noemt
- Audit-log per worker-spawn is verplicht — nodig voor "wie heeft welke PBI aangemaakt"-traceerbaarheid

**Verificatie**
- Handmatig: maak `ClaudeBrainstorm` via prisma-studio met simpel idee → handler runt → na 5-10min verschijnt `_draft-feature-…md`
- Tijdens run: in-app brainstorm-tracker toont vragen via SSE
- Audit-log heeft entry per spawn

---

## ST-1205 — UI: "Nieuw idee", brainstorm-tracker, draft-preview

**Bestanden**
- `components/product/new-idea-button.tsx` — nieuw
- `components/product/new-idea-dialog.tsx` — nieuw, volgt `docs/patterns/dialog.md`
- `components/product/brainstorm-list.tsx` — nieuw
- `components/product/draft-preview.tsx` — nieuw — in-app modal-preview voor draft + plan
- `actions/brainstorm.ts` — `createBrainstorm`, `cancelBrainstorm`, `triggerDetailPlan`
- `stores/brainstorm-store.ts` — nieuw
- `lib/realtime/use-brainstorm-realtime.ts` — listen op `entity: 'brainstorm'` + `entity: 'brainstorm_question'`
- `app/(app)/products/[id]/page.tsx` — bridge mount + button-render

**Stappen**

1. **NewIdeaDialog** — entity-dialog per `docs/patterns/dialog.md`. Body: textarea (max 2000, char-counter), helper "Beschrijf het idee in eigen woorden — Claude stelt straks vervolgvragen." Submit → `createBrainstorm` Server Action. Demo-modus: knop disabled met tooltip.

2. **BrainstormList** op product-pagina:
   - Status-pills met MD3-tokens (zie `docs/design/styling.md`)
   - Per item: idea-truncate, created_by, last_updated-relative, action-buttons per status
   - `draft_ready` → "Bekijk draft" (opent `<DraftPreview />`) + secondary `obsidian://`-link + "Maak detail-plan" knop
   - `plan_ready` → "Bekijk plan" + "Open PBI" als pbi_id gevuld
   - `brainstorming` → "Annuleer" + indicator "Wacht op vragen" (blinkt als ask-pending)
   - Realtime-update via M12-bridge

3. **DraftPreview** (in-app v1):
   - shadcn `Dialog` met markdown-render (`react-markdown`, al gebruikt in M5 task-detail)
   - Inhoud opgehaald via MCP `read_doc({ path })` door een Server Action `getDraftContent(brainstormId)` die access-check doet
   - Read-only; bewerken gaat via Obsidian (link toont "Open in Obsidian" als secondary action)
   - Footer: "Maak detail-plan"-button (zelfde action als in lijst)

4. **Server Actions** — volgt `docs/patterns/server-action.md`:
   - `createBrainstorm({ productId, ideaText })` — Zod, demo-blok, access-check, transactie insert brainstorm + insert job
   - `triggerDetailPlan(brainstormId)` — vereist `status === 'draft_ready'`, insert `DETAIL_PLAN`-job
   - `cancelBrainstorm(brainstormId)` — alleen creator of OWNER, kill-signal naar job indien actief
   - `getDraftContent(brainstormId)` — read-only, access-check, roept MCP `read_doc` aan

5. **Bridge + store** — analoog aan M11 NotificationsBridge.

**Aandachtspunten**
- Twee bronnen van M12-events: brainstorm-status-changes en brainstorm-questions. Bridge subscribet beide.
- Brainstorm-questions hoeven NIET in M11's notification-bell te verschijnen — ze zitten in BrainstormList op de product-pagina. Houd ze visueel apart van M11-questions.
- Open punt: wil je later toch een unified-bell? Buiten M12-scope.
- Paginate BrainstormList na 10 items, "Toon archief" voor cancelled/failed

**Verificatie**
- E2E tot fase 1: product → "Nieuw idee" → typ → submit → BrainstormList toont "brainstorming" → vragen verschijnen in component → user beantwoordt → status flipt naar "draft_ready" → "Bekijk draft" opent modal met markdown-content
- Demo-modus: alle write-knoppen disabled met tooltip
- Cancel: status flipt, subprocess wordt gekillt

---

## ST-1206 — Fase 2: `DETAIL_PLAN`-worker + `write_plan`-tool + plan-templates

**Bestanden**
- `scrum4me-mcp/src/tools/write-plan.ts` — nieuw, write-tool met strikte path-rules
- `scrum4me-mcp/src/tools/write-doc-draft.ts` — nieuw, write-tool voor `_draft-*.md` (gebruikt door fase 1)
- `scrum4me-mcp/src/index.ts` — registreer
- `lib/jobs/handlers/detail-plan.ts` — nieuw
- `lib/agents/detail-plan-prompt.ts` — system-prompt template
- `docs/plans/templates/milestone.md` — nieuw
- `docs/plans/templates/story.md` — nieuw
- `docs/plans/templates/_README.md` — nieuw, beschrijft wanneer welke template gebruiken

**Stappen**

1. **`write_doc_draft`** (write-tool, geen demo-blok want alleen agent_writer):
   - Input: `{ relativePath, content, overwrite? }`
   - Path moet matchen `plans/_draft-[a-z0-9-]+\.md` (regex enforcement)
   - Schrijven; output `{ path, bytesWritten }`

2. **`write_plan`** (write-tool, geen demo-blok):
   - Input: `{ relativePath, content, overwrite? }`
   - Path moet matchen `plans/(M\d+|ST-\d+)-[a-z0-9-]+\.md` — geen `_draft-`, geen subdirs
   - Mag niet bestaan tenzij `overwrite=true` (default false)
   - Output: `{ path, bytesWritten }`

3. **Plan-templates** — `docs/plans/templates/`:
   - `milestone.md` — voor ≥3 stories. Bevat front-matter-skelet, intro-blok, story-template (h2, bestanden-lijst, stappen, aandachtspunten, verificatie), branch-strategie-blok, acceptatie-blok, scope-out-blok. Gebruik `{{placeholders}}` in dezelfde stijl als `docs/adr/templates/nygard.md`.
   - `story.md` — voor 1-2 stories, lichter
   - `_README.md` — kies-criteria + handmatig-vs-fase-2 instructies. Underscore-prefix → niet in INDEX.

4. **Detail-plan-handler** ontvangt `ClaudeBrainstorm.id`:
   - Laad brainstorm, lees draft via MCP `read_doc({path: brainstorm.draft_path})`
   - Spawn Claude Code subprocess met `MCP_BEARER` (worker-session), `DOCS_ROOT`, `BRAINSTORM_ID`
   - System-prompt:
     - "Fase 2. Lees `read_doc({path:'<draft_path>'})`. Gebruik `search_docs` + `read_doc` voor relevante patterns/ADRs/architecture. Lees `read_doc({path:'plans/templates/milestone.md'})` of `plans/templates/story.md` voor het skelet (kies milestone bij ≥3 stories). Schrijf canonical plan via `write_plan` naar `plans/M{N}-<slug>.md` of `plans/ST-XXXX-<slug>.md` (volgnummer via `list_docs({category:'plan'})` + max+1). Maak vervolgens via `save_pbi` + `save_story` + `save_task` de DB-rijen aan. Geen vragen aan de user — open punten markeer je als `**TODO:**` in het plan."
   - Bij subprocess-exit: update brainstorm `plan_path`, `pbi_id`, status `plan_ready` of `failed`

**Aandachtspunten**
- Plan-templates moeten zelf in INDEX-generator gefilterd worden via `_README.md`-prefix-rule (al bestaand) en via een `templates/`-pad-skip in `scripts/generate-docs-index.mjs` — kleine generator-edit nodig
- Volgnummer-bepaling: lees alle `plans/M\d+-*.md` files, parse max nummer, +1. Race-conditie als twee fase-2-jobs tegelijk draaien — accept; dubbele nummers geven manuele review
- `write_plan` mag een PBI-rij maken voor je het plan schrijft; volgorde in prompt: eerst plan-file (rollback-bestand bij fout), dan DB-rijen
- Path-injection-test verplicht in unit-test

**Verificatie**
- Handmatig: trigger `triggerDetailPlan` op een draft → na 5-15min verschijnt nieuw `plans/M{N}-…md` + PBI in DB → BrainstormList status `plan_ready`
- `npm run docs:index` regenereert INDEX.md met het nieuwe plan en negeert `templates/`
- Path-rules: prompt-injection-test `write_plan({relativePath:'../adr/0009.md', ...})` faalt
- Bestaand plan niet overschreven zonder `overwrite=true`

---

## ST-1207 — `ProductAgentGrant`: per-product machtiging voor `AGENT_WRITER`

**Bestanden**
- `prisma/schema.prisma` — `ProductAgentGrant`-model
- `prisma/migrations/<ts>_add_product_agent_grant/migration.sql`
- `lib/auth/agent-grant.ts` — helper `grantAgentForBrainstorm(brainstormId)`
- `lib/auth/access.ts` — `userCanAccessProduct` uitbreiden om `AGENT_WRITER` met geldige grant te accepteren
- `actions/brainstorm.ts` — `createBrainstorm` maakt grant aan in dezelfde transactie

**Stappen**

1. **`ProductAgentGrant`-model**:
   ```prisma
   model ProductAgentGrant {
     id             String   @id @default(cuid())
     product_id     String
     product        Product  @relation(fields: [product_id], references: [id], onDelete: Cascade)
     agent_user_id  String
     agent          User     @relation(fields: [agent_user_id], references: [id], onDelete: Cascade)
     reason         String   // 'brainstorm:<id>' | 'detail_plan:<id>' | 'story_execute:<id>'
     granted_by     String
     granter        User     @relation("AgentGrantGranter", fields: [granted_by], references: [id])
     created_at     DateTime @default(now())
     expires_at     DateTime // 24h voor brainstorm/detail; 7d voor story_execute

     @@unique([product_id, agent_user_id, reason])
     @@index([agent_user_id, expires_at])
     @@map("product_agent_grants")
   }
   ```

2. **Access-uitbreiding** in `lib/auth/access.ts`:
   - Bestaand: `userCanAccessProduct(productId, userId)` checkt ProductMember
   - Nieuw: als user-role `AGENT_WRITER` is, valid grant-check via `ProductAgentGrant WHERE product_id=… AND agent_user_id=… AND expires_at > now()`

3. **Grant-helper** `grantAgentForBrainstorm(brainstormId)`:
   - Haal agent-user op (gecachet)
   - Insert grant met `reason='brainstorm:<id>'`, `granted_by=brainstorm.created_by`, `expires_at=now+24h`
   - Idempotent via unique-constraint

4. **`createBrainstorm`-action** doet de grant aan in dezelfde transactie als de brainstorm + job. Fase 2 maakt een tweede grant met `reason='detail_plan:<id>'`.

5. **Cleanup-cron** (lichtgewicht — kan in bestaande `expire-questions`-cron uit M11 ST-1107):
   - `DELETE FROM product_agent_grants WHERE expires_at < now()`
   - Hergebruik `CRON_SECRET`-pad

**Aandachtspunten**
- Audit-trail: `granted_by` legt vast welke menselijke user de agent gemachtigd heeft — voor compliance bij eventuele schade-analyse
- Grant verloopt automatisch — voorkomt dat een lang-stilstaande agent later nog kan schrijven
- Demo-user kan geen grants aanmaken (hij kan geen brainstorm starten — demo-blok op `createBrainstorm`)

**Verificatie**
- Unit-test: grant binnen window → access ja; grant verlopen → access nee
- Unit-test: `AGENT_WRITER` zonder grant → access nee
- Bestaande user-flows ongewijzigd (regressie)

---

## ST-1208 — Fase 3-integratie + acceptatietests + docs

**Bestanden**
- `lib/jobs/handlers/story-execute.ts` — uitbreiden om `story.metadata.plan_path` mee te nemen
- `docs/architecture/agent-pipeline.md` — nieuw — sequence-diagram + threat-model
- `docs/patterns/agent-pipeline-stage.md` — nieuw — herbruikbaar pattern
- `docs/runbooks/mcp-integration.md` — uitbreiden met 6 nieuwe tools
- `docs/obsidian-authoring.md` — corrigeer "MCP exposes geen docs"-passage
- `docs/backlog/index.md` — M12-tabel + sectie
- `prisma/seed-data/parse-backlog.ts` — `M12: 'PROPOSED'`
- `CLAUDE.md` — patterns quickref-tabel uitbreiden
- `scripts/generate-docs-index.mjs` — skip `plans/templates/`

**Stappen**

1. **ST-1111 minimale aanpassing**: story-execute-handler leest `story.metadata.plan_path` indien gevuld en includeert "Lees `plan_path` voor je begint" in de prompt. Backwards-compatible.

2. **Architecture-doc** met Mermaid sequence: User → UI → Job → Worker (fase 1) → MCP (DB+docs+brainstorm-Q) → User → Worker schrijft draft → User triggert fase 2 → Worker (fase 2) → write_plan + save_pbi → User triggert fase 3 → bestaande flow.

3. **Pattern-doc** "agent-pipeline-stage" — generiek per fase: input-state, agent-context, output-artefact, exit-criteria, escalation-pad, audit-trail.

4. **Obsidian-authoring update** — vervang sectie "the mcp server does not expose docs/ content" door verwijzing naar ADR-0009 + de 6 nieuwe tools (`list_docs`, `read_doc`, `search_docs`, `write_plan`, `write_doc_draft`, `ask_brainstorm_question`/`get_brainstorm_answer`/`list_open_brainstorm_questions`). Behoud "Obsidian is geen tweede waarheidsbron"-stelling.

5. **Index-generator** — skip `plans/templates/` zodat templates niet in `INDEX.md` belanden.

6. **Backlog + parser** — M12 als PROPOSED; activeren wanneer M11 + ST-1111 done.

7. **Acceptatie-scenario's** (zeven):
   1. **Happy path E2E**: idee → grill-me → draft → fase 2 → plan + PBI → fase 3 → code ✅
   2. **MCP docs-tool**: agent vindt via `search_docs` + `read_doc` de juiste pattern ✅
   3. **Path-safety**: prompt-injection via `write_plan` of `read_doc` faalt ✅
   4. **Demo-block**: alle drie de Server Actions geblokkeerd voor demo-user ✅
   5. **Cancel mid-flight**: brainstorm cancellen kill't subprocess ✅
   6. **Plan-overwrite-protectie**: bestaand plan niet overschreven ✅
   7. **Agent-grant-expiry**: agent kan na grant-expiry geen `save_pbi` meer doen voor dat product ✅

**Aandachtspunten**
- Acceptatie 1 vereist M11 + ST-1111 volledig groen
- Threat-model in architecture-doc: prompt-injection via `idea_text` (max-length + escape), agent-grant-misbruik (audit-log + auto-expire), schema-divergentie scrum4me ↔ scrum4me-mcp (sync ná merge)
- Pipeline-pattern is bruikbaar voor toekomstige flows (bug-triage, refactor-planning) — pattern-doc moet generiek genoeg zijn

**Verificatie**
- 7/7 acceptatie groen (1+5 handmatig, 2+3+4+6+7 unit-test)
- `npm run docs:index` clean — M12-plan, ADR-0009, nieuwe pattern + architecture in INDEX.md, templates niet
- `npm run lint && npm test && npm run build` clean op Scrum4Me
- `tsc --noEmit && npm test` clean op `scrum4me-mcp`
- Submodule-sync ná merge

---

## Branch- en commit-strategie

Per [Branch & PR Strategy](../runbooks/branch-and-commit.md):

- **Eén branch op Scrum4Me**: `feat/M12-feature-pipeline` afgesplitst van `main` ná M11-merge
- **Aparte branch op `scrum4me-mcp`**: `feat/M12-docs-and-plan-tools`
- **MCP-PR pas mergen ná Scrum4Me-PR** + submodule-sync
- **Push pas na handmatige acceptatie** van scenario 1 (happy path) + 3 (path-safety) + 7 (grant-expiry)

Commit-volgorde:

```
feat(ST-1201): add list_docs, read_doc, search_docs MCP tools
feat(ST-1201): add DOCS_ROOT env + safeJoin path validator
feat(ST-1202): add JobType enum extension
feat(ST-1202): add Role.AGENT_WRITER + agent-writer seed
feat(ST-1202): add ClaudeBrainstorm + ClaudeBrainstormQuestion models
feat(ST-1202): add notify triggers for brainstorm entities
feat(ST-1203): add ask_brainstorm_question + get_brainstorm_answer + list_open_brainstorm_questions
feat(ST-1204): add agent-writer worker-session helper
feat(ST-1204): add brainstorm-prompt template
feat(ST-1204): add FEATURE_BRAINSTORM job handler
feat(ST-1205): add createBrainstorm + triggerDetailPlan + cancelBrainstorm server actions
feat(ST-1205): add brainstorm Zustand store + realtime hook
feat(ST-1205): add NewIdeaButton + dialog on product page
feat(ST-1205): add BrainstormList component
feat(ST-1205): add DraftPreview modal with read_doc fetch
feat(ST-1206): add write_doc_draft + write_plan MCP tools
docs(ST-1206): add plans/templates/milestone.md + story.md + _README.md
feat(ST-1206): add detail-plan-prompt template
feat(ST-1206): add DETAIL_PLAN job handler
feat(ST-1207): add ProductAgentGrant model + migration
feat(ST-1207): extend userCanAccessProduct with grant-check
feat(ST-1207): wire grantAgentForBrainstorm into createBrainstorm + triggerDetailPlan
feat(ST-1207): add grant-cleanup to expire-questions cron
feat(ST-1208): wire plan_path into story-execute prompt
docs(ST-1208): add agent-pipeline architecture doc
docs(ST-1208): add agent-pipeline-stage pattern
docs(ST-1208): update obsidian-authoring to reflect MCP docs-tools
docs(ST-1208): skip plans/templates from index-generator
chore(ST-1208): backlog M12 + parser PROPOSED
```

---

## Buiten scope (volgende milestones)

- **Auto-advance fase 1 → 2** wanneer de gebruiker dat per brainstorm aanvinkt (default blijft handmatige checkpoint)
- **Multi-agent-detail-plan** (twee agents schrijven elk een variant; user kiest)
- **Idee-clustering** via embeddings ("lijken deze 3 brainstorms op elkaar?")
- **Stats-dashboard**: gemiddelde tijd per fase, % brainstorms die plan_ready halen, % geïmplementeerd
- **Bulk-import** van bestaande backlog-items naar de pipeline
- **Cross-product-pipeline**: idee dat impact heeft op meerdere producten
- **Unified-bell** die zowel M11- als M12-vragen toont
- **Edit-in-place voor draft in DraftPreview** (v1 is read-only; bewerken via Obsidian)
