Scrum4Me/docs/plans/M12-ideas.md
Madhura68 300e426a4e schema: add Idea + IdeaLog models, extend ClaudeJob/Question for ideas (M12 T-491)
- new enums IdeaStatus, ClaudeJobKind, IdeaLogType
- new models Idea (with @@unique([user_id, code]) + pbi_id @unique) and IdeaLog
- User.idea_code_counter Int @default(0) for IDEA-{nnn} code generation
- ClaudeJob.task_id nullable; new idea_id + kind fields + index
- ClaudeQuestion.story_id nullable; new idea_id field + index
- existing call sites narrowed to story-questions / task-jobs (idea-paths come in T-502+)
- includes the M12 plan doc copied from /Users/janpetervisser/.claude/plans

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 19:25:07 +02:00

15 KiB

title status audience language
M12 — Idea entity + Grill/Plan Claude jobs planned implementation nl

M12 — Idea entity + Grill/Plan Claude jobs

Context

Scrum4Me ondersteunt Todo als lichtgewicht voorstel-laag, en kan dat handmatig promoveren naar PBI/Story. Dat slaat het denkproces niet vast: waarom werd iets een PBI, welke alternatieven zijn afgewogen, welke randvoorwaarden waren er.

Doel: een nieuw concept Idee dat:

  • werkt als een Todo (top-level lijst, privé per gebruiker), met een Grill Me- en Make Plan-knop;
  • via de bestaande Claude-job/worker-infrastructuur een gestructureerd plan oplevert;
  • het hele planningsproces vastlegt (Q&A, beslissingen, grill-md, plan-md, link naar PBI);
  • na goedkeuring deterministisch materialiseert tot PBI + stories + taken (incl. implementation_plan).

Vastgelegde keuzes (uit grill-sessie)

  1. UI-plek: top-level /ideas, naast /todos.
  2. Auth-scope: strikt user_id-only (privé, ook ná PLANNED). Geen productAccessFilter op idea-acties; geen pbi.idea_id-veld nodig.
  3. Product-binding: een idee mag bestaan zonder product, maar Grill Me én Make Plan vereisen een product met repo_url (de worker leest sources/docs uit de repo). claude_jobs.product_id blijft NOT NULL.
  4. Executie-model: bestaand worker-model. ClaudeJob{kind:IDEA_*} QUEUED → lokale Claude-CLI claimt via wait_for_job. Knoppen zijn disabled als connectedWorkers === 0 (exact zoals solo/task-detail-dialog.tsx).
  5. Skill-afhankelijkheid: embedded prompts in lib/idea-prompts/{grill,make-plan}.md; meegestuurd in payload. Geen externe anthropic-skills:grill-me-plugin-vereiste op de worker.
  6. Make-Plan flow: preview-en-bevestigen. Job produceert Idea.plan_md, status → PLAN_READY. Aparte knop "Materialiseer plan" parseert md → entiteiten in één Prisma-transactie, status → PLANNED.
  7. Plan-md formaat: YAML-frontmatter (structuur) + markdown-body (overwegingen, alternatieven, vrije reasoning).
  8. Make-Plan-job: single-pass (geen ask_user_question). Twijfels → terug naar grill (append-context).
  9. Backward transitions:
    • Re-grill vanuit GRILLED/PLAN_READY: nieuwe IDEA_GRILL-job met append-context (oude grill_md als input); oude versie naar IdeaLog{type:GRILL_RESULT} als history.
    • Re-plan vanuit PLAN_READY: idem voor plan_md.
    • PBI-verwijdering vanuit PLANNED: expliciete user-actie "Re-link plan" (geen DB-trigger). Zet pbi_id=null, status PLAN_READY.
    • Failed grill/plan: dedicated states GRILL_FAILED / PLAN_FAILED (zichtbaar voor user), niet stilzwijgend resetten.
  10. Logging-model: IdeaLog smal (DECISION | NOTE | GRILL_RESULT | PLAN_RESULT | STATUS_CHANGE | JOB_EVENT). Q&A blijft uitsluitend in claude_questions. Timeline-tab in UI doet UNION ALL over beide bronnen.
  11. Opslag md-bestanden: alleen DB (Idea.grill_md, Idea.plan_md). Geen auto-commit naar repo (zou strict-private auth-keuze ondergraven). UI biedt "Download .md".
  12. Editability: beide md's bewerkbaar door user in hun ready-states (GRILLED voor grill_md, PLAN_READY voor plan_md). Bij PLANNED: read-only. Yaml-frontmatter wordt zod-gevalideerd-on-save voor plan_md.
  13. Promotie vanuit Todo: nieuwe promoteTodoToIdeaAction (Todo → DRAFT-Idea + Todo wordt archived=true). Bestaande Todo→PBI/Story-acties blijven onaangetast.
  14. Demo-policy (3-laag, zoals Todo): create/edit/archive mag; Grill / Make Plan / Materialiseer / promote-from-Todo zijn geblokkeerd (proxy.ts 403 + session.isDemo-guard + <DemoTooltip>).
  15. Idea-code: Idea.code = "IDEA-{nnn}", @@unique([user_id, code]), counter op User.idea_code_counter.
  16. Realtime-store: nieuwe stores/idea-store.ts. connectedWorkers direct selecten via useSoloStore(s => s.connectedWorkers) (lift naar shared store is opvolg-refactor).
  17. Sidebar: nieuwe entry Ideeën (Lightbulb-icon) direct boven Todo's.
  18. Q&A-expiry: 24h aanhouden (consistent met bestaand). Verlopen → re-grill (append-context).

