docs: add ADR-0009 and M12 three-phase pipeline planning docs
This commit is contained in:
parent
422df4f9fb
commit
152ed44e84
3 changed files with 587 additions and 0 deletions
512
docs/plans/M12-three-phase-feature-pipeline.md
Normal file
512
docs/plans/M12-three-phase-feature-pipeline.md
Normal file
|
|
@ -0,0 +1,512 @@
|
|||
---
|
||||
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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue