Scrum4Me/docs/plans/tweede-claude-agent-planning.md
Janpeter Visser 7e45bbdbc0
docs: AI-optimized docs restructure (Phases 1–8) (#61)
* docs(dialog-pattern): add generic entity-dialog spec

Introduceert docs/patterns/dialog.md als bron-of-truth voor elke
create/edit/detail-dialog in Scrum4Me, ongeacht het achterliggende
dataobject. Bevat 14 secties: uitgangspunten, stack, component-
architectuur, layout, validatie, drielaagse demo-policy, submission,
dialog-gedrag, theming, footer, triggers/URL-state, per-entiteit
profile-template, out-of-scope, en een verificatie-checklist.

Registreert het patroon in CLAUDE.md "Implementatiepatronen"-tabel
zodat Claude (en mensen) de spec verplicht raadplegen voor elke
nieuwe dialog.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(dialog-pattern): convert task spec + add pbi/story entity-profiles

Reduceert docs/scrum4me-task-dialog.md van 507 naar ~140 regels: alle
gedeelde regels verhuisd naar docs/patterns/dialog.md, dit document
bevat nu alleen Task-specifieke velden, URL-pattern, status-veld,
server actions, triggers en bewuste out-of-scope-keuzes.

Voegt twee nieuwe entity-profielen toe voor bestaande dialogen:
- docs/scrum4me-pbi-dialog.md (PbiDialog: state-based, code+title-rij,
  PbiStatusSelect, geen delete in v1)
- docs/scrum4me-story-dialog.md (StoryDialog: state-based, header met
  status/priority badges, inline activity-log, demo-readonly-fallback,
  inline-delete-confirm i.p.v. AlertDialog)

Beide profielen documenteren expliciet de "Bekende gaps t.o.v.
generieke spec" zodat opvolgende PR's de afwijkingen kunnen
rechtzetten of bewust kunnen accorderen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Added pdevelopment docs

* docs(plans): add docs-restructure plan for AI-optimized lookup

Audit of existing 39 doc files (~10.700 lines) and a phased restructure
proposal aimed at minimising the tokens an AI agent has to read to find
the right reference. Captures resolved decisions on language (English),
ADR template (Nygard default with MADR escape-hatch), index generator
(node script), and folder taxonomy. Proposal status — fase 1 to follow.

* docs(adr): add ADR scaffolding (templates, README, meta-ADR)

Set up docs/adr/ as the canonical home for architecture decisions:

- templates/nygard.md — default four-section format (Status, Context,
  Decision, Consequences) for one-way-door decisions.
- templates/madr.md — MADR v4 with YAML front-matter and explicit
  Considered Options for decisions where rejected alternatives matter.
- README.md — naming convention (NNNN-kebab-case), template-selection
  guidance (Nygard default; MADR for auth, queue mechanics, agent
  integration), status lifecycle, and ADR roster.
- 0000-record-architecture-decisions.md — meta-ADR establishing the
  practice itself, in Nygard format.

Backfilling existing implicit decisions (base-ui-over-radix, float
sort_order, demo-user three-layer policy, etc.) is fase 6 of the
docs-restructure plan.

* feat(docs): add docs index generator + initial INDEX.md

scripts/generate-docs-index.mjs walks docs/**/*.md, parses YAML
front-matter (or first H1 fallback) and a Nygard-style ## Status
section, then writes docs/INDEX.md with grouped tables for ADRs,
Specs, Plans (with archive subsection), Patterns, and Other.

Pure Node 20 (no external deps); idempotent — running it twice
produces byte-identical output. Excludes adr/templates/, the ADR
README, INDEX.md itself, and any *_*.md sidecar file.

Wire-up:
- package.json: docs:index → node scripts/generate-docs-index.mjs

Initial run indexed 35 docs across the existing structure; the
generated INDEX.md is committed so the table is reviewable in the
PR before hooking generation into a pre-commit step.

* chore: ignore Obsidian vault and personal sidecar files

Add .obsidian/ (Obsidian vault config) and _*.md (personal sidecar
notes) to .gitignore so the docs/ tree can serve as canonical source
of truth while still being usable as an Obsidian vault for personal
authoring. The docs index generator already excludes the same _*.md
pattern from INDEX.md.

* docs(plans): add PBI bulk-create spec for docs-restructure

Machine-parseable spec for an executor that calls the scrum4me MCP
(create_pbi → create_story → create_task) to seed the docs-restructure
work into the DB.

- Section 1 (Context) is the PBI description; serves as task-context
  via mcp__scrum4me__get_claude_context.
- Section 2 lists the 6 resolved decisions (English, MD3+styling
  merged, solo-paneel merged, .Plans archived, Nygard ADR default,
  node index script).
- Section 3 records what already shipped on this branch so the
  executor doesn't duplicate the ADR scaffolding or index generator.
- Section 4 carries the structured YAML graph: 1 PBI, 8 stories
  (one per phase), 39 tasks. product_id is REPLACE_ME — fill before
  running.
- YAML validated with PyYAML; field schema sanity-checked.

* docs(junk-cleanup): remove stub patterns/test.md

* docs(junk-cleanup): archive .Plans/ to docs/plans/archive/

* docs(front-matter): add YAML front-matter to docs/ root

* docs(front-matter): add YAML front-matter to patterns/

* docs(front-matter): add YAML front-matter to plans + agent files

* docs(index): regenerate INDEX.md after front-matter pass

* docs(naming): drop scrum4me- prefix from doc filenames

* docs(naming): lowercase API.md and MD3 filenames

* docs(naming): rename plan file to kebab-case ASCII

* docs(naming): rename middleware.md to proxy.md (next 16)

* docs(naming): polish CLAUDE.md doc-index after renames

* docs(taxonomy): scaffold topical folders under docs/

* docs(taxonomy): move spec files into docs/specs/

* docs(taxonomy): move design/api/qa/backlog/assets into folders

* docs(taxonomy): move agent-instruction-audit into decisions/

* docs(split): break architecture.md into 6 topical files

* docs(split): merge solo-paneel-spec into specs/functional.md

* docs(split): merge md3-color-scheme into design/styling

* docs(trim): extract branch/commit rules into runbook

* docs(trim): extract MCP integration into runbook

* docs(adr): add 0001-base-ui-over-radix

* docs(adr): add 0002-float-sort-order

* docs(adr): add 0003-one-branch-per-milestone

* docs(adr): add 0004-status-enum-mapping

* docs(adr): add 0005-iron-session-over-nextauth

* docs(adr): add 0006-demo-user-three-layer-policy

* docs(adr): add 0007-claude-question-channel-design

* docs(adr): add 0008-agent-instructions-in-claude-md + update README index

* docs(index): regenerate after ADR 0001-0008

* docs(glossary): add docs/glossary.md

* chore(docs): regenerate INDEX.md in pre-commit hook

* docs(readme): link INDEX + glossary + agent instructions

* feat(docs): add doc-link checker script

* chore(docs): wire docs:check-links and docs npm scripts

* ci(docs): block merge on broken doc links

* docs(links): fix broken cross-references after restructure

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 03:21:59 +02:00

18 KiB

title status audience language last_updated applies_to
Tweede Claude Agent — Planning Agent proposal
maintainer
contributor
nl 2026-05-03

Plan: Tweede Claude Agent — Planning Agent (PBI/Story → children)

Eerder goedgekeurd plan in deze file: Scrum4Me v1.0 Release (mobile shell + sprint-snapshots + release-discipline). Beschikbaar in chat-history; te verhuizen naar docs/plans/v1-release.md op een later moment. Dit nieuwe plan vervangt de plan-file inhoudelijk niet — het v1.0-werk blijft van kracht parallel hieraan.


Context

Wat de gebruiker wil: twee gespecialiseerde Claude-agents, elk met eigen context.

Agent Doel Context Status
Implementation Agent Codeert één task af → branch + PR Code-repo (filesystem), implementation_plan, story, pbi, sprint, repo_url Live (M13 / ST-1111)
Planning Agent (nieuw) Genereert children: PBI→stories of story→tasks (incl. implementation_plan per task) Specs + architectuur + patterns (filesystem in Scrum4Me-checkout), parent-record, bestaande children Te bouwen

Wat al staat (hergebruikbaar):

  • ClaudeJob-model met state-machine QUEUED→CLAIMED→RUNNING→DONE/FAILED, CANCELLED-pad, stale-cleanup >30min
  • ClaudeWorker-model voor presence-heartbeat
  • SSE-pijplijn op /api/realtime/solo met payload-routing op user_id + product_id
  • MCP-tools wait_for_job, update_job_status, get_claude_context, create_pbi, create_story, create_task, update_task_plan, log_implementation
  • Idempotency-pattern: max 1 actieve job per resource

Vastgelegde keuzes (uit AskUserQuestion-sessies):

  1. Agent kan beide niveaus: PBI→stories én story→tasks (één agent, twee modi via target_type)
  2. Output: items direct in DB aanmaken via bestaande create_*-tools — geen review-stap in v1
  3. Context: lokaal draaien + filesystem-toegang tot Scrum4Me-checkout (zoals impl-agent al doet)
  4. Rol-scheiding: ClaudeJob.kind enum (IMPLEMENTATION | PLANNING) — één table, polymorf
  5. Bestaande children: aanvullen — agent leest bestaande titels en voegt alleen ontbrekende toe
  6. Live feedback: stille SSE + status-pill op de PBI/Story-card; geen aparte modal
  7. MCP-shape: bestaande wait_for_job uitbreiden met accept_kinds: string[]-arg, default ['IMPLEMENTATION'] (backwards-compat)

Approach (8 stappen)

Stap 1 — Schema-uitbreiding

prisma/schema.prisma:

enum ClaudeJobKind {
  IMPLEMENTATION
  PLANNING
}

enum PlanningTargetType {
  PBI
  STORY
}

enum ApiTokenKind {
  IMPLEMENTATION
  PLANNING
  // beide kinds simultaan = afzonderlijke tokens; eenvoudiger dan multi-kind-flag
}

model ClaudeJob {
  // ... bestaande velden ...
  kind                ClaudeJobKind   @default(IMPLEMENTATION)
  task_id             String?         // wordt nullable — planning-jobs hebben geen task
  task                Task?           @relation(fields: [task_id], references: [id], onDelete: Cascade)
  planning_target_type PlanningTargetType?
  planning_target_id   String?

  @@index([kind, status])
  @@index([planning_target_type, planning_target_id, status])
}

model ApiToken {
  // ... bestaande velden ...
  kind  ApiTokenKind  @default(IMPLEMENTATION)
}

model ClaudeWorker {
  // ... bestaande velden ...
  // accepted_kinds wordt afgeleid uit token.kind (geen extra kolom nodig)
}

Constraint via DB-check (CHECK constraint of app-level): een ClaudeJob heeft óf task_id (kind=IMPLEMENTATION) óf planning_target_* (kind=PLANNING). Nooit beide leeg, nooit beide gevuld.

Migratie: alle bestaande rijen krijgen kind=IMPLEMENTATION + apiToken.kind=IMPLEMENTATION als default. Backwards-compatible.

Bestand: prisma/migrations/<date>_planning_job_kind/migration.sql

Stap 2 — Status-mappers + Zod-schemas

  • lib/claude-job-status.ts — voeg kind toe aan API-shape (lowercase: implementation | planning)
  • lib/schemas/claude-job.ts (NEW of MODIFY) — discriminated union op kind
  • lib/schemas/planning-target.ts (NEW) — { type: 'PBI'|'STORY', id: string } validator

Stap 3 — Server actions

actions/claude-jobs.ts uitbreiden:

export async function enqueuePlanningJobAction(input: {
  productId: string
  target: { type: 'PBI' | 'STORY', id: string }
}): Promise<EnqueueResult>

Logica:

  1. Auth-scope-check (productAccessFilter) — target moet binnen product zitten
  2. Demo-block (session.isDemo → 403)
  3. Idempotency: weiger als er al een PLANNING-job actief is voor dit (target_type, target_id)
  4. Insert ClaudeJob met kind=PLANNING, task_id=null, planning_target_* ingevuld
  5. pg_notify('scrum4me_changes', { type: 'claude_job_enqueued', kind: 'planning', ... })

cancelClaudeJobAction (bestaand) blijft werken — accepteert nu ook PLANNING-jobs (zelfde state-machine).

Stap 4 — SSE-routing

app/api/realtime/solo/route.ts:

  • Bestaande claude_job_*-events krijgen kind in payload
  • Bij connect: claude_jobs_initial-event bevat ook actieve PLANNING-jobs van vandaag
  • Filter blijft user_id + product_id — geen extra topic nodig

Stap 5 — UI: triggers + status-pills

Trigger in beide dialog-profielen (geprofileerd in PR #45):

Locatie Knop-label Target
components/backlog/story-dialog.tsx (edit-mode) 🤖 Genereer taken met Claude { type: 'STORY', id: story.id }
components/backlog/pbi-dialog.tsx (edit-mode) 🤖 Genereer stories met Claude { type: 'PBI', id: pbi.id }

Knop-gedrag:

  • <DemoTooltip show={isDemo}> rond knop (laag 3 demo-policy)
  • disabled als er al een PLANNING-job actief is voor deze target (live via SSE-store)
  • Tooltip bij disabled: "Plan-job al gestart — wachten op resultaat"
  • Klik → enqueuePlanningJobAction → toast "Plan gestart" → dialog blijft open zodat user resultaat ziet binnenkomen

Status-pill component (NEW):

components/shared/planning-job-pill.tsx — kleine badge die de status van een lopende PLANNING-job toont:

  • QUEUED — grijs, "In wachtrij"
  • CLAIMED / RUNNING — blauw met spinner, "Plan wordt gegenereerd…"
  • DONE — groen, fade-out na 5s
  • FAILED — rood, klikbaar voor error-detail
  • CANCELLED — niet getoond (verwijdert pill)

Plaatsing:

  • Op PbiList-card naast PBI-titel (rechts)
  • Op StoryPanel-card naast story-titel (rechts)
  • In PbiDialog / StoryDialog-header (edit-mode) als large variant

Live updates: bestaande useClaudeJobsStore (Zustand, populated uit SSE) — alleen kind toevoegen aan filter-helpers.

Stap 6 — MCP-tools (mcp repo, aparte PR)

Wijziging 1 — bestaande tool uitbreiden:

// wait_for_job tool input schema
{
  accept_kinds?: ('IMPLEMENTATION' | 'PLANNING')[]  // default: ['IMPLEMENTATION']
  wait_seconds?: number                             // bestaand
}

Server-side: WHERE kind = ANY($1) AND status = 'QUEUED' in de FOR UPDATE SKIP LOCKED-query. Token-kind moet ook compatibel zijn (token.kind IN accept_kinds-overlap).

Response-shape voegt kind toe; voor PLANNING-jobs vervangt task door planning_target met embedded record:

{
  job_id: string
  kind: 'IMPLEMENTATION' | 'PLANNING'
  product: { id, name, repo_url, ... }
  // IMPL-only:
  task?: { ..., implementation_plan, story, pbi, sprint }
  // PLANNING-only:
  planning_target?: {
    type: 'PBI' | 'STORY'
    pbi?: { id, code, title, description, priority, status, existing_stories: [{ id, code, title, priority }] }
    story?: { id, code, title, description, acceptance_criteria, priority, status, pbi: {...}, existing_tasks: [{ id, title, priority, status }] }
  }
}

Wijziging 2 — nieuwe MCP-tool:

get_planning_context(target_type, target_id) — losstaande lookup voor agent die handmatig een planning wil starten zonder job-claim. Optioneel; wait_for_job retourneert dezelfde data al.

Geen nieuwe write-tools nodig: bestaande create_story + create_task + update_task_plan werken al.

Schema-sync: vendor/scrum4me submodule update na Scrum4Me-PR merge.

Stap 7 — Agent-prompt + lokale Claude-command

In de Scrum4Me-checkout (of in een gedeelde plek voor agent-prompts) twee Claude Code commands:

/implement-next-story — bestaand, gebruikt wait_for_job({ accept_kinds: ['IMPLEMENTATION'] })

/generate-plan — nieuw:

Korte prompt-flow:

  1. wait_for_job({ accept_kinds: ['PLANNING'], wait_seconds: 600 }) — claim
  2. Lees planning_target uit response (PBI of STORY) + existing_*
  3. Lees lokale docs uit Scrum4Me-checkout:
    • docs/specs/functional.md (functioneel kader)
    • docs/architecture.md (technisch kader)
    • docs/patterns/*.md (relevante patterns op basis van target-titel/-beschrijving)
    • docs/design/styling.md als target UI-werk betreft
  4. Bedenk children:
    • Voor STORY-target: 3-7 taken met titel, korte beschrijving, implementation_plan (verwijst naar relevante patterns + bestanden), priority
    • Voor PBI-target: 2-5 stories met titel, beschrijving in user-story-format, acceptance_criteria, priority
  5. Filter ontbrekende items: skip wat overlapt met existing_* (titel-match)
  6. Voor elk: create_task of create_story via MCP
  7. update_job_status({ status: 'DONE', summary: 'Aangemaakt: 4 taken / 0 overgeslagen (titel-overlap)' })

Bij failure: update_job_status({ status: 'FAILED', error }) + toast voor user.

Mens-rolverdeling: twee Claude Code-sessies tegelijk draaien (één met /implement-next-story running, één met /generate-plan running). Beide claimen alleen hun eigen kind via accept_kinds. Dezelfde gebruiker-token of twee aparte (afhankelijk van hoe je workers wilt scheiden).

Stap 8 — Tests

Test Locatie
enqueuePlanningJobAction — auth, demo, idempotency, scope __tests__/actions/claude-jobs-planning.test.ts
Schema-mapper voor kind + planning_target_* __tests__/lib/claude-job-status.test.ts
SSE-event format met kind __tests__/api/realtime-solo-planning.test.ts
Status-pill rendering per status __tests__/components/shared/planning-job-pill.test.tsx
Knop disabled-state in StoryDialog/PbiDialog bij actieve job __tests__/components/backlog/dialog-planning-button.test.tsx

MCP-tools testen in mcp repo (aparte PR).


Critical files

Scrum4Me-repo

File Action Reden
prisma/schema.prisma MODIFY ClaudeJobKind, PlanningTargetType, ApiTokenKind enums + nullable task_id + planning_target_* velden
prisma/migrations/<date>_planning_job_kind/ NEW Migratie + check-constraint
lib/claude-job-status.ts MODIFY kind in API-shape
lib/schemas/claude-job.ts NEW/MODIFY Discriminated union op kind
lib/schemas/planning-target.ts NEW Target-validator
actions/claude-jobs.ts MODIFY enqueuePlanningJobAction toevoegen, idempotency uitbreiden
app/api/realtime/solo/route.ts MODIFY kind in payload, initial-state ook PLANNING-jobs
stores/claude-jobs-store.ts (of vergelijkbaar) MODIFY kind-filter helpers
components/backlog/story-dialog.tsx MODIFY "Genereer taken"-knop + status-pill in header
components/backlog/pbi-dialog.tsx MODIFY "Genereer stories"-knop + status-pill in header
components/backlog/story-panel.tsx MODIFY Status-pill op story-card
components/backlog/pbi-list.tsx MODIFY Status-pill op pbi-card
components/shared/planning-job-pill.tsx NEW Generic pill-component
docs/patterns/claude-agent-roles.md NEW Pattern-doc: één table, kind-enum, accept_kinds-arg, lokale agent-prompts
docs/architecture.md MODIFY Sectie "Claude Agents" uitbreiden — twee rollen, schema, queue, prompts
docs/specs/dialogs/pbi.md MODIFY Sectie "Speciale gedragingen → Planning-trigger" toevoegen
docs/specs/dialogs/story.md MODIFY Idem
docs/specs/dialogs/task.md MODIFY Vermelden dat tasks ook door planning-agent kunnen ontstaan
__tests__/actions/claude-jobs-planning.test.ts NEW
__tests__/lib/claude-job-status.test.ts MODIFY kind-mapping testen
__tests__/api/realtime-solo-planning.test.ts NEW
__tests__/components/shared/planning-job-pill.test.tsx NEW
__tests__/components/backlog/dialog-planning-button.test.tsx NEW

mcp repo (aparte PR, na Scrum4Me-merge)

File Action
src/tools/wait_for_job.ts MODIFY — accept_kinds-arg + polymorf response
src/tools/get_planning_context.ts NEW (optioneel, helper)
src/types/job.ts MODIFY — kind + planning_target
src/prompts/generate-plan.md NEW — Claude command-prompt
vendor/scrum4me/ (submodule) UPDATE — na Scrum4Me-merge

Volgorde van uitvoering

  1. Schema + migratie + status-mapper (Stap 1+2) — eigen commit, geen UI-impact
  2. Server action enqueuePlanningJobAction + tests (Stap 3) — werkt headless via curl/test
  3. SSE-payload uitbreiden + claude-jobs-store (Stap 4) — backend pipe klaar
  4. Status-pill component + tests (Stap 5a) — losstaande primitive
  5. Trigger-knoppen in StoryDialog + PbiDialog (Stap 5b) — UI-trigger werkt, agent nog niet
  6. Pause — verifieer end-to-end met handmatig insert in claude_jobs (kind=PLANNING) of via mock-MCP-call
  7. MCP-PR in mcp repo (Stap 6) — wait_for_job uitbreiden, types updaten
  8. Lokaal /generate-plan-command schrijven + testen (Stap 7) — agent claimt, leest, schrijft
  9. End-to-end test (Stap 8) — story → klik knop → agent rendert taken → SSE → live in TaskPanel
  10. Docs-PR — pattern-doc claude-agent-roles.md, architecture-update, dialog-profielen aanvullen

Branch-naming: feat/M15-planning-agent (Scrum4Me) + feat/planning-agent (mcp).

Conform CLAUDE.md "branch-per-milestone": commits accumuleren lokaal, pushen pas na gebruikerstest.


Verification

  1. npm run lint && npm test && npm run build groen
  2. Schema-migratie: bestaande claude_jobs-rijen krijgen kind=IMPLEMENTATION; check-constraint blokkeert ongeldige combinaties
  3. Idempotency: twee keer klikken op "Genereer taken" → tweede klik geeft toast "Plan-job al gestart", knop disabled
  4. Demo-block: demo-user ziet knop disabled met DemoTooltip; server action returnt 403 als je 'm toch aanroept
  5. SSE live: trigger planning-job → status-pill verschijnt op story-card binnen 1s zonder refresh
  6. End-to-end: lokale /generate-plan agent claimt job, leest target via wait_for_job, leest 3-4 docs uit Scrum4Me-checkout, maakt 3-5 taken via create_task, status DONE → taken zichtbaar in TaskPanel zonder refresh
  7. Cancel-flow: gebruiker kan vanuit dialog een running PLANNING-job cancellen → status-pill verdwijnt, agent ziet job-status CANCELLED bij volgende update_job_status
  8. Cross-kind isolation: een implementation-agent met accept_kinds=['IMPLEMENTATION'] (default) ziet PLANNING-jobs niet; idem omgekeerd
  9. Aanvullen-policy: trigger op story die al 2 taken heeft — agent voegt alleen ontbrekende toe (logged in summary: "Aangemaakt: 3 / Overgeslagen: 2 (titel-overlap)")
  10. Documentatie: docs/patterns/claude-agent-roles.md beschrijft beide agent-rollen, hun job-flow, en hoe je een derde agent zou toevoegen

Open punten (na approval expliciet maken)

  1. Token-strategie: krijgt elke agent-rol een eigen ApiToken (cleaner, twee credentials), of bestaat er één multi-kind-token per user? Voorstel: aparte tokens — één per kind. Gebruiker beheert ze in Settings.
  2. Concurrency in 1 worker: mag accept_kinds: ['IMPLEMENTATION', 'PLANNING'] (één worker pakt allebei)? Voorstel: ja, technisch toegestaan, maar in v1 raad je af want context-pollution. Documenteren als "kan, maar gebruik gescheiden processen".
  3. Doc-selectie: hoe bepaalt de agent welke docs/patterns/*.md relevant zijn? Voorstel: lees docs/patterns/-index op + match op keywords uit target-titel/-beschrijving. Geen embeddings in v1.
  4. Hoeveel children per run? Voorstel: hard cap 8 in de prompt (anders gaat 'ie speculeren). Gebruiker kan opnieuw klikken voor meer.
  5. Editable plan-text: wanneer agent implementation_plan invult op een nieuwe task, kan de gebruiker die later via TaskDialog editen — dat werkt al, geen extra werk.
  6. Failure-recovery: wat als agent halverwege crasht? Stale-cleanup >30min werkt al; partial-children blijven aangemaakt. Voorstel: accepteer partial — gebruiker kan opnieuw triggeren, aanvullen-policy filtert duplicaten.

Out of scope (v1 van deze feature)

  • Diff-review-flow (vervangen-modus uit eerdere AskUserQuestion)
  • Live streaming-output van agent (alleen status-events, geen tekst-stream)
  • Meerdere parallele PLANNING-jobs op dezelfde target (idempotency blokkeert)
  • Custom prompts per product (vaste prompt-template /generate-plan)
  • Embeddings / vector-search over docs (agent leest plain files)
  • Cross-user planning (agent werkt altijd binnen eigen product-scope)
  • Auto-trigger (bv. "elke nieuwe lege story krijgt automatisch een plan-job") — handmatige trigger blijft regel
  • Agent-tot-agent-orkestratie (planning-agent kan geen impl-agent triggeren) — gebruiker blijft in the loop
  • Planning op product-niveau (PBI's genereren) — pas zinvol als product-spec-input bestaat
  • Planning-agent UI in Solo Paneel (Solo blijft impl-only) — triggers zitten in backlog-dialogs

Migratie-pad voor toekomstige derde agent (referentie)

Als er ooit een derde agent komt (bv. review-agent die een PR review't):

  1. ClaudeJobKind enum uitbreiden met REVIEW
  2. ApiTokenKind enum uitbreiden met REVIEW
  3. enqueueReviewJobAction aanmaken (kopieert pattern van planning)
  4. wait_for_job accepteert nieuwe kind automatisch via accept_kinds
  5. Pattern-doc claude-agent-roles.md uitbreiden met de derde rol

Geen schema-revolutie nodig — kind-enum is het uitbreidingspunt.