State machine

                   ┌──── re-grill ────┐
                   ▼                  │
DRAFT ──Grill Me──▶ GRILLING ─done──▶ GRILLED ─Make Plan─▶ PLANNING ─done──▶ PLAN_READY ─Materialiseer─▶ PLANNED
                       │ fail                                  │ fail            │  ▲                       │
                       ▼                                       ▼                 │  │                       │
                   GRILL_FAILED                            PLAN_FAILED           └──┘ re-plan               │
                       │                                                                                    │
                       └────── retry/edit ──────────────────────────────────────── PBI verwijderd ──────────┘
                                                                                  + "Re-link plan"

archived: boolean is orthogonaal en kan vanuit elke status.

Datamodel

Nieuwe enums

enum IdeaStatus {
  DRAFT
  GRILLING
  GRILL_FAILED
  GRILLED
  PLANNING
  PLAN_FAILED
  PLAN_READY
  PLANNED
}

enum ClaudeJobKind {
  TASK_IMPLEMENTATION
  IDEA_GRILL
  IDEA_MAKE_PLAN
}

enum IdeaLogType {
  DECISION
  NOTE
  GRILL_RESULT
  PLAN_RESULT
  STATUS_CHANGE
  JOB_EVENT
}

User

  • Veld toevoegen: idea_code_counter Int @default(0).

Nieuwe tabel ideas

model Idea {
  id           String     @id @default(cuid())
  user         User       @relation(fields: [user_id], references: [id], onDelete: Cascade)
  user_id      String
  product      Product?   @relation(fields: [product_id], references: [id], onDelete: SetNull)
  product_id   String?
  code         String     @db.VarChar(30)
  title        String
  description  String?    @db.VarChar(4000)
  grill_md     String?    @db.Text
  plan_md      String?    @db.Text
  pbi          Pbi?       @relation(fields: [pbi_id], references: [id], onDelete: SetNull)
  pbi_id       String?    @unique
  status       IdeaStatus @default(DRAFT)
  archived     Boolean    @default(false)
  created_at   DateTime   @default(now())
  updated_at   DateTime   @updatedAt

  questions    ClaudeQuestion[]
  jobs         ClaudeJob[]
  logs         IdeaLog[]

  @@unique([user_id, code])
  @@index([user_id, archived, status])
  @@index([user_id, product_id])
  @@map("ideas")
}

Nieuwe tabel idea_logs

model IdeaLog {
  id         String       @id @default(cuid())
  idea       Idea         @relation(fields: [idea_id], references: [id], onDelete: Cascade)
  idea_id    String
  type       IdeaLogType
  content    String       @db.Text
  metadata   Json?
  created_at DateTime     @default(now())

  @@index([idea_id, created_at])
  @@map("idea_logs")
}

Aanpassingen claude_jobs

  • task_idnullable.
  • idea_id String? toegevoegd, FK → Idea, onDelete: Cascade.
  • kind ClaudeJobKind @default(TASK_IMPLEMENTATION) toegevoegd.
  • product_id blijft NOT NULL.
  • Raw-SQL check-constraint: (task_id IS NOT NULL) <> (idea_id IS NOT NULL).
  • Index: @@index([idea_id, status]).

Aanpassingen claude_questions

  • story_idnullable.
  • idea_id String? toegevoegd, FK → Idea, onDelete: Cascade.
  • Raw-SQL check-constraint: (story_id IS NOT NULL) <> (idea_id IS NOT NULL).
  • Index: @@index([idea_id, status]).
  • pg_notify-trigger payload uitbreiden met idea_id (nullable). SSE-filter laat idea-payloads alleen door naar idea.user_id === session.user_id.

