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
75
docs/adr/0009-three-phase-feature-pipeline.md
Normal file
75
docs/adr/0009-three-phase-feature-pipeline.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
title: "ADR-0009: Three-phase agent pipeline for feature ideation → plan → implementation"
|
||||
status: proposed
|
||||
date: 2026-05-03
|
||||
---
|
||||
|
||||
# ADR-0009: Three-phase agent pipeline for feature ideation → plan → implementation
|
||||
|
||||
## Status
|
||||
|
||||
Proposed
|
||||
|
||||
## Context
|
||||
|
||||
Today a new feature-idea has no integrated path inside Scrum4Me. The user either types a PBI/plan by hand, or starts an ad-hoc Claude session outside the app and pastes the result back in. Both routes lose product-context (the agent doesn't see the existing PBIs, sprint, or done stories), lose docs-context (patterns and ADRs are not consulted), and produce plans of varying depth.
|
||||
|
||||
Several building blocks exist or are in flight that, on their own, address parts of the problem:
|
||||
|
||||
- **M11** — agent ↔ user question channel via persistent table + LISTEN/NOTIFY (`docs/plans/M11-claude-questions.md`).
|
||||
- **ST-1111** — "Voer uit"-knop that enqueues a Claude Code job (`docs/plans/ST-1111-claude-job-trigger.md`).
|
||||
- **MCP tools** — 20 tools that expose product/PBI/story/task/sprint state from the DB.
|
||||
- **Docs as filesystem** — `CLAUDE.md`, `docs/INDEX.md`, patterns, ADRs and architecture notes are readable by any agent that has repo access.
|
||||
- **`grill-me` skill** — relentless interview to surface assumptions and decision-tree branches.
|
||||
- **Plans-as-files convention** — canonical plans live in `docs/plans/*.md` with front-matter; drafts as `_draft-*.md` sidecars (`docs/obsidian-authoring.md`, ADR-0008).
|
||||
|
||||
What is missing is the orchestration: a single, repeatable flow that composes these pieces from one entry point on the product page, with explicit human checkpoints, so the user can go from "I have an idea" to "an agent is implementing it" without ad-hoc setup each time.
|
||||
|
||||
## Decision
|
||||
|
||||
We adopt a **three-phase agent pipeline**, triggered by a "New idea"-button on the product page. Each phase is an independent `Job` row picked up by the existing Claude Code worker (ST-1111). A human reviews the artefact between each phase; nothing auto-advances.
|
||||
|
||||
| Fase | Job type | Input | Skill | Output |
|
||||
|---|---|---|---|---|
|
||||
| 1. Brainstorm | `FEATURE_BRAINSTORM` | `{ productId, ideaText, userId }` | `grill-me` | `docs/plans/_draft-feature-<slug>.md` |
|
||||
| 2. Detail plan | `DETAIL_PLAN` | path to draft | none (custom system-prompt) | `docs/plans/ST-XXXX-<slug>.md` + PBI/Story/Task rows via MCP |
|
||||
| 3. Implementation | reuses ST-1111 | story id | (per-story choice) | code changes |
|
||||
|
||||
Each agent gets context from two sources, both via MCP:
|
||||
|
||||
1. **DB-context via MCP** — `get_product`, list of PBIs/stories/sprints, related done work (existing 20 tools).
|
||||
2. **Docs-context via new MCP tools** — extend `scrum4me-mcp` with `list_docs`, `read_doc`, `search_docs`. The agent no longer reads `docs/` directly from the filesystem; all docs access is mediated by the MCP server.
|
||||
|
||||
This is a deliberate change from the position recorded in `docs/obsidian-authoring.md` ("the mcp server does not expose docs/ content"). That stance held while docs were only consumed by interactive Claude Code sessions running in the repo. The pipeline introduces background workers and a future where the worker may run outside the repo (container, separate host); MCP-mediated docs access keeps the contract uniform across both modes and lets us version, log, and rate-limit docs reads alongside DB reads.
|
||||
|
||||
Plans remain file-based (per current convention). Execution state (jobs, PBIs, stories, tasks, questions) remains DB-based. A lightweight `BrainstormSession` row may be added in fase 1 to expose status in the UI; the file is the source of truth for content.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- One predictable flow per feature, replacing ad-hoc Claude sessions outside the app.
|
||||
- Each fase is replaceable in isolation (e.g., swap `grill-me` for a different skill in fase 1 without touching fase 2 or 3).
|
||||
- Human review-points between fases prevent runaway agents and let plans land in Obsidian for graph/backlinks inspection before any code is written.
|
||||
- Reuses M11, ST-1111, MCP tools and docs-as-filesystem — no new source of truth.
|
||||
- Plans created this way are indistinguishable from hand-written plans (`docs/plans/ST-XXXX-…md` with the same front-matter), so the existing INDEX generator and Obsidian properties UI keep working.
|
||||
|
||||
### Negative
|
||||
|
||||
- Three job types to define, queue, and observe — more surface area than a single end-to-end agent.
|
||||
- Hybrid file/DB model: the pipeline coordinates a file (the plan) with rows (the jobs and DB entities). Mismatches (file deleted, job orphaned) need explicit handling.
|
||||
- The grill-me phase can run open-ended if the agent fails to converge. Mitigation: per-session question cap and a "stop and write what you have" timeout.
|
||||
- Hard dependency on M11 and ST-1111 being shipped first; cannot start fase-1 implementation until the question channel and job queue are live.
|
||||
- Extending `scrum4me-mcp` with three docs-tools requires schema work in the MCP repo and a coordinated release with this repo. `docs/obsidian-authoring.md` must be updated to reflect that the MCP now exposes docs.
|
||||
- Docs reads through MCP add a small latency and serialization step compared to direct file reads; acceptable because docs are small and rarely fetched per turn.
|
||||
|
||||
## Confirmation
|
||||
|
||||
Implementation plan: `docs/plans/M12-three-phase-feature-pipeline.md`. Open implementation choices that were resolved while drafting the plan and are recorded here for traceability:
|
||||
|
||||
- **Worker authentication.** Workers run under a dedicated service-account user with role `agent_writer`. The account is provisioned per environment (seed) and bound to the `ClaudeBrainstorm.created_by` for audit. This avoids granting personal user-credentials to background processes and gives demo-block + access-checks a single subject to reason about.
|
||||
- **Brainstorm Q&A schema.** Brainstorm questions live in a new `ClaudeBrainstormQuestion` table linked to `ClaudeBrainstorm`, parallel to M11's `ClaudeQuestion` (which stays story-bound). Avoids retrofitting M11 with optional `story_id`.
|
||||
- **Plan templates.** Canonical plan-templates live under `docs/plans/templates/` (`milestone.md`, `story.md`) so fase 2 produces reproducibly-shaped plans.
|
||||
- **Headless preview.** An in-app `<DraftPreview />` shipped in v1 alongside the `obsidian://`-link, so Cowork-only users can review without an Obsidian install.
|
||||
|
||||
The pipeline is considered working when a user can submit an idea on a product page, complete a grill-me dialogue inside the app, review the resulting draft in-app or in Obsidian, trigger fase 2, see PBI/Story/Task rows appear, and trigger fase 3 from the existing "Voer uit"-knop without any manual file editing in between.
|
||||
0
docs/app/getting-started/route-handlers.md
Normal file
0
docs/app/getting-started/route-handlers.md
Normal file
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