Plan voor live updates in het Solo Paneel: Server-Sent Events op een Node.js-runtime route die luistert naar Postgres NOTIFY's op tasks/ stories. Eén bron van waarheid (DB) — werkt onafhankelijk of een mutatie van de web-UI, REST API of MCP-server komt. Zes stories: - ST-801 trigger-functie + triggers - ST-802 SSE-route /api/realtime/solo - ST-803 useSoloRealtime hook - ST-804 solo-store realtime-acties - ST-805 wire-up + UI-indicator - ST-806 documentatie + acceptatie Volledig plan in .Plans/2026-04-27-m8-realtime-solo.md (lokaal). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
53 KiB
Scrum4Me — Implementatie Backlog
Versie: 0.1 — april 2026 Volgt op: Functionele Specificatie v0.2, Architectuur v0.1
MVP-definitie
De MVP is klaar wanneer Lars — de primaire persona — de volledige cyclus kan doorlopen: een product aanmaken, een Product Backlog opbouwen met PBI's en stories, een Sprint plannen, taken aanmaken, en Claude Code de volgende story laten ophalen, implementeren en vastleggen — allemaal zonder hulp of handleiding. De app draait stabiel op Vercel en is volledig lokaal opzetbaar via één README.
Milestone-overzicht
| Milestone | Doel | Tasks |
|---|---|---|
| M0: Foundation | Project, database, auth, navigatieshell | ST-001 – ST-008 |
| M1: Producten & Product Backlog | Producten, PBI's, gesplitst scherm | ST-101 – ST-110 |
| M2: Stories & Drag-and-drop | Stories als blokken, dnd-kit, Zustand | ST-201 – ST-210 |
| M3: Sprint Backlog & Sprint Planning | Sprint aanmaken, stories slepen, taken | ST-301 – ST-313 |
| M3.5: Solo Paneel & Story Assignment | Story-claim, persoonlijk Kanban-bord per product | ST-350 – ST-360 |
| M4: Claude Code REST API | Alle endpoints, tokenbeheer | ST-401 – ST-410 |
| M5: Todo-lijst | Todo CRUD, promotie naar PBI/story; Data Table + detail-kaart | ST-501 – ST-506, ST-509 – ST-510 |
| M6: Polish & Launch-ready | Foutafhandeling, toegankelijkheid, CI/CD, beveiliging | ST-601 – ST-612 |
| M7: MCP-server voor Claude Code | Native MCP-laag bovenop Scrum4Me-DB (aparte repo scrum4me-mcp) |
ST-701 – ST-710 |
| M8: Realtime Solo Paneel | Live updates voor stories/tasks via SSE + Postgres LISTEN/NOTIFY | ST-801 – ST-806 |
Backlog
M0: Foundation
-
ST-001 Project scaffolding
create-next-appmet TypeScript strict, Tailwind CSS, App Router; installeer shadcn/ui, Zustand, dnd-kit, iron-session, bcrypt, Zod; configureer path aliases (@/)- Done when:
npm run devstart zonder fouten;npm run lintgeeft geen errors; shadcnButtonrendert op een testpagina
-
ST-002 Prisma v7 setup +
prisma.config.ts- Installeer Prisma v7 +
@prisma/adapter-pg; schrijfprisma.config.tsmetDATABASE_URLvia Zod-gevalideerde env; schrijflib/prisma.tssingleton - Done when:
npx prisma db pushslaagt; Prisma Client importeerbaar in een testbestand zonder fouten
- Installeer Prisma v7 +
-
ST-003 Database schema migratie (volledige initiële migratie)
- Schrijf het volledige
schema.prismaop basis van het architectuurdocument:User,UserRole,ApiToken,Product,Pbi,Story,StoryLog,Sprint,Task,Todo; alle enums, indexes, cascade deletes - Done when:
npx prisma migrate dev --name initslaagt; alle tabellen zichtbaar in DB-client;npx prisma validategeeft geen fouten
- Schrijf het volledige
-
ST-004 Seed met testdata
- Schrijf
prisma/seed.tsop basis van het Product Backlog document (devplanner-product-backlog.md); seed één gebruiker, één product (Scrum4Me zelf), alle PBI's en stories als testdata; voeg demo-gebruiker toe - Done when:
npx prisma db seedslaagt; DB bevat alle PBI's en stories uit het backlog-document; demo-gebruiker aanwezig
- Schrijf
-
ST-005 Environment variabelen +
lib/env.ts- Schrijf Zod-schema voor alle env vars (
DATABASE_URL,DIRECT_URL,SESSION_SECRET,NODE_ENV); exporteer gevalideerdenvobject; schrijf.env.examplemet instructies - Done when: app gooit een begrijpelijke fout bij ontbrekende env var;
.env.examplevolledig gedocumenteerd
- Schrijf Zod-schema voor alle env vars (
-
ST-006 Authenticatie — registratie en inloggen
- Schrijf
lib/auth.ts(registreer met bcrypt hash, verifieer bij inloggen); schrijflib/session.ts(iron-session config); implementeer/registeren/loginpagina's met Server Actions; sla{ userId, isDemo }op in sessiecookie - Done when: registreren → ingelogde sessie → redirect
/dashboard; inloggen met verkeerde credentials geeft generieke foutmelding; sessie blijft actief na paginaverversing
- Schrijf
-
ST-007 Route-beveiliging via
proxy.ts- Schrijf
proxy.tsdie sessiecookie-aanwezigheid controleert; redirect naar/loginbij alle/dashboard,/products/*,/todos,/settings/*routes zonder sessiecookie; authenticated users worden van/loginen/registerdoorgestuurd naar/dashboard; volledige sessievalidatie gebeurt server-side in de app layout - Done when: directe navigatie naar
/dashboardzonder sessie redirect naar/login; ingelogde gebruiker op/loginredirect naar/dashboard
- Schrijf
-
ST-008 Navigatieshell + dashboard-layout
- Schrijf
app/(app)/layout.tsxmet navigatiebalk (logo, productenlink, todolink, instellingen, uitlogknop); implementeer uitlog Server Action; implementeer/dashboardals lege productenlijstpagina met "Maak je eerste product aan" lege staat; zet demo-badge zichtbaar alsisDemo === true - Done when: volledige auth-flow (register → login → dashboard → logout → login) werkt end-to-end; demo-gebruiker ziet badge in navigatie
- Schrijf
M1: Producten & Product Backlog
-
ST-101 Product aanmaken
/products/newpagina met formulier (naam, beschrijving, repo URL, definition of done);createProductServer Action met Zod-validatie; uniekheidscontrole op naam per gebruiker; redirect naar/products/[id]na aanmaken- Done when: product aangemaakt en zichtbaar op dashboard; dubbele naam geeft inline validatiefout; lege naam blokkeert submit
-
ST-102 Productenlijst op dashboard
- Haal actieve producten op via Prisma Server Component; toon naam, beschrijving (ingekort 80 tekens), repo-link; lege staat met CTA; klikken opent Product Backlog
- Done when: twee producten zichtbaar na aanmaken; gearchiveerd product niet zichtbaar in standaardlijst
-
ST-103 Product bewerken en archiveren
- Bewerkformulier (naam, beschrijving, repo URL, DoD) via Server Action; archiveerknop met bevestigingsdialoog; hersteloptie voor gearchiveerde producten; "toon gearchiveerd"-filter op dashboard
- Done when: naam bijwerken persisteert; archiveren verbergt product; herstel maakt het weer zichtbaar
-
ST-104 Gesplitst scherm layout component (
SplitPane)- Bouw herbruikbaar
<SplitPane>Client Component met versleepbare horizontale splitter; sla splitter-positie op inlocalStorageper sleutel; standaard 40/60 verhouding; minimale panelbreedte 200px; responsive fallback naar tabs op < 1024px - Done when: splitter versleepbaar en positie behouden na paginaverversing; tabs getoond op smal scherm
- Bouw herbruikbaar
-
ST-105 Navigatiebar-component per paneel
- Bouw herbruikbaar
<PanelNavBar>component met slots voor knoppen (aanmaken, filter, verwijderen); consistent design voor linker- en rechterpaneel - Done when: navigatiebar herbruikt in minimaal twee gesplitste schermen zonder duplicatie
- Bouw herbruikbaar
-
ST-106 PBI aanmaken en weergeven
- Linkerpaneel van
/products/[id]: haal PBI's op gegroepeerd op prioriteit en sort_order; "PBI aanmaken" knop opent inline formulier (titel, prioriteit);createPbiServer Action; nieuw PBI verschijnt onderaan de juiste prioriteitsgroep - Done when: PBI aangemaakt en zichtbaar in juiste prioriteitsgroep; lege staat toont prompt
- Linkerpaneel van
-
ST-107 PBI prioriteitsgroepen met visuele scheiding
- Render PBI's gegroepeerd per prioriteit (1–4) met gelabelde scheidingslijn per groep (bijv. "Kritiek", "Hoog"); lege groepen zijn niet zichtbaar; prioriteitsbadge per PBI
- Done when: vier prioriteitsgroepen correct gerenderd met labels; PBI met prioriteit 1 staat boven prioriteit 4
-
ST-108 PBI bewerken en verwijderen
- Inline bewerkingsmodus via dubbelklik of contextmenu (titel, omschrijving, prioriteit);
updatePbiServer Action; verwijderen met bevestigingsdialoog inclusief waarschuwing cascade;deletePbiServer Action - Done when: titelbewering opgeslagen zonder paginaverversing; verwijderen cascade-verwijdert stories (verifieerbaar in DB)
- Inline bewerkingsmodus via dubbelklik of contextmenu (titel, omschrijving, prioriteit);
-
ST-109 PBI selecteren → stories laden
- Klikken op PBI in linkerpaneel toont bijbehorende stories rechts via
useSelectionStore; geselecteerd PBI visueel gemarkeerd; lege staat rechts als geen stories - Done when: klikken op PBI A toont stories van A rechts; klikken op PBI B schakelt direct over
- Klikken op PBI in linkerpaneel toont bijbehorende stories rechts via
-
ST-110 PBI filter
- Filterknop in linkerpaneel navigatiebar; dropdown voor prioriteit (1–4, alle); filter werkt realtime op gerenderde lijst; actief filter zichtbaar als badge; wissen via ×-knop
- Done when: filter op prioriteit 1 verbergt alle andere PBI's; wissen herstelt volledige lijst
M2: Stories & Drag-and-drop
-
ST-201
usePlannerStoreZustand-store- Schrijf
stores/planner-store.tsmetpbiOrder,storyOrder,taskOrder;init*,reorder*,rollback*actions; TypeScript strict types - Done when: store importeerbaar in een Client Component;
initPbisvult order;reorderPbismuteert order;rollbackPbisherstelt vorige staat
- Schrijf
-
ST-202
useSelectionStoreZustand-store- Schrijf
stores/selection-store.tsmetselectedPbiId,selectedStoryId, setters enclearSelection - Done when: selectie in linkerpaneel via store zichtbaar in rechterpaneel zonder prop drilling
- Schrijf
-
ST-203 dnd-kit setup + PBI drag-and-drop
- Installeer dnd-kit; wrap linkerpaneel in
DndContext+SortableContext; implementeeruseSortableper PBI-rij;onDragEnd: bereken nieuwesort_ordervia float-gemiddelde; optimistisch updaten viausePlannerStore;reorderPbisActionServer Action; rollback bij fout - Done when: PBI versleepbaar binnen prioriteitsgroep; volgorde opgeslagen na loslaten; UI rollback bij gesimuleerde server-fout
- Installeer dnd-kit; wrap linkerpaneel in
-
ST-204 PBI drag-and-drop over prioriteitsgrens
- Uitbreiding ST-203: slepen over een prioriteitsgrens wijzigt
priorityvan het PBI;sort_orderwordt onderaan de doelgroep geplaatst;updatePbiPriorityServer Action - Done when: PBI naar prioriteit 2 slepen vanuit prioriteit 3 wijzigt zowel prioriteit als volgorde
- Uitbreiding ST-203: slepen over een prioriteitsgrens wijzigt
-
ST-205 Story aanmaken en weergeven als blokken
- Rechterpaneel van Product Backlog: haal stories op voor geselecteerd PBI; render als blokken (~10% schermbreedte, horizontaal); elk blok toont titel (ingekort), prioriteitsbadge, statusbadge; "Story aanmaken" knop;
createStoryServer Action - Done when: drie stories zichtbaar als blokken; nieuw blok verschijnt in juiste prioriteitsgroep
- Rechterpaneel van Product Backlog: haal stories op voor geselecteerd PBI; render als blokken (~10% schermbreedte, horizontaal); elk blok toont titel (ingekort), prioriteitsbadge, statusbadge; "Story aanmaken" knop;
-
ST-206 Story prioriteitsgroepen met visuele scheiding
- Groepeer story-blokken per prioriteit; gekleurde band of scheidingslijn per groep; blokken horizontaal gerangschikt per rij; nieuwe rij bij overloop
- Done when: stories van vier prioriteiten correct gescheiden weergegeven
-
ST-207 Story drag-and-drop (horizontaal, binnen en tussen groepen)
- dnd-kit horizontale
SortableContextper prioriteitsgroep;onDragEnd: herrangschikking via float-gemiddelde instoryOrder; slepen naar andere groep wijzigt prioriteit; optimistisch viausePlannerStore;reorderStoriesActionServer Action; rollback bij fout - Done when: story versleepbaar binnen groep en naar andere groep; volgorde en prioriteit persistent na loslaten
- dnd-kit horizontale
-
ST-208 Story detail-modal / slide-over
- Klikken op storyblok opent slide-over of modal met titel, omschrijving, acceptatiecriteria, statusbadge, activiteitenlog (leeg bij nieuwe story); bewerkformulier voor titel/omschrijving/acceptatiecriteria;
updateStoryServer Action - Done when: klikken op blok opent detail; bewerken persisteert; sluiten keert terug naar backlog
- Klikken op storyblok opent slide-over of modal met titel, omschrijving, acceptatiecriteria, statusbadge, activiteitenlog (leeg bij nieuwe story); bewerkformulier voor titel/omschrijving/acceptatiecriteria;
-
ST-209 Story verwijderen
- Verwijderknop in story-detail of contextmenu; bevestigingsdialoog met waarschuwing cascade (taken);
deleteStoryServer Action; blok verdwijnt optimistisch uit het rechterpaneel - Done when: story verwijderd incl. cascade-taken (verifieerbaar in DB); blok direct verdwenen uit UI
- Verwijderknop in story-detail of contextmenu; bevestigingsdialoog met waarschuwing cascade (taken);
-
ST-210 Story filter in rechterpaneel
- Filterknop in rechterpaneel navigatiebar; filter op status (OPEN, IN_SPRINT, DONE) en prioriteit; realtime; actief filter als badge; wissbaar
- Done when: filter op OPEN verbergt IN_SPRINT stories
M3: Sprint Backlog & Sprint Planning
-
ST-301
useSprintStoreZustand-store- Schrijf
stores/sprint-store.ts;initSprint,addStoryToSprint,removeStoryFromSprint,reorderSprintStories,rollbackSprint - Done when: store beheert sprint-story-volgorde onafhankelijk van planner-store
- Schrijf
-
ST-302 Sprint aanmaken
- "Sprint starten" knop op productpagina (zichtbaar als geen actieve Sprint); modal met Sprint Goal invoerveld;
createSprintServer Action; max. 1 actieve Sprint per product afgedwongen in service-laag - Done when: Sprint aangemaakt met Goal; tweede sprint aanmaken terwijl eerste actief is geeft foutmelding
- "Sprint starten" knop op productpagina (zichtbaar als geen actieve Sprint); modal met Sprint Goal invoerveld;
-
ST-303 Sprint Backlog scherm — layout
/products/[id]/sprintpagina;SplitPanemet Sprint Backlog links (stories in Sprint op volgorde) en rechts de Product Backlog stories gegroepeerd per PBI (inklapbaar); Sprint Goal zichtbaar bovenaan; lege staat links met instructie- Done when: pagina rendert correct; Sprint Goal zichtbaar; beide panelen tonen juiste data
-
ST-304 Story vanuit Product Backlog naar Sprint slepen
- dnd-kit drag vanuit rechterpaneel naar linkerpaneel;
onDragEnd:addStoryToSprintin store; story krijgt badge "In Sprint" in Product Backlog;addStoryToSprintActionServer Action (zetsprint_id+ statusIN_SPRINT); rollback bij fout - Done when: story gesleept naar Sprint verschijnt links en toont "In Sprint" badge rechts; persistent na herlaad
- dnd-kit drag vanuit rechterpaneel naar linkerpaneel;
-
ST-305 Sprint Backlog story volgorde aanpassen
- dnd-kit verticale
SortableContextin linkerpaneel; herrangschikking via float-gemiddelde inuseSprintStore;reorderSprintStoriesActionServer Action - Done when: volgorde in Sprint Backlog persistent na loslaten en na paginaverversing
- dnd-kit verticale
-
ST-306 Story uit Sprint verwijderen
- Verwijderknop per story in Sprint Backlog;
removeStoryFromSprintActionServer Action (wistsprint_id, zet status terug opOPEN); story verdwijnt links en badge verdwijnt rechts - Done when: verwijderen persistent; story beschikbaar in Product Backlog rechterpaneel
- Verwijderknop per story in Sprint Backlog;
-
ST-307 Sprint Planning scherm — layout
/products/[id]/sprint/planningpagina;SplitPanemet Sprint Backlog stories links (op volgorde) en taken van geselecteerde story rechts; Sprint Goal zichtbaar; lege staat rechts als geen story geselecteerd- Done when: pagina rendert; story selecteren links toont taken rechts
-
ST-308 Taak aanmaken
- "Taak aanmaken" knop in rechterpaneel navigatiebar; inline formulier (titel, omschrijving, prioriteit);
createTaskServer Action; voortgangsindicator per story (bijv. "0/0 Done") - Done when: taak aangemaakt en zichtbaar in takenlijst; voortgangsindicator toont "0/1 Done"
- "Taak aanmaken" knop in rechterpaneel navigatiebar; inline formulier (titel, omschrijving, prioriteit);
-
ST-309 Taak drag-and-drop (verticaal)
- dnd-kit verticale
SortableContextin rechterpaneel; herrangschikking via float-gemiddelde inusePlannerStore.taskOrder;reorderTasksActionServer Action - Done when: taken versleepbaar; volgorde persistent na loslaten
- dnd-kit verticale
-
ST-310 Taakstatus bijhouden
- Status-toggle per taak (TO_DO → IN_PROGRESS → DONE) via klikbare badge of dropdown;
updateTaskStatusServer Action; voortgangsindicator op story updatet optimistisch - Done when: taak op DONE zetten verhoogt teller in voortgangsindicator; persistent na herlaad
- Status-toggle per taak (TO_DO → IN_PROGRESS → DONE) via klikbare badge of dropdown;
-
ST-311 Taak bewerken en verwijderen
- Inline bewerken van titel, omschrijving en prioriteit;
updateTaskServer Action; verwijderen met bevestiging;deleteTaskServer Action - Done when: titelwijziging persisteert; verwijderde taak verdwijnt uit lijst
- Inline bewerken van titel, omschrijving en prioriteit;
-
ST-312 Sprint afronden
- "Sprint afronden" knop op Sprint-pagina; dialoog toont per story de status en vraagt: "Markeer als Done of terug naar Backlog?";
completeSprintServer Action zet Sprint op COMPLETED, verwerkt keuzes per story - Done when: Sprint afgerond; stories correct verplaatst naar DONE of OPEN; nieuwe Sprint aanmaakbaar
- "Sprint afronden" knop op Sprint-pagina; dialoog toont per story de status en vraagt: "Markeer als Done of terug naar Backlog?";
-
ST-313 Sprint Board — drie-panelen layout (vervangt ST-303 + ST-307)
- Doel:
/products/[id]/sprintwordt één scherm met drie panelen van links naar rechts: Product Backlog · Sprint Backlog · Taken. De losse/sprint/planningroute wordt verwijderd (redirect →/sprint). - Panelen:
- Links — Product Backlog: PBIs met stories gegroepeerd en inklapbaar; stories die al in sprint zijn grijs/disabled; klikken of slepen voegt story toe aan Sprint Backlog (midden)
- Midden — Sprint Backlog: stories in sprint op volgorde; klikken selecteert story → taken laden rechts; versleepbaar om te sorteren; trash-knop verwijdert uit sprint
- Rechts — Taken:
TaskListvoor de geselecteerde story; lege staat "Selecteer een story" als niets geselecteerd; "+ Taak" knop zoals huidig
- Layout:
TriplePanecomponent — drie verticale panelen met twee versleepbare scheidingslijnen; opslaan inlocalStorageper product (key:sprint-triple-${productId}) - DnD: één
DndContextomhult alle drie panelen; drag van links naar midden werkt viaDragOverlay; reorder binnen midden viaSortableContext; taken-reorder in eigen genesteDndContext - State:
SprintBoardClientbeheert sprint stories, product backlog data,selectedStoryId, en taken per story (vanuit server props);useSelectionStore.selectedStoryIdvoor story-selectie - Navigatie: "Sprint Planning →" link onderaan Sprint Backlog pagina verwijderd;
SprintHeaderblijft bovenaan met "Sprint afronden" - Route cleanup:
/sprint/planning/page.tsxvervangt door redirect naar/products/[id]/sprint;PlanningLeft,PlanningRightClientcomponents verwijderen - Done when: één
/sprintpagina toont alle drie panelen; story slepen van links naar midden werkt; story selecteren toont taken rechts; taak aanmaken en sorteren werkt; pagina hervat na herlaad met juiste data;/sprint/planningredirect werkt
- Doel:
M3.5: Solo Paneel & Story Assignment
Doel: een persoonlijk Kanban-bord per product dat de taken toont van stories die geclaimd zijn door de ingelogde developer. Story-level assignment volgt het Scrum self-organizing principe: developers claimen vrijwillig stories (pull, niet push). Volledige technische specificatie in
solo-paneel-spec.md.
-
ST-350 Story.assignee_id schema-migratie + auth-helpers
- Schema: voeg
assignee_id String?+assignee User? @relation("StoryAssignee", fields: [assignee_id], references: [id], onDelete: SetNull)toe aanStory; voegassigned_stories Story[] @relation("StoryAssignee")toe aanUser; voeg index@@index([sprint_id, assignee_id])toe; migratie viaprisma migrate dev --name add_story_assignee - Auth-helpers: schrijf
lib/auth.tsmetgetSession,requireUser,requireWriter,requireProductAccess,requireProductWriter— laatste twee doen membership-check via owner (Product.user_id) OF lid (ProductMember); demo-check op basis vansession.isDemo(uit ST-006); throwt "Niet beschikbaar in demo-modus" bij demo-write-poging - Done when: migratie slaagt;
requireProductWriterblokkeert demo-user;requireProductAccessaccepteert zowel owner als member
- Schema: voeg
-
ST-351
<UserAvatar>herbruikbare component- Wrapper rond shadcn
Avatar; props:userId,username,size('xs' | 'sm' | 'md' | 'lg'),className;<AvatarImage src="/api/users/{userId}/avatar">met fallback naar initialen (eerste 2 tekens username) opbg-primary-container; vier groottes via Tailwind classes - Done when: avatar rendert in 4 sizes; bij ontbrekende avatar-data (404) fallback naar initialen zichtbaar; component bruikbaar in story-kaart, sprint board, instellingen
- Wrapper rond shadcn
-
ST-352 Story-claim Server Actions
- Vier acties in
actions/stories.ts:claimStoryAction(zetassignee_id = currentUserId),unclaimStoryAction(null),reassignStoryAction(valideert dat target user lid van product is),claimAllUnassignedInActiveSprintAction(bulk viaupdateManyvoor ongeclaimde stories in actieve sprint); allemaal Zod-gevalideerd, achterrequireProductWriter, metrevalidatePathvoor/sprintén/solo; tenant-guard viawhere: { id, product_id } - Done when: alle vier acties testbaar via testbestand; demo-user krijgt foutmelding; reassignment naar niet-lid faalt met foutmelding; bulk claimt alleen ongeclaimde
- Vier acties in
-
ST-353 Sprint Board: assignee-chip + dropdown menu op story-kaart
- Op story-kaart in middenpaneel van ST-313 Sprint Board: assignee-chip onderaan met
<UserAvatar size="xs">+ username (of muted "Niet geclaimd" badge alsassignee_id === null); shadcnDropdownMenu(3-dots rechtsboven) met items "Pak op" / "Geef terug aan team" / "Wijs toe aan ▶" (submenu met members); items conditioneel zichtbaar op basis van huidige assignee; demo-modus: dropdown disabled met tooltip "Niet beschikbaar in demo-modus" - Done when: chip toont juiste state; dropdown roept juiste acties aan; revalidatie ververst kaart; toast "Story geclaimd" / "Toegewezen aan X" bij succes; demo-user ziet disabled-tooltip
- Op story-kaart in middenpaneel van ST-313 Sprint Board: assignee-chip onderaan met
-
ST-354 Sprint Board: bulk-claim knop "Claim alle ongeclaimde"
- Knop bovenaan Sprint Backlog paneel met telling: "Claim alle ongeclaimde stories (N)"; disabled als N=0 of
isDemo; klik roeptclaimAllUnassignedInActiveSprintActionaan; Sonner success-toast "{count} stories geclaimd"; pending state viauseTransition - Done when: telling correct; claimen werkt; knop disabled bij 0 ongeclaimd of demo; toast verschijnt na succes
- Knop bovenaan Sprint Backlog paneel met telling: "Claim alle ongeclaimde stories (N)"; disabled als N=0 of
-
ST-355 Solo route —
/soloredirect +/products/[id]/solopagina + cookie- Cookie-helper: schrijf
lib/cookies.tsmetsetLastProductCookie(productId)(HTTP-only, sameSite lax, 30 dagen) /solopage.tsx: Server Component; leest cookielastProductId; valideert toegang en redirect naar/products/[id]/solo, of toont<ProductPicker>als geen cookie of cookie ongeldig/products/[id]/solopage.tsx: Server Component; haalt active sprint op (404 → empty state<NoActiveSprint>); haalt taken op viaTask.findManymetwhere: { sprint_id, story: { assignee_id: session.userId } }+ count ongeclaimde stories parallel; geeft data door aan<SoloBoard>; zetlastProductIdcookie bij elk bezoek- Empty state:
<NoActiveSprint>met titel, uitleg, link naar productpagina <ProductPicker>: lijst van toegankelijke producten, klikken redirect naar/products/[id]/solo- Done when:
/solozonder cookie toont picker; met geldige cookie redirect; pagina toont juiste taken; geen actieve sprint toont empty state; cookie persisteert tussen sessies
- Cookie-helper: schrijf
-
ST-356 Solo Kanban-bord met DnD en Zustand
- Store
stores/solo-store.ts:tasks,initTasks,optimisticMove(taskId, toStatus)(returnt vorige status),rollback(taskId, prevStatus),updatePlan(taskId, plan); volgt patroon vanusePlannerStore(ST-201) <SoloBoard>Client Component: root metDndContext(overslaan alsisDemo),PointerSensormetactivationConstraint: { distance: 5 },closestCornerscollision detection; header met productnaam, sprint goal, knop "Toon openstaande stories (N)"; grid met drie kolommen<SoloColumn>: drop target per status (TO_DO/IN_PROGRESS/DONE); header met statuskleur via MD3 tokens (bg-status-todo/15etc.); count en lege staat<SoloTaskCard>: hergebruik bestaande task-card (ST-310); draggable; toont prioriteit-indicator, taaktitel, story-titel; klik opent detail-dialoog (ST-357); demo: niet draggableonDragEndflow: optimistische update viaoptimisticMove, danupdateTaskStatusActionaanroepen, op error rollback + Sonner error-toast "Status bijwerken mislukt — taak teruggeplaatst"; geen success-toast (te frequent)- Done when: kaart sleepbaar tussen kolommen; status persisteert; gesimuleerde server-fout rollbackt UI; demo-user kan niet slepen
- Store
-
ST-357 Task detail-dialoog +
updateTaskPlanActionupdateTaskPlanActioninactions/tasks.ts: Zod-schema{ taskId, productId, implementationPlan };requireProductWriter; tenant-guard viawhere: { id: taskId, story: { product_id: productId } };revalidatePath<TaskDetailDialog>shadcnDialog: header met taaktitel + statusbadge (MD3 tokens); sectie Beschrijving (read-only, volg bestaand patroon); sectie Implementatieplan met<Textarea>save-on-blur; on-blur roeptupdateTaskPlanAction, indicator rechtsonder ("Bezig met opslaan…" → "Opgeslagen", vervaagt na 2s); error-toast bij fout; footer-link "Open in Sprint Board ↗"; demo-modus: textareareadOnlymet tooltip- Done when: edit + blur + refresh persisteert; gesimuleerde server-fout toont error-toast; demo-user kan dialoog openen maar niet bewerken
-
ST-358 Openstaande stories sheet
- Knop "Toon openstaande stories (N)" bovenaan Solo bord opent shadcn
<Sheet>(slide-out van rechts); inhoud: lijst van ongeclaimde stories in actieve sprint met titel, taakaantal, "Pak op"-knop per item; klik roeptclaimStoryAction, sheet blijft open (zodat meerdere achter elkaar claimen kan); Sonner success-toast per claim; lege staat "Geen ongeclaimde stories. Lekker bezig!"; pending state viauseFormStatus; demo: knoppen disabled met tooltip - Done when: sheet opent met N items; claimen verwijdert item uit lijst en verlaagt teller; lege staat correct; demo-user ziet sheet maar kan niet claimen
- Knop "Toon openstaande stories (N)" bovenaan Solo bord opent shadcn
-
ST-359 Navbar-link "Solo"
- Voeg
<NavLink href="/solo" icon={<UserSquare />}>Solo</NavLink>toe aan navigatieshell (ST-008); altijd zichtbaar voor ingelogde users (geen product-context); plek tussen "Producten" en "Todos" - Done when: link altijd zichtbaar in nav; klik gaat naar
/soloen redirect verder
- Voeg
-
ST-360 Demo-seed uitbreiden met geclaimde stories
- Update
prisma/seed.ts(ST-004): demo-user (is_demo = true) heeft minimaal één product met ACTIVE sprint; minimaal 3 stories metassignee_id = demoUser.id(variërend over taakstatussen TO_DO, IN_PROGRESS, DONE); minimaal 1 ongeclaimde story (om "Toon openstaande" te demonstreren — demo-user kan niet claimen, ziet wel hoe het werkt) - Done when: login als demo → Solo bord toont werkend Kanban met taken in alle drie kolommen; "Toon openstaande" sheet toont ten minste 1 story (claim-knoppen disabled)
- Update
M4: Claude Code REST API
-
ST-401 API-token infrastructuur
- Schrijf
lib/api-auth.ts: parseerAuthorization: Bearerheader; bereken SHA-256 hash; zoek op inapi_tokens; controleerrevoked_at; retourneeruserIdof 401; retourneer 403 alsis_demo - Done when: geldige token geeft userId terug; ongeldige token geeft 401; ingetrokken token geeft 401; demo-token op schrijf-endpoint geeft 403
- Schrijf
-
ST-402 API-tokenbeheer UI
/settings/tokenspagina; token aanmaken (label optioneel); token eenmalig getoond in kopieerbaar veld na aanmaken; tokenoverzicht (label, datum, actief/ingetrokken); intrekken via Server Action; max. 10 actieve tokens- Done when: token aangemaakt en waarde zichtbaar; na sluiten dialoog niet meer te zien; intrekken maakt token onbruikbaar (getest via curl)
-
ST-403
GET /api/products— productenlijst- Route Handler; authenticatie via
api-auth.ts; retourneer actieve producten[{ id, name, repo_url }]als JSON voor producten waar de tokengebruiker eigenaar of teamlid is - Done when:
curl -H "Authorization: Bearer <token>" /api/productsretourneert correct JSON inclusief gedeelde product backlogs; 401 zonder token
- Route Handler; authenticatie via
-
ST-404
GET /api/products/:id/next-story— volgende story ophalen- Route Handler; haal hoogst geprioriteerde OPEN story op van actieve Sprint van het product (priority ASC, sort_order ASC); retourneer
{ id, title, description, acceptance_criteria, tasks[] }; 404 als geen open stories - Done when: endpoint retourneert eerste story van Sprint; 404 als Sprint leeg; 404 als geen actieve Sprint
- Route Handler; haal hoogst geprioriteerde OPEN story op van actieve Sprint van het product (priority ASC, sort_order ASC); retourneer
-
ST-405
GET /api/sprints/:id/tasks— taken ophalen- Route Handler met
?limit=Nquery param (default 10, max 50); retourneer taken van actieve Sprint op(story.sort_order, task.priority, task.sort_order)volgorde; retourneer{ id, title, story_id, priority, sort_order, status } - Done when: endpoint retourneert max N taken in juiste volgorde;
?limit=5retourneert max 5
- Route Handler met
-
ST-406
PATCH /api/stories/:id/tasks/reorder— taakvolgorde aanpassen- Route Handler; body:
{ task_ids: string[] }; valideer alle IDs behoren tot de story; updatesort_ordervia float-verdeling; retourneer{ success: true } - Done when: volgorde in DB veranderd na PATCH; gewijzigde volgorde zichtbaar in Sprint Planning UI na herlaad; ongeldige task_id geeft 400
- Route Handler; body:
-
ST-407
POST /api/stories/:id/log— activiteit vastleggen- Route Handler; body:
{ type, content, status?, commit_hash?, commit_message? }; Zod-validatie per type; schrijf naarstory_logs; retourneer{ id, created_at } - Done when: drie typen werken (IMPLEMENTATION_PLAN, TEST_RESULT, COMMIT); log-entry zichtbaar in story-detail UI na aanmaken via API; ontbrekend verplicht veld geeft 400
- Route Handler; body:
-
ST-408
PATCH /api/tasks/:id— taakstatus en implementatieplan bijwerken- Route Handler; body:
{ status?: "TO_DO" | "IN_PROGRESS" | "DONE", implementation_plan?: string }; minimaal één veld verplicht; valideer dat taak aan requester's product behoort; retourneer{ id, status, implementation_plan } - Done when: status update via API zichtbaar in Sprint Planning UI; implementation_plan opgeslagen en opvraagbaar; lege body geeft 400; andere gebruikers taak geeft 403
- Route Handler; body:
-
ST-409
POST /api/todos— todo aanmaken- Route Handler; body:
{ title: string, product_id: string }; valideer dat product bij de geverifieerde gebruiker hoort; schrijf naartodos; retourneer{ id, title, created_at } - Done when: todo aangemaakt via API met product_id verschijnt in todo-lijst UI gekoppeld aan het juiste product; lege titel of ontbrekend product_id geeft 400; onbekend product geeft 404
- Route Handler; body:
-
ST-410 Story-activiteitenlog UI
- Activiteitenlog sectie in story-detail slide-over; haal
story_logsop via Server Component; render chronologisch; visuele stijl per type (IMPLEMENTATION_PLAN = blauw, TEST_RESULT passed = groen, failed = rood, COMMIT = paars); commit-hash klikbaar alsrepo_urlingesteld; lege staat - Done when: drie log-entries (plan, test, commit) correct gestyled; commit-hash link opent in nieuw tabblad
- Activiteitenlog sectie in story-detail slide-over; haal
M5: Todo-lijst
Herontwerp (april 2026): ST-501–505 beschreven de oorspronkelijke QuickInput-aanpak. Die is geïmplementeerd maar vervangen door een Data Table + detail-kaart ontwerp (ST-509–510). ST-501–505 zijn als referentie bewaard; de functionele eisen zijn ongewijzigd.
-
ST-501 Todo-lijst pagina (vervangen door ST-509)
/todospagina; haal actieve (niet-gearchiveerde) todos op inclusief productnaam; snel-invoerveld bovenaan met product-dropdown (verplicht) en titel (Enter om op te slaan);createTodoServer Action; lege staat met instructie; productnaam-badge per todo-item- Done when: todo aanmaken via Enter persisteert en verschijnt in lijst met productnaam; aanmaken zonder product geblokkeerd; lege staat zichtbaar bij geen todos
-
ST-502 Todo afvinken (vervangen door ST-509)
- Checkbox per todo;
toggleTodoServer Action; afgevinkte todos visueel doorgestreept; afgevinkte todos blijven zichtbaar onderaan de lijst - Done when: afvinken persistent na herlaad; visuele doorstreping correct
- Checkbox per todo;
-
ST-503 Afgevinkte todos archiveren (vervangen door ST-510)
- "Archiveer afgeronde items" knop;
archiveCompletedTodosServer Action; gearchiveerde todos verdwijnen uit standaardweergave - Done when: archiveren verbergt alle afgevinkte todos; telling correct
- "Archiveer afgeronde items" knop;
-
ST-504 Todo promoveren naar PBI (vervangen door ST-510)
- "Promoveer naar PBI" contextmenu of knop per todo; dialoog: product dropdown (actieve producten), prioriteit dropdown; titel vooringevuld (bewerkbaar); bevestigingswaarschuwing;
promoteTodeToPbiServer Action (maak PBI aan, verwijder todo) - Done when: gepromoveerde todo verdwijnt; PBI zichtbaar in juist product met juiste prioriteit
- "Promoveer naar PBI" contextmenu of knop per todo; dialoog: product dropdown (actieve producten), prioriteit dropdown; titel vooringevuld (bewerkbaar); bevestigingswaarschuwing;
-
ST-505 Todo promoveren naar story (vervangen door ST-510)
- "Promoveer naar story" knop per todo; dialoog: product dropdown → PBI dropdown (gefilterd op product), prioriteit; titel vooringevuld;
promoteTodoToStoryServer Action (maak story aan, verwijder todo) - Done when: gepromoveerde todo verdwijnt; story zichtbaar in juist PBI met juiste prioriteit
- "Promoveer naar story" knop per todo; dialoog: product dropdown → PBI dropdown (gefilterd op product), prioriteit; titel vooringevuld;
-
ST-509 Todo Data Table
- Installeer
@tanstack/react-table; voeg shadcndata-table-patroon toe - Kolommen:
- Selectie-checkbox (kolom 1): multi-select voor bulk-archivering; header-checkbox selecteert/deselecteert alle zichtbare rijen
- Titel (kolom 2): max 2 regels,
line-clamp-2 truncate; afgevinkte todos doorgestreept; klik op rij (buiten checkbox) opent detail-kaart - Productnaam-badge (kolom 3)
- Aanmaakdatum (kolom 4)
- Toolbar boven de tabel:
- Product-filter dropdown (Alles / Geen product / per product)
- "+" knop: opent lege detail-kaart voor nieuw aanmaken (erft geselecteerd filter-product)
- "Archiveer geselecteerde (N)" knop: actief zodra ≥ 1 checkbox aangevinkt; roept
archiveSelectedTodosActionaan met de geselecteerde IDs; resettet selectie na afloop
- Paginering: max 10 rijen per pagina; vorige/volgende knoppen; paginatelling ("1–10 van 23")
- Lege staat: "Geen todo's voor deze selectie." bij lege filter; "Nog geen todo's. Gebruik + om er een aan te maken." bij volledig lege lijst
archiveSelectedTodosActiontoevoegen aanactions/todos.ts: valideert dat alle meegegeven IDs bij de ingelogde gebruiker horen vóór schrijven;archiveManyviaupdateMany- Done when: tabel toont alle actieve todos; paginering werkt; product-filter werkt; selectie-checkbox selecteert meerdere rijen; bulk-archiveren verwijdert geselecteerde rijen uit de weergave
- Installeer
-
ST-510 Todo detail-kaart
- Kaart onder de tabel; altijd zichtbaar (leeg als geen todo geselecteerd of aangemaakt wordt)
- Aanmaken: "+" in toolbar zet kaart in aanmaak-modus; velden: product-dropdown (erft filter-product, of "Geen product" bij "Alles"), titel; opslaan via
createTodoAction; na opslaan kaart leegmaken en tabel ververst - Bewerken: klik op tabelrij (buiten checkbox) laadt todo in kaart; velden: product-dropdown, titel, done-toggle; opslaan via nieuwe
updateTodoAction(title + product_id + done); annuleren deselecteert rij en leegt kaart - Promoveren: knoppen "→ PBI" en "→ Story" in de kaart; openen de bestaande
PromotePbiDialogenPromoteStoryDialog; alleen zichtbaar bij een bestaande geselecteerde todo - Demo-modus: kaart-invoervelden uitgeschakeld; knoppen verborgen of disabled
updateTodoActiontoevoegen aanactions/todos.ts: valideert eigenaarschap; pasttitle,product_iden/ofdoneaan;revalidatePath('/todos')- Done when: aanmaken via kaart persisteert; bewerken van titel, product en done-status werkt; promote vanuit kaart opent juist dialoog en verwijdert todo na bevestiging; kaart leeg bij geen selectie; demo-gebruiker ziet uitgeschakelde kaart
-
ST-506 Rolbeheer in instellingen
/settingspagina met roltoewijzing (checkbox per rol: Product Owner, Scrum Master, Developer); minimaal één rol verplicht;updateRolesServer Action; geselecteerde rollen zichtbaar in profielbalk- Done when: rollen bijwerken persisterend; profielbalk toont gekozen rollen; uitvinken van alle rollen geeft validatiefout
-
ST-507 Gebruikersprofiel (buiten originele backlog toegevoegd)
- Profielfoto-upload (JPEG/PNG/WebP, max 12 MB), server-side resizing naar max 700×700 WebP met Sharp, opgeslagen als bytea in Neon; bio (max 160) en bio_detail (max 2000) als aparte velden;
POST /api/profile/avatar+GET /api/profile/avatar+updateProfileAction - Done when: foto geüpload en zichtbaar in instellingen; bio opgeslagen; ongeldige bestanden geweigerd vóór verwerking
- Profielfoto-upload (JPEG/PNG/WebP, max 12 MB), server-side resizing naar max 700×700 WebP met Sharp, opgeslagen als bytea in Neon; bio (max 160) en bio_detail (max 2000) als aparte velden;
-
ST-508 Product Backlog-overzicht in instellingen (buiten originele backlog toegevoegd)
- Gecombineerde lijst op
/settingsvan eigen producten (badge "Eigenaar") en team-lidmaatschappen (badge "Developer" + eigenaarsnaam); klikbaar naar product; "Verlaten"-knop met bevestiging voor lidmaatschappen; lege staat met CTA - Done when: eigenaar-producten en team-producten zichtbaar in één lijst; verlaten werkt en verwijdert rij
- Gecombineerde lijst op
-
ST-511 Entity codes voor Product, PBI en Story (buiten originele backlog toegevoegd)
- Schema:
code String? @db.VarChar(30)opProduct,PbienStory; unique per parent (user_idvoor Product,product_idvoor Pbi/Story);Taskheeft geen DB-veld — code wordt afgeleid als${story.code}.${index_in_story} - Auto-generatie:
lib/code-server.tsmetgenerateNextStoryCode(ST-001,ST-002, … 3-cijferig per product) engenerateNextPbiCode(PBI-1,PBI-2, … per product);createWithCodeRetry-helper vangt P2002 op het code-veld op en probeert max 3× opnieuw zodat gelijktijdige inserts niet crashen - Validatie: Zod max 30 tekens, regex
^[A-Za-z0-9._-]+$; handmatige override mag elk format dat aan de basis-regex voldoet (geen format-enforcement opST-NNN) - Forms: code-input op Product/Pbi/Story dialogen; auto-default zichtbaar als placeholder
auto; field-level error-rendering onder code-input voor zowel create- als edit-mode (uniciteits-conflict, ongeldig format) - Display:
CodeBadge(components/shared/code-badge.tsx) consistent op dashboard product-list, PBI-list, story-blocks (Product Backlog), sprint board (alle drie panelen incl. PBI-headers), solo-bord task-cards, task-detail-dialoog, sprint-afronden-dialoog en de story-dialoog title; task-card toont derived${story.code}.${index}-badge rechtsboven uitgelijnd - Seed: parser strip
ST-XXX:-prefix uit titles, vultcodeapart; productScrum4Mekrijgtcode: 'SCRUM4ME', milestones krijgenM0/M3.5/etc., stories krijgenST-001…ST-612 - Done when: auto-toegekende codes per product oplopend en uniek; race-conflict wordt opgevangen door retry-helper i.p.v. te crashen; handmatige duplicate code toont inline error onder de input in zowel create- als edit-mode; codes zichtbaar als badge in alle lijsten/cards/dialogen; seed verdeelt codes correct (8 PBI's met
M*, 84 stories metST-NNN)
- Schema:
-
ST-512 REST API uitbreidingen voor codes, todo-description en task implementation_plan (buiten originele backlog toegevoegd)
GET /api/products: voegcodetoe (naastid,name,repo_url); optioneeldescriptionendefinition_of_doneGET /api/products/:id/next-story: voegcodetoe op story; voeg per taskcode(derived${story.code}.${index_in_story}) enimplementation_plantoeGET /api/sprints/:id/tasks: voegdescription,implementation_planenstory_codetoe per task; voeg een derivedcode-veld per task toe (${story.code}.${index_in_story})POST /api/todos: accepteer optioneledescription(max 2000 tekens); valideer en sla op; retourneerdescriptionin response- Done when: alle vier endpoints retourneren / accepteren de nieuwe velden zoals beschreven; curl-test toont
codeop products, story en tasks; todo aanmaken via API metdescriptionslaat op
-
ST-513 REST API hardening voor Claude Code (buiten originele backlog toegevoegd)
- Health: nieuwe
GET /api/healthzonder auth; retourneert{ status, version, time }; optioneel?db=1voor DB-ping ({ database: 'ok' | 'down' }) - Claude-context: nieuwe
GET /api/products/:id/claude-context(auth) die in één callproduct,active_sprint,next_story(met tasks), enopen_todosvan de gebruiker terugbrengt — voorkomt round-trips - Status-case op API-boundary: nieuwe
lib/task-status.tsmapper; API exposeert lowercase (todo/in_progress/review/donevoor tasks;open/in_sprint/donevoor stories); DB blijft UPPER_SNAKE; UI ongewijzigd PATCH /api/tasks/:id: accepteert lowercasestatusvia mapper; retourneert lowercase- Story-log metadata: nieuwe optionele
metadata Json?kolom opStoryLog;POST /api/stories/:id/logaccepteert per type een optioneelmetadata-veld (bv.{ branch: 'feat/x' }); bestaande velden ongewijzigd → backwards-compatible - Foutcodes: Zod-validatie geeft
422(was400);400blijft voor malformed body;401/403/404/500ongewijzigd - API-documentatie: nieuwe
docs/API.mdmet endpoints, request/response, foutcodes, status-enums en curl-voorbeelden;CLAUDE.mdverwijst ernaar - Done when:
curl /api/healthwerkt zonder auth;curl /api/products/:id/claude-contextretourneert bundled JSON; PATCH/PUT routes accepteren lowercase status en geven 422 bij ongeldige body; story-log POST bewaartmetadata;docs/API.mdis gepubliceerd GET /api/products: voegcodetoe (naastid,name,repo_url); optioneeldescriptionendefinition_of_doneGET /api/products/:id/next-story: voegcodetoe op story; voeg per taskcode(derived${story.code}.${index_in_story}) enimplementation_plantoeGET /api/sprints/:id/tasks: voegdescription,implementation_planenstory_codetoe per task; voeg een derivedcode-veld per task toe (${story.code}.${index_in_story})POST /api/todos: accepteer optioneledescription(max 2000 tekens); valideer en sla op; retourneerdescriptionin response- Backwards-compat: alle wijzigingen zijn additief — bestaande clients negeren onbekende keys; nieuwe input-velden zijn optioneel
- Done when: alle vier endpoints retourneren / accepteren de nieuwe velden zoals beschreven; curl-test toont
codeop products, story en tasks; todo aanmaken via API metdescriptionslaat op
- Health: nieuwe
M6: Polish & Launch-ready
-
ST-601 Loading states en skeletons
loading.tsxvoor alle zware routes (/products/[id],/sprint,/sprint/planning); skeletoncomponenten voor PBI-lijst, story-blokken en takenlijst; pending states op alle form-submit-knoppen viauseFormStatus- Done when: navigeren naar een trage route toont skeleton; submit-knoppen disablen tijdens Server Action
-
ST-602 Error boundaries
error.tsxvoor alle beschermde routes; toon gebruiksvriendelijke foutmelding met "Probeer opnieuw" knop; log fout naar console (Sentry in M6)- Done when: gesimuleerde Server Action-fout toont error boundary zonder witte pagina
-
ST-603 Toast-notificaties (Sonner)
- Installeer Sonner; success-toast na aanmaken/bewerken/verwijderen van producten, PBI's, stories, taken, todos; error-toast bij mislukte Server Actions; toast niet bij drag-and-drop (te frequent)
- Done when: aanmaken van PBI toont success-toast; gesimuleerde netwerk-fout toont error-toast
-
ST-604 Demo-gebruiker write-protection in UI
- Alle aanmaak-, bewerk- en verwijderknoppen disabled + tooltip "Niet beschikbaar in demo-modus" voor demo-sessies; gebaseerd op
isDemoin sessie - Done when: demo-gebruiker ziet alle knoppen maar kan niets wijzigen; tooltip zichtbaar bij hover
- Alle aanmaak-, bewerk- en verwijderknoppen disabled + tooltip "Niet beschikbaar in demo-modus" voor demo-sessies; gebaseerd op
-
ST-605 Keyboard-navigatie
- Tab-volgorde logisch in alle formulieren; Enter submits formulieren; Escape sluit modals/slide-overs; dnd-kit keyboard-drag (Space om te pakken, pijltjestoetsen, Space om te laten vallen)
- Done when: volledige PBI aanmaken-flow keyboard-only uitvoerbaar; dnd-kit drag via keyboard werkt
-
ST-606 Desktop-first UI-review
- Test alle flows op 1280px, 1440px en 1920px breedte; fix overflow, uitlijning en proportie-issues; controleer minimum schermbreedte 1024px (toon melding bij smaller)
- Done when: alle M0–M5 flows correct op drie schermbreedtes; melding bij < 1024px
-
ST-607 Toegankelijkheid (WCAG AA)
- Kleurcontrast-check op alle tekst en badges; aria-labels op icon-only knoppen; focus-ring zichtbaar op alle interactieve elementen;
roleenaria-selectedop geselecteerde PBI in linkerpaneel - Done when: geen WCAG AA contrastfouten op primaire flows; alle knoppen hebben toegankelijke labels
- Kleurcontrast-check op alle tekst en badges; aria-labels op icon-only knoppen; focus-ring zichtbaar op alle interactieve elementen;
-
ST-608 Ratelimiting op auth-endpoints
- Max. 10 inlogpogingen per IP per minuut; max. 5 registraties per IP per uur; implementeer via in-memory counter (v1) of Vercel Edge middleware
- Done when: 11 snelle inlogpogingen leiden tot 429-respons met duidelijke melding
-
ST-609 Beveiligingsreview API-endpoints
- Controleer alle Route Handlers: elke schrijfoperatie valideert dat de resource binnen de toegankelijke product-scope valt; cross-scope reads zijn onmogelijk tenzij de gebruiker via
product_membersgekoppeld is; voeg integratietests toe die cross-user toegang testen - Done when: poging om een niet-gedeeld product van een andere gebruiker op te halen via API geeft 403 of 404; gedeelde producten zijn wel zichtbaar; getest met twee test-gebruikers
- Controleer alle Route Handlers: elke schrijfoperatie valideert dat de resource binnen de toegankelijke product-scope valt; cross-scope reads zijn onmogelijk tenzij de gebruiker via
-
ST-610 CI/CD via GitHub Actions
- Workflow:
lint(ESLint),typecheck(tsc --noEmit),prisma validate,build(next build) op elke PR en push naar main; Vercel auto-deploy op main - Done when: een TypeScript-fout in een PR blokkeert merge; succesvolle merge triggert Vercel-deploy
- Workflow:
-
ST-611 README en lokale setup-documentatie
- Schrijf
README.mdmet: wat is Scrum4Me, quickstart lokaal (clone → env → prisma push → seed → dev), cloud deployment (Vercel + Neon stappenplan), API-documentatie (alle 7 endpoints met voorbeelden), Claude Code-integratie uitleg, Vercel Analytics status en directe dependencies zoals Sharp - De in-app landingspagina (
/) bevat al een gebruikershandleiding, Scrum-samenvatting en API-overzicht — de README richt zich op lokale setup en deployment - Done when: iemand zonder context de app lokaal kan draaien op basis van alleen de README en
.env.example
- Schrijf
-
ST-612 End-to-end acceptatietest
- Voer handmatig de volledige Lars-flow uit: product aanmaken → PBI's en stories aanmaken → Sprint starten → stories slepen → taken aanmaken → API-token aanmaken → curl
next-story→ curllog(plan, test, commit) → activiteitenlog controleren in UI - Done when: volledige flow werkt zonder fouten of onverwacht gedrag; alle API-responses correct JSON
- Voer handmatig de volledige Lars-flow uit: product aanmaken → PBI's en stories aanmaken → Sprint starten → stories slepen → taken aanmaken → API-token aanmaken → curl
M7: MCP-server voor Claude Code
Aparte repo: madhura68/scrum4me-mcp. Native Prisma-toegang (geen REST-tussenlaag), stdio-transport, Scrum4Me-schema gevendord als git submodule. Tokens hergebruikt uit api_tokens. v1 is alleen dev-flow tools — geen PBI/sprint-creatie of profielbeheer.
-
ST-701 Repo-skeleton scrum4me-mcp
- npm init, tsconfig strict, .gitignore, MCP SDK 1.29, Prisma 7, zod, tsx; lege
src/index.tsdie op stdio start - Done when:
npx tsx src/index.tsprintrunning on stdiozonder crash;tsc --noEmitslaagt
- npm init, tsconfig strict, .gitignore, MCP SDK 1.29, Prisma 7, zod, tsx; lege
-
ST-702 Schema-sync via git submodule
- Submodule
vendor/scrum4me,scripts/sync-schema.shkopieertschema.prismaen strip degenerator erd-block,npm run prisma:generateals postinstall - Done when:
npm run sync-schema && npm run prisma:generatewerkt op een verse clone
- Submodule
-
ST-703 Auth en Prisma-singleton
src/auth.tsSHA-256 hash vanSCRUM4ME_TOKEN→ lookup inapi_tokens, cached{ userId, isDemo };requireWriteAccess()throwtPermissionDeniedErrorvoor demosrc/prisma.tslazy proxy zodat bootstrap niet crasht zonderDATABASE_URL- Done when: ongeldig token geeft
SCRUM4ME_TOKEN is invalid or revoked; demo-tokens blokkeren writes
-
ST-704 Status-mappers + error-helpers
src/status.tszelfde mappers als RESTlib/task-status.tssrc/errors.tsformatZodError,toolError,toolJson,withToolErrorswrapper- Done when: zod-fouten en
PermissionDeniedworden als gestructureerde MCP-errors teruggegeven
-
ST-705 Read-tools —
health,list_products,get_claude_contexthealthdoetSELECT 1;list_productsmet product-access filter;get_claude_contextbundelt product + active sprint + next story (met tasks) + 50 open todos- Done when: smoke-test tegen live DB groen voor alle drie
-
ST-706 Write-tools tasks —
update_task_status,update_task_plan- Status-input lowercase (
todo|in_progress|review|done), conversie via mapper; access-check via story → product → membership/owner - Done when: niet-eigenaar krijgt 'not accessible'; demo geeft
PERMISSION_DENIED
- Status-input lowercase (
-
ST-707 Log-tools —
log_implementation,log_test_result,log_commit- Append
StoryLogmet juistetype; optioneelmetadataJSONB - Done when: drie logs verschijnen in story-activiteit met
type/status/commit_hash/metadatazoals meegegeven
- Append
-
ST-708
create_todo-tool- Optionele
description(max 2000) enproduct_id(gevalideerd via access-check) - Done when: nieuwe todo verschijnt in
/todosvoor de tokengebruiker
- Optionele
-
ST-709 Prompt
implement_next_story- Workflow:
get_claude_context→ plan → log_implementation → per taskin_progress/done→ tests →log_test_result→log_commit - Done when: prompt zichtbaar in MCP-clients met argument
product_id
- Workflow:
-
ST-710 README + Claude Code-config + smoke-test
- README beschrijft setup, tools-tabel, schema-sync,
~/.claude/mcp_servers.jsonsnippet, risico's scripts/smoke-test.tsvalideert read-tools tegen live DB- Done when: smoke-test groen; MCP Inspector toont 9 tools + 1 prompt
- README beschrijft setup, tools-tabel, schema-sync,
M8: Realtime Solo Paneel
Live updates voor stories en tasks in het Solo Paneel zonder pagina-refresh. Wanneer Claude Code (via MCP), Codex (via REST) of een andere browser-tab een task/story muteert, ziet de gebruiker het binnen 1–2 seconden in zijn kanban-bord.
Transport: Server-Sent Events (Vercel ondersteunt geen stateful WebSockets). Bron: Postgres LISTEN/NOTIFY via row-level triggers op tasks en stories. Eén-richting (server → client) — mutaties blijven via Server Actions/REST/MCP.
Filtering server-side: alleen events binnen de actieve sprint van een product waar de gebruiker eigenaar of lid van is, plus assignee_id == userId (eigen kolommen) of assignee_id IS NULL (claim-lijst).
-
ST-801 Postgres LISTEN/NOTIFY-infrastructuur
- Migratie met
notify_solo_change()-functie +AFTER INSERT/UPDATE/DELETE-triggers optasksenstories; payload bevatop,entity,id,product_id,sprint_id,assignee_id,fields(gewijzigde kolommen) - Done when:
psql $DIRECT_URL -c "LISTEN scrum4me_solo;"toont een payload bij een UI-mutatie
- Migratie met
-
ST-802 SSE-route
/api/realtime/soloapp/api/realtime/solo/route.ts,runtime: 'nodejs',maxDuration: 300; auth via iron-session, query-paramproduct_id, opentpg.ClientopDIRECT_URLmetLISTEN; heartbeat 25s; hard close 240s; in-handler filtering op product/sprint/assignee- Done when:
curl -Nop localhost levert binnen 1s een event op na een task-mutatie via UI
-
ST-803 Client hook
useSoloRealtime(productId)lib/realtime/use-solo-realtime.ts; opentEventSource, exponential backoff reconnect (1s → 30s); Page Visibility API voor pauseren/hervatten; cleanup op unmount- Done when: tab wisselen sluit/opent connectie zichtbaar in DevTools Network
-
ST-804 Solo-store realtime-acties
applyTaskUpdate,applyTaskCreate,applyTaskDelete,applyStoryAssignment,markPending/clearPendingom eigen optimistic-echo te onderdrukken- Done when: unit-test op solo-store met gesimuleerde events laat juiste eindstate zien
-
ST-805 Wire-up in SoloBoard + UI-indicator
components/solo/solo-board.tsxroept de hook aan; klein "live"/"verbinden..."-statusindicator; toast bij langer dan 5s disconnected- Done when: twee tabs van Solo Paneel — mutatie in tab A komt binnen 1–2s in tab B zonder refresh
-
ST-806 Documentatie + acceptatietest
- Sectie "Realtime updates" in
docs/scrum4me-architecture.mdmet diagram en filtering-regels; vermelding inCLAUDE.md; korte note over/api/realtime/soloindocs/API.md; handmatig E2E-scenario's gedraaid (zelfde gebruiker twee tabs, MCP-write, REST-write, story-claim, network-flap) - Done when: alle scenario's lopen door zonder onverwachte gedragingen
- Sectie "Realtime updates" in
Volledig plan in .Plans/2026-04-27-m8-realtime-solo.md (lokaal, niet gecommit).
v2 Backlog (na MVP)
- Uitnodigingsflow voor teams — e-mailuitnodiging of link-gebaseerd; nu kunnen alleen admins met toegang tot het systeem Developers toevoegen via gebruikersnaam
- Daily Scrum scherm — voortgang per story bijhouden tijdens Sprint
- Sprint Review scherm — demo en feedback vastleggen per story
- Sprint Retrospective scherm — reflectie per Sprint
- Automatische story-statusupdate na commit via API
- Velocity tracking — statistieken over meerdere Sprints
- Definition of Done per product configureerbaar (nu vaste instelling)
- Notificaties / reminders
- Timeline / kalenderweergave per Sprint
- Integratie GitHub Issues / Linear
- Mobiele app — uitsluitend taken afvinken
- Export van Product Backlog en Sprint als markdown of CSV
Definition of MVP Done
- Alle M0–M6 tasks afgerond
- Volledige Lars-flow succesvol doorlopen (ST-612)
- Alle 7 API-endpoints getest via curl (ST-403 t/m ST-409)
- Demo-gebruiker kan inloggen en heeft geen schrijfrechten (ST-604)
- App lokaal opzetbaar via README zonder extra hulp (ST-611)
- CI/CD actief — falende build blokkeert merge (ST-610)
- Beveiligingsreview API geslaagd (ST-609)
- Geen bekende blocker-bugs