Server-laag

Schemas + helpers

  • lib/schemas/idea.tsideaCreateSchema, ideaUpdateSchema, ideaPlanMdFrontmatterSchema.
  • lib/idea-status.ts — DB-enum ↔ API-string mapping.
  • lib/idea-plan-parser.ts — synchroon: parsePlanMd(md): ParsedPlan | ZodError. Gebruikt yaml-package + zod.
  • lib/idea-code.ts — atomair nextIdeaCode(userId) via Prisma-transactie.

Embedded prompts

  • lib/idea-prompts/grill.md — eigen scrum4me-versie. Instrueert: gebruik ask_user_question MCP, schrijf via update_idea_grill_md aan eind.
  • lib/idea-prompts/make-plan.md — strict yaml-frontmatter-format. Instrueert: lees grill_md, gebruik repo-files, geen vragen, eindig met update_idea_plan_md.

Server actions — actions/ideas.ts

Volg docs/patterns/server-action.md: auth → demo-check → zod → user-id-scope-check → write → revalidatePath.

  • createIdeaAction(input)nextIdeaCode(userId), status DRAFT.
  • updateIdeaAction(id, input) — alleen DRAFT|GRILL_FAILED|GRILLED|PLAN_FAILED|PLAN_READY.
  • archiveIdeaAction(id) / unarchiveIdeaAction(id).
  • deleteIdeaAction(id) — geweigerd als pbi_id gevuld.
  • updateGrillMdAction(id, md) — alleen in GRILLED|PLAN_READY. Logt IdeaLog{NOTE}.
  • updatePlanMdAction(id, md) — alleen in PLAN_READY. Eerst parsePlanMd(md); bij parse-fail → 422 met line-info.
  • startGrillJobAction(id) — vereist product met repo_url, connectedWorkers > 0. ClaudeJob{kind:IDEA_GRILL, idea_id, product_id, QUEUED}. Status → GRILLING. Demo: 403.
  • startMakePlanJobAction(id) — vereist GRILLED|PLAN_FAILED|PLAN_READY voor re-plan, product met repo, worker. Status → PLANNING. Demo: 403.
  • cancelIdeaJobAction(id) — actieve job CANCELLED, idea-status terug naar vorige.
  • materializeIdeaPlanAction(id)PLAN_READYparsePlanMd → Prisma-$transaction:
    1. Counters incrementeren (PBI/Story/Task).
    2. INSERT PBI + N stories + M tasks (incl. implementation_plan).
    3. UPDATE idea: pbi_id, status:PLANNED.
    4. INSERT IdeaLog{type:PLAN_RESULT, metadata}. Rollback bij ANY fail. Demo: 403.
  • relinkIdeaPlanAction(id) — alleen als status===PLANNED && pbi_id===null. Status → PLAN_READY.
  • downloadIdeaMdAction(id, kind: 'grill'|'plan') — server returnt md.

Promote van Todo → Idea

  • actions/todos.ts: nieuwe promoteTodoToIdeaAction(todoId) — auth + demo + scope. Maakt Idea (DRAFT) met title/description; zet Todo archived=true. Demo: 403.

REST-routes

  • app/api/ideas/route.ts (GET, POST) en app/api/ideas/[id]/route.ts (GET, PATCH).

proxy.ts (demo-laag)

  • 403 op POST/PATCH/DELETE /api/ideas* voor demo-token.
  • 403 op grill/make-plan/materialize-endpoints.

MCP-laag (scrum4me-mcp-repo)

Nieuwe tools

  • get_idea_context(idea_id){idea, product, repo_url, grill_md_so_far, open_questions, prompt_text}.
  • update_idea_grill_md(idea_id, markdown) — schrijft veld; status → GRILLED; logt IdeaLog{GRILL_RESULT}.
  • update_idea_plan_md(idea_id, markdown) — schrijft veld; parser draait server-side; status → PLAN_READY of PLAN_FAILED.
  • log_idea_decision(idea_id, type, content, metadata?) — types: DECISION | NOTE.

Uitbreiding bestaande tools

  • ask_user_question: contract uitbreiden — exact één van story_id of idea_id.
  • wait_for_job: response uitbreiden:
    • kind: 'TASK_IMPLEMENTATION' | 'IDEA_GRILL' | 'IDEA_MAKE_PLAN'
    • bij IDEA_*: idea, product, repo_url, prompt_text.
  • update_job_status: bij failed voor IDEA_*-jobs zet idea-status GRILL_FAILED / PLAN_FAILED.

