# CLAUDE.md — Scrum4Me Dit is het centrale instructiedocument voor Claude Code. Lees dit volledig voordat je iets bouwt. --- ## Wat is Scrum4Me? Een desktop-first fullstack webapplicatie voor solo developers en kleine Scrum Teams die meerdere softwareprojecten parallel beheren. De app organiseert werk hiërarchisch (product → PBI → story → taak), biedt gesplitste planningsschermen met drag-and-drop, en integreert met Claude Code via een REST API. --- ## Specificatiedocumenten Lees het relevante document voordat je aan een feature begint. Nooit gokken over requirements. | Document | Gebruik voor | |---|---| | `docs/scrum4me-functional-spec.md` | Acceptatiecriteria, randgevallen, user flows | | `docs/scrum4me-architecture.md` | Stack, datamodel, Prisma schema, Zustand stores | | `docs/scrum4me-backlog.md` | Welke task bouwen, volgorde, "done when"-criteria | | `docs/scrum4me-personas.md` | Lars (primair), Dina, Remi — gebruik bij UI-beslissingen | | `docs/scrum4me-product-backlog.md` | Historische domein-backlog (referentie); seed wordt sinds ST-004 gegenereerd uit `scrum4me-backlog.md` via `prisma/seed-data/parse-backlog.ts` | | `docs/API.md` | REST-API contract voor Claude Code — endpoints, status-enums, foutcodes, voorbeeld-curls | | `docs/scrum4me-styling.md` | **Lees dit voor elk component** — MD3-kleuren, shadcn patronen | | `docs/agent-instruction-audit.md` | Waarom de agent-instructies zijn aangescherpt; checklist voor toekomstige wijzigingen | | `docs/plans/-*.md` | Implementatieplan per milestone — Bestanden, Stappen, Aandachtspunten, Verificatie. Lees vóór je aan een ST begint. Milestone-key matcht backlog-header (`M9`, `M3.5`, `PBI-9`, …). | | [`madhura68/scrum4me-mcp`](https://github.com/madhura68/scrum4me-mcp) | MCP-server repo: native tools voor Claude Code, schema-sync via git submodule | --- ## Waar te beginnen Volg de backlog strikt op volgorde. Start bij **ST-001**. Sla geen milestone over. ``` M0 (ST-001–008) → M1 (ST-101–110) → M2 (ST-201–210) → M3 (ST-301–312) → M4 (ST-401–410) → M5 (ST-501–506) → M6 (ST-601–612) ``` Werken aan een task kan via twee tracks. Track A heeft de voorkeur als je in Claude Code zit; Track B is voor Codex of omgevingen zonder MCP. ### Track A — via Claude Code MCP (aanbevolen) 1. Roep `mcp__scrum4me__implement_next_story` aan met `product_id` (gebruik `mcp__scrum4me__list_products` als je het id niet weet) 2. De prompt orkestreert: `get_claude_context` → `log_implementation` → per task `update_task_status(in_progress)` → bouw → `update_task_status(done)` → `log_test_result` → `log_commit` 3. Bouw de tasks in volgorde van `sort_order`; lees per task de relevante pattern-doc en styling 4. Verifieer: `npm run lint && npm test && npm run build` 5. Commit per laag (zie Commit Strategy) ### Track B — manueel (Codex of zonder MCP) 1. Lees de task in `scrum4me-backlog.md` 2. Zoek de bijbehorende feature-spec in `scrum4me-functional-spec.md` 3. Lees het relevante patroon in `docs/patterns/` en styling in `docs/scrum4me-styling.md` als dat van toepassing is 4. Bouw — test — verifieer de "Done when"-criteria 5. Vraag of de code correct is 6. Commit (zie Commit Strategy hieronder) 7. Vraag of de volgende taak gedaan moet worden --- ## Tech stack ``` Next.js 16 (App Router) + React 19 TypeScript strict Tailwind CSS + shadcn/ui MD3 kleurensysteem via app/styles/theme.css Zustand (client state) dnd-kit (drag-and-drop) Prisma v7 + PostgreSQL (Neon) iron-session (auth cookies) bcryptjs + Zod + Sonner Sharp (avatarverwerking) Vercel Analytics (@vercel/analytics/next) ``` > ⚠️ **Stylingregel:** Gebruik **nooit** `bg-blue-500` of willekeurige Tailwind-kleuren. > Gebruik altijd semantische MD3-tokens: `bg-primary`, `bg-status-done`, `bg-priority-critical`. > Zie `scrum4me-styling.md` voor alle patronen. > ⚠️ **Next.js-versie:** Lees `node_modules/next/dist/docs/` bij twijfel — API's kunnen afwijken van trainingsdata. --- ## UI Library Conventions - Dit project gebruikt **`@base-ui/react`**, *niet* Radix UI — ondanks dat shadcn-componenten visueel-identiek zijn - Composition gebeurt via de **`render`-prop**, niet via Radix's `asChild`: - ✅ `}>...` - ❌ `` — geeft TS-errors - Vóór je een nieuwe shadcn-/UI-primitive gebruikt: grep eerst de codebase voor bestaand gebruik en volg dat patroon (`grep -rn "PrimitiveTrigger" components/`) - shadcn-componenten in `components/ui/` zijn dunne wrappers rond `@base-ui/react`-primitives; lees die voor de exacte prop-API --- ## Implementatiepatronen Lees het relevante patroon vóór je begint. Nooit uit het hoofd schrijven. | Patroon | Bestand | |---|---| | iron-session (auth cookies) | `docs/patterns/iron-session.md` | | Prisma Client singleton | `docs/patterns/prisma-client.md` | | Server Action (met auth + Zod) | `docs/patterns/server-action.md` | | Route Handler (REST API) | `docs/patterns/route-handler.md` | | Zustand optimistische update + rollback | `docs/patterns/zustand-optimistic.md` | | Float sort_order drag-and-drop | `docs/patterns/sort-order.md` | | Middleware (route protection) | `docs/patterns/middleware.md` | | QR-pairing (unauth-SSE + pre-auth cookie) | `docs/patterns/qr-login.md` | | Bidirectionele async-comms MCP-agent ↔ user | `docs/patterns/claude-question-channel.md` | | **Entity Dialog (verplicht voor élke create/edit/detail-dialog)** | `docs/patterns/dialog.md` — bron-of-truth; per entiteit één profile-doc (bv. `docs/scrum4me-task-dialog.md`) | | **Story met UI-component (verplicht 3-task-patroon: Helper / Component / Integration)** | `docs/patterns/story-with-ui-component.md` — elke story met een `*-component.tsx` vereist een afsluitende Integration-task die de component in `page.tsx` wirt | | Status-enum mapping (DB ↔ API) | `lib/task-status.ts` | | Client/server module-boundary | `*-server.ts` bevat DB-calls of node-only deps; `*.ts` is pure (client-safe). Nooit `import { ... } from '@/lib/foo-server'` in een client-component, anders krijg je `Module not found: 'dns'`/`'pg'`-style runtime fouten | --- ## Env vars ```bash DATABASE_URL="" # postgresql://... (verplicht) DIRECT_URL="" # postgresql://... — pooler-bypass voor LISTEN/NOTIFY (Neon/cloud) SESSION_SECRET="" # min 32 chars; openssl rand -base64 32 CRON_SECRET="" # M11 — Bearer-secret voor /api/cron/*; verplicht in productie, optioneel lokaal (genereer met openssl rand -base64 32) ``` Volledige Zod-schema in `lib/env.ts`. `.env.example` is de canonieke lijst voor nieuwe checkouts. --- ## Conventies - **Branches:** `feat/ST-001-scaffolding` - **Server Actions:** altijd in `actions/[domein].ts`, nooit inline in page.tsx - **Validatie:** altijd Zod, nooit handmatige checks - **Toegangsmodel:** product-scoped resources gebruiken `productAccessFilter(userId)` tenzij het expliciet een eigenaarsactie is - **Bulk-ID's:** reorder- en beslissingsacties valideren dat alle meegegeven IDs binnen dezelfde parent-scope vallen voordat er geschreven wordt - **Foreign keys:** denormalized keys zoals `story.product_id` worden afgeleid uit de database-parent (`pbi.product_id`), nooit uit client-input - **Demo-check (drie lagen — ST-1110):** write-acties zijn drielaags afgedekt: (1) middleware-guard in `proxy.ts` blokkeert non-GET op `/api/*` voor demo; (2) elke Server Action / Route Handler controleert `session.isDemo` vóór schrijven; (3) write-knoppen in UI zijn `disabled` met ``. Zie `docs/scrum4me-architecture.md#demo-user-policy` en `docs/plans/ST-1110-demo-readonly.md` - **Foutberichten:** Nederlands voor eindgebruikers — comments in code: Engels - **Dependencies:** elke geïmporteerde runtime package staat direct in `dependencies`, niet alleen transitief in `package-lock.json` - **Docs-sync:** elke gedrags-, dependency-, API- of deploymentwijziging werkt README, relevante docs en patterns bij in dezelfde change - **Entity codes:** gebruik product/PBI/story-codes in commit-titles wanneer aanwezig (`feat(ST-356.2): ...`); branchnaam blijft `feat/ST-XXX-slug` - **Status-enums op API:** lowercase (`todo|in_progress|review|done`, `open|in_sprint|done`); DB houdt UPPER_SNAKE; conversie uitsluitend via `lib/task-status.ts`-mappers — nooit ad-hoc `.toLowerCase()` elders - **Foutcodes API:** `400` alleen voor malformed JSON-body (parse-fout via `request.json()`); `422` voor zod-validatie en well-formed-maar-niet-acceptabel; `403` voor demo-tokens. Documenteer per endpoint in `docs/API.md` - **Tests volgen contract:** bij een API-contract-wijziging (status, foutcode, response-shape) MOET in dezelfde commit ook `__tests__/api/` mee — een test die rood gaat omdat de oude waarde wordt verwacht is een onvolledige wijziging, niet een "kapotte test" - **Dev port:** `npm run dev` draait altijd op **3000**. Een `predev`-hook killt vooraf elk proces op 3000 (stale Next.js dev-server, vorige sessie) zodat sessies, cookies en MCP-config consistent op één poort werken. Wijk hier niet van af — geen `-p 3001` o.i.d. tenzij je expliciet twee dev-servers naast elkaar wil draaien --- ## Branch & PR Strategy (STRICT — kostenbeheersing) > **Core rule: één branch per milestone, PR alleen na gebruikerstest** Elke `git push` naar een feature-branch triggert een Vercel preview-deployment. Op het huidige Hobby-account zijn die schaars en kosten geld; we minimaliseren preview-builds tot er werkelijk iets te reviewen valt. ### Wel doen - Eén branch voor de hele milestone — `feat/M{N}-{slug}` (bv. `feat/M10-qr-login`); voor losse stories zonder milestone blijft `feat/ST-XXX-{slug}` geldig - Commits accumuleren lokaal volgens de Commit Strategy hieronder — één commit per stap, ST-code in de titel - Pushen + PR openen **pas nadat de gebruiker de milestone handmatig heeft getest en goedgekeurd** — vraag expliciet om bevestiging vóór `git push` - Tussentijdse "klaar voor jouw test"-momenten markeren met een lokale tag of een berichtje in chat, niet met een push ### Niet doen - Pushen na elke story of commit - Een PR per story openen tijdens de implementatie - "Just-in-case" pushen om backup te hebben — gebruik `git stash`, een lokale tag, of meerdere lokale branches - `--force-push` om eerdere preview-builds "weg te toveren" (kost dezelfde build opnieuw bij hercreatie) - **Direct pushen naar `main`** — die branch heeft protection rules; gebruik altijd een PR ### Wanneer wel commit-zonder-vragen, wanneer niet - **Tijdens een directed sprint-flow** (Track A: `mcp__scrum4me__implement_next_story` of een expliciete *"implementeer M{N}"*-opdracht): commit-per-laag conform de Commit Strategy hieronder is impliciet geautoriseerd — niet per commit vragen - **Bij ad-hoc / out-of-band werk** (bug-fix tussendoor, refactor, kleine wijziging op verzoek): toon de diff + voorgestelde commit-message en wacht op `"commit it"` voordat je `git commit` draait - **`git push` is altijd expliciet** — de scope van de policy gaat over preview-builds, dus push gebeurt alleen na gebruiker-test, ongeacht commit-context ### Uitzonderingen op de push-regel - Een **planning-PR** zonder code-wijzigingen (alleen docs in `docs/plans/` of `docs/`) mag direct gepusht worden — die triggert geen functional regressie en is goedkoop te bouwen - Een **bugfix-hotfix** op `main` met aantoonbare productie-impact mag direct gepusht worden (via een PR — zie boven) ### Wanneer aanpassen Zodra het Vercel-account naar Pro (of andere omgeving zonder per-build-kosten) gaat: vervang deze regel door "branch + PR per story" zoals oorspronkelijk in dit document stond. Werk deze sectie bij én documenteer de wijziging in `docs/agent-instruction-audit.md`. --- ## Plan Mode - Voor simpele, goed-afgebakende file-edits: **niet** in plan mode gaan — gewoon de wijziging maken - Reserveer plan mode voor multi-step refactors, ambigue verzoeken, of milestone-planning waarbij design-keuzes vooraf bevestigd moeten worden - Plannen die uit plan mode komen: opslaan als `docs/plans/M{N}-{slug}.md` (zie memory `feedback_plan_location`), niet als ephemeral systeem-bestand --- ## Commit Strategy (STRICT) > **Core rule: één commit = één verantwoordelijkheid** ### Nooit doen - Database + API + UI in één commit mengen - Feature + documentatie combineren - Grote "alles gewijzigd" commits - Vage berichten zoals "update stuff" ### Verplichte structuur Splits werk op in logische lagen: 1. Database / Prisma 2. API / server actions 3. UI / components 4. Config / infra 5. Documentatie ### Commit-formaat ``` feat(ST-XXX): korte beschrijving fix(ST-XXX): korte beschrijving chore(ST-XXX): korte beschrijving docs(ST-XXX): korte beschrijving ``` ### Voorbeeld (verplicht patroon) In plaats van: ```bash feat: add profile system ``` Splits altijd op in: ```bash feat(ST-XXX): add user profile fields to Prisma schema feat(ST-XXX): add avatar upload endpoint feat(ST-XXX): add profile editor component chore(ST-XXX): configure sharp for avatar processing docs(ST-XXX): document profile feature ``` --- ## Scrum-terminologie | Correct | Niet gebruiken | |---|---| | Product Backlog Item (PBI) | Feature, Epic, Issue | | Story | User Story, Ticket | | Sprint Goal | Sprint Objective | | Scrum Team | Team | --- ## MCP-integratie Scrum4Me heeft een eigen MCP-server in repo [`madhura68/scrum4me-mcp`](https://github.com/madhura68/scrum4me-mcp) die de REST-API als native tools voor Claude Code aanbiedt. Schema's worden gedeeld via een git submodule (`vendor/scrum4me`), niet gedupliceerd. ### Tools beschikbaar in Claude Code (18) **Read / context:** - `mcp__scrum4me__health` — service + DB ping - `mcp__scrum4me__list_products` — producten waar de tokengebruiker toegang tot heeft - `mcp__scrum4me__get_claude_context` — bundled product / actieve sprint / next story (met tasks) / open todos **Authoring (PBI/Story/Task aanmaken):** - `mcp__scrum4me__create_pbi` — `{ product_id, title, description?, priority, sort_order? }`; auto sort_order = last+1 binnen prio-groep - `mcp__scrum4me__create_story` — `{ pbi_id, title, description?, acceptance_criteria?, priority, sort_order? }`; product_id afgeleid uit PBI; status=OPEN - `mcp__scrum4me__create_task` — `{ story_id, title, description?, implementation_plan?, priority, sort_order? }`; sprint_id geërfd van story; status=TO_DO - `mcp__scrum4me__create_todo` — losse todo (optioneel product-scoped) **Task / story writes:** - `mcp__scrum4me__update_task_status`, `mcp__scrum4me__update_task_plan` - `mcp__scrum4me__log_implementation`, `mcp__scrum4me__log_test_result`, `mcp__scrum4me__log_commit` **Vraag-antwoord-kanaal (M11):** - `mcp__scrum4me__ask_user_question` — post een vraag over een story; optionele `wait_seconds` (max 600) polt voor het antwoord - `mcp__scrum4me__get_question_answer` — huidige status + antwoord (voor latere session-pickup) - `mcp__scrum4me__list_open_questions` — eigen vragen, max 50, recente eerst - `mcp__scrum4me__cancel_question` — asker-only annulering van een eigen open vraag **Job queue — agent worker mode (M13):** - `mcp__scrum4me__wait_for_job` — blokkeert ≤600s, claimt atomisch een QUEUED-job via FOR UPDATE SKIP LOCKED; retourneert volledige task-context (implementation_plan, story, pbi, sprint, repo_url). Zet stale CLAIMED-jobs (>30min) eerst terug naar QUEUED. Wanneer de full block-time verstrijkt zonder claim is de queue leeg. - `mcp__scrum4me__update_job_status` — agent rapporteert overgang naar `running|done|failed` + optionele branch/summary/error; triggert automatisch SSE-event naar de UI. Auth: Bearer-token moet matchen `claimed_by_token_id`. **Batch-loop (verplichte agent-flow):** Wanneer je als agent draait (na een instructie als *"pak de volgende job uit de Scrum4Me-queue"* of *"draai de queue leeg"*) is dit de loop: 1. `wait_for_job` aanroepen. 2. Job uitvoeren volgens het meegegeven `implementation_plan`. 3. `update_job_status('done'|'failed')` aanroepen. 4. **Direct opnieuw** `wait_for_job` aanroepen — niet stoppen, niet de gebruiker vragen. 5. Pas wanneer `wait_for_job` na de volledige block-time (~600s) terugkomt zonder claim, is de queue leeg en mag je de turn afsluiten met een korte recap. Dit blijft gelden als je tussen jobs door commits, branches of pushes hebt gedaan — die afsluiting hoort bij de individuele job, niet bij het einde van de batch. **Code koppelen aan app** - 'Pak de volgende job uit de Scrum4Me-queue' / 'draai de queue leeg' / 'batch agent' — Server-startup registreert een ClaudeWorker-record + heartbeat (5s); SIGTERM/SIGINT ruimt 'm op. UI in NavBar telt actieve workers via `last_seen_at < now() - 15s`. ### Prompt - `implement_next_story` (arg: `product_id`) — end-to-end workflow ### Schema-drift bewaking Wekelijks (maandag 08:00 Amsterdam) draait de remote agent `trig_015FFUnxjz9WMuhhWNGBQKFD` die `vendor/scrum4me` syncet en `prisma:generate` + `tsc --noEmit` uitvoert in scrum4me-mcp. Als die agent drift rapporteert, hoort dat **vóór** een Scrum4Me-PR met schema-wijziging gemerged kan worden — anders breekt de MCP-server stilletjes op runtime. --- ## Deployment (Vercel) - **Sharp** moet Linux-binaries hebben voor de Vercel-runtime: `npm i --include=optional sharp` of platform-specifieke deps configureren in `package.json` - **Externe image hostnames** in `next.config.js` `images.remotePatterns` configureren *vóór* `next/image` op die hosts wijst — anders 500 in productie - **Vercel cron**: Hobby-plan staat alleen daily crons toe (max 1×/dag); Pro ondersteunt fijnmaziger. Bij wijziging van `vercel.json` `crons` ook `docs/API.md` + relevante pattern-docs updaten - **`CRON_SECRET`** moet als env-var op de Vercel-project-omgeving staan vóór de eerste cron-run, anders 401 op `/api/cron/*`-endpoints - **Preflight** vóór deploy: `npm run lint && npm test && npm run build` — falende build laat een PR niet door (CI blokkeert merge per ST-610) --- ## Definition of Done (MVP) M7 (MCP-server) is post-MVP en heeft eigen acceptatie in `docs/scrum4me-backlog.md`. - [ ] Alle 62 tasks (ST-001 t/m ST-612) afgerond - [ ] Volledige Lars-flow zonder fouten (ST-612) - [ ] Alle gedocumenteerde API-endpoints werken via curl (zie `docs/API.md`) - [ ] Demo-gebruiker heeft geen schrijfrechten - [ ] App opzetbaar via README zonder extra hulp - [ ] CI/CD actief — falende build blokkeert merge - [ ] Beveiligingsreview API geslaagd (cross-user toegang onmogelijk) - [ ] Documentatie is bijgewerkt voor gewijzigde API's, dependencies, deployment en agent-instructies