Schema-drift

  • docs/runbooks/mcp-integration.md:62: schema-drift-watchdog moet groen zijn vóór merge. MCP-server-PR parallel.

Realtime-laag

  • app/api/realtime/notifications/route.ts — idea-questions alleen aan idea.user_id === session.user_id.
  • app/api/realtime/solo/route.tsJobPayload uitbreiden met kind en idea_id. Idea-jobs op user_id.
  • stores/idea-store.ts (nieuw). connectedWorkers direct uit useSoloStore.

UI-laag

Routing

  • app/(app)/ideas/page.tsx — top-level lijst.
  • app/(app)/ideas/[id]/page.tsx — detailpagina met tabs Idee · Grill · Plan · Timeline.
  • Sidebar-entry: Lightbulb, label "Ideeën", boven Todo's.

Componenten — components/ideas/

  • idea-list.tsx — TanStack Table; kolommen code/title/product/status/archived. Bulk-archive.
  • idea-row-actions.tsx — Grill Me / Make Plan / Materialiseer / Edit / Archive met disabled-rules.
  • idea-dialog.tsx + components/dialogs/idea-dialog.tsx (wrapper) volgens dialog-pattern.
  • idea-md-editor.tsx — markdown editor met yaml-validate voor plan_md.
  • idea-timeline.tsx — UNION-view IdeaLog + claude_questions.
  • idea-pbi-link-card.tsx — incl. "Re-link plan"-banner.
  • download-md-button.tsx.

Promote-from-Todo UI

  • components/todos/todo-list.tsx: extra menu-item "Promote naar Idee".

Profiel-doc

  • docs/specs/dialogs/idea.md — verplicht volgens dialog-pattern.

Te raken / aan te maken bestanden

Laag Bestand
Schema prisma/schema.prisma
Migratie prisma/migrations/<ts>_add_ideas/migration.sql
Schemas lib/schemas/idea.ts, lib/idea-status.ts, lib/idea-plan-parser.ts, lib/idea-code.ts
Prompts lib/idea-prompts/grill.md, lib/idea-prompts/make-plan.md
Actions actions/ideas.ts, uitbreiding actions/todos.ts
API app/api/ideas/route.ts, app/api/ideas/[id]/route.ts, proxy.ts
Realtime app/api/realtime/notifications/route.ts, app/api/realtime/solo/route.ts
Pages app/(app)/ideas/page.tsx, app/(app)/ideas/[id]/page.tsx
UI components/ideas/*.tsx, components/dialogs/idea-dialog.tsx, sidebar-update
Store stores/idea-store.ts
Docs docs/specs/dialogs/idea.md, docs/runbooks/mcp-integration.md, docs/backlog/index.md
MCP-server madhura68/scrum4me-mcp (parallel-PR)

Implementatievolgorde

  1. DB & migratie
  2. Lib + schemas + prompts
  3. Server actions + Todo-promote
  4. REST + proxy demo-laag
  5. Realtime SSE + idea-store
  6. MCP-server tools (extern repo, parallel)
  7. UI lijst + row-actions
  8. UI detail + dialog + tabs
  9. UI promote-from-Todo + sidebar-entry
  10. End-to-end smoke + docs

Verificatie

npm run lint && npm test && npm run build

End-to-end:

  1. npm run dev + lokale Claude-CLI met wait_for_job-loop.
  2. Maak idee, koppel aan product. Status DRAFT.
  3. Grill Me → vragen via answer-modal → update_idea_grill_mdGRILLED.
  4. Edit grill_md handmatig → IdeaLog{NOTE}.
  5. Make Plan → update_idea_plan_mdPLAN_READY.
  6. Yaml-fout in plan_md → save geblokkeerd.
  7. Materialiseer → PBI + stories + taken in transactie. idea.pbi_id gezet, PLANNED.
  8. PBI verwijderen → "Re-link plan"-banner → PLAN_READY.
  9. Demo-test: knoppen geblokkeerd via DemoTooltip + 403.
  10. Failure-test: kill worker → GRILL_FAILED/PLAN_FAILED.
  11. Promote-test: Todo → "Promote naar Idee" → DRAFT-Idea, Todo archived.

Open punten (niet-blokkerend)

  • Concrete copy-finetuning van prompt-md's tijdens implementatie.
  • Lift connectedWorkers naar gedeelde worker-presence-store (opvolg-refactor).
  • Optionele "Commit plan_md naar repo"-knop (buiten v1).