Commit graph

53 commits

Author SHA1 Message Date
Janpeter Visser
ff22196714
Sprint: Stories en taken krijgen één voorspelbare volgorde gekoppeld aan hun code; drag-and-drop herordening voor stories/taken verdwijnt, priority wordt puur label. (#201)
* feat(code): add parseCodeNumber helper to lib/code.ts

Pure helper that extracts the trailing numeric sequence from a code string
(ST-007 → 7, T-42 → 42). Non-conforming codes fall back to Number.MAX_SAFE_INTEGER
so they sort to the end. Includes 5 unit tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(tasks): add code field to BacklogTask type and all task selects

Adds `code: string | null` to BacklogTask interface and includes it in
all Prisma task.findMany selects (backlog API, stories tasks API, page
hydration routes). Updates coerceTaskPayload and test fixtures to match.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(sort-order): derive story/task sort_order from parseCodeNumber(code)

All create paths (createStoryAction, saveTask, createTaskAction,
materializeIdeaPlanAction) and code-edit paths (updateStoryAction, saveTask
update) now set sort_order = parseCodeNumber(code) instead of last+1.
Removes stale last-record queries from create paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(sort-order): decouple sprint membership actions from sort_order

createSprintAction and addStoryToSprintAction no longer write sort_order
when adding stories to a sprint. sort_order is derived from code via
parseCodeNumber, so membership should only set sprint_id + status.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(ordering): remove priority from all story/task orderBy

Story- en taak-ordering is nu puur sort_order asc (created_at als
tiebreaker). PBI-ordering (priority + sort_order) blijft ongewijzigd.

Gewijzigd: backlog/route, pbis/stories/route, claude-context/route,
next-story/route, workspace/route, tasks/route, sprint-runs (query +
in-memory sort), solo-workspace-server, page.tsx (app + mobile + sprint),
store compareStory, actions/sprints story-query, next-story test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(dnd): remove drag-and-drop reorder for stories and tasks

- Remove reorderStoriesAction, reorderTasksAction, reorderSprintStoriesAction
- Delete REST route app/api/stories/[id]/tasks/reorder/route.ts
- Remove DnD from backlog story-panel and task-panel (flat list)
- Remove reorder-within-sprint branch from sprint-board-client handleDragEnd
- Switch SortableSprintRow to plain SprintRow using useDraggable (membership drag kept)
- Remove all DnD from task-list (status toggle + edit kept)
- Remove story-order/task-order/sprint-story-order/sprint-task-order mutation types and store handlers
- Remove related tests for deleted reorder route; fix sprint store tests

* feat(backlog): toon code-badge op backlog-taakkaarten

Geeft code={task.code} door aan <BacklogCard> in TaskCard (task-panel.tsx).
BacklogCard rendert de CodeBadge al conditionally — alleen de prop ontbrak.

* feat(migration): backfill story/task sort_order from code numeric suffix

One-time Prisma migration that sets sort_order = trailing numeric part
of code for all existing stories and tasks, consistent with
parseCodeNumber (fallback = Number.MAX_SAFE_INTEGER for non-conforming
codes). PBIs are intentionally excluded.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs+tests(sort-order): update for code-binding order on stories/tasks

- Rewrite docs/patterns/sort-order.md: float-insertion PBI only; story/task
  sort_order = parseCodeNumber(code), never drag/membership mutated
- Update plan-to-pbi-flow.md: sort_order auto, sprint_id param, priority=label
- Update make-plan.md: priority=label, array order = execution order
- Update rest-contract.md: fix sprint-tasks ordering, remove reorder endpoint
- Add ADR-0011: code is bindende volgordesleutel voor stories/taken
- Regenerate docs/INDEX.md via npm run docs
- Remove reorderStoriesAction/reorderTasksAction mocks from backlog tests
- Remove dnd-kit mocks from task-panel test (panel no longer uses dnd)
- Extend materializeIdeaPlanAction test: assert sort_order=parseCodeNumber(code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 19:02:36 +02:00
Janpeter Visser
b6249a41c0
feat(PBI-67): IDEA_REVIEW_PLAN + geconsolideerde WIP-fixes (#203)
* fix(ci): docs:check-links groen — exclude docs/old/ + archiveer stale plans

CI faalde sinds #191 (docs cleanup) op pre-existing broken links:
- docs/old/ bevat archief-docs met by-design stale paden
- docs/plans/PBI-79*, M9*, M11* hadden geprojecteerde paden naar
  ../backlog/index.md (verplaatst naar docs/old/backlog/) en naar
  app-bestanden die nooit met de juiste relatieve prefix waren geschreven
- docs/adr/0000* verwees naar docs-restructure-ai-lookup.md (verplaatst)
- docs/glossary.md verwees naar /docs/backlog/index.md (verplaatst)

Fixes:
- scripts/check-doc-links.mjs: skip docs/old/ recursief
- Move docs/plans/{PBI-79,M9,M11}*.md → docs/old/plans/ (allemaal merged PBIs;
  plans waren historisch)
- docs/adr/0000-record-architecture-decisions.md: update pad naar archief
- docs/glossary.md: verwijder dode "backlog index"-link

Verificatie: `npm run docs:check-links` → ✓ All doc links valid (105 files)

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

* fix(sprint-conflicts): stories uit CLOSED/ARCHIVED/FAILED sprints zijn weer eligible

Bug: bij sprint-aanmaken (en story-toevoegen aan een actieve sprint) gaf de
backend "Geen eligible stories voor deze sprint" zodra je stories aanvinkte
die ooit in een sprint hadden gezeten — ook als die sprint allang gesloten
of gearchiveerd was. partitionByEligibility checkte alleen story.sprint_id,
nooit sprint.status, terwijl getBlockingSprintMap in dezelfde file wél al
filterde op sprint: { status: 'OPEN' }. Inconsistent.

Fix: partitionByEligibility en isEligibleForSprint wegen nu sprint.status
mee. Een story blokkeert alleen als hij in een ANDERE sprint zit DIE NOG
OPEN is. Stories uit CLOSED/ARCHIVED/FAILED sprints worden weer vrij voor
planning — story.sprint_id blijft als historische referentie staan tot de
volgende updateMany hem overschrijft naar de nieuwe sprint.

Neveneffect: een DONE story in een gesloten sprint krijgt nu reason='DONE'
i.p.v. het misleidende reason='IN_OTHER_SPRINT'.

Tests: 3 nieuwe scenario's in __tests__/lib/sprint-conflicts.test.ts
(CLOSED/ARCHIVED/FAILED → eligible, DONE-in-CLOSED → reason=DONE).
De oude test 'does NOT mark crossSprint for stories in CLOSED other sprint'
is vervangen omdat hij het bug-gedrag vastlegde.

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

* test(sprint-switcher): repareer mock om CI te unblocken

Twee pre-existing mock-bugs die op main al rood waren maar geen gevolgen
hadden tot de CI-monitor erop sloeg in deze PR:

1. Mock-state miste `entities.settings`. Sinds PBI-79 (commit d587be2)
   selecteert SprintSwitcher ook `s.entities.settings.workflow?.pendingSprintDraft?.[productId]?.goal`,
   maar de testmock leverde alleen `{ context }`. → undefined-crash op
   `entities.settings` reading.

2. Mock factory exporteerde alleen `setActiveSprintAction`, maar de
   productie roept `switchActiveSprintAction` aan. Door `vi.mock` werden
   alle andere exports `undefined`, waardoor `actionMock` nooit kon
   triggeren.

Out-of-scope-fix t.o.v. de sprint-eligibility-fix in dit PR — apart commit
zodat reviewer dit als losse cleanup kan zien. CI is nu groen lokaal:
3/3 sprint-switcher tests + 839/839 full suite.

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

* feat(ideas): upload-plan knop — short-circuit van Make-Plan AI-flow

Voegt een 'Upload plan' knop toe in idea-row-actions (verschijnt in zowel
list als idea-detail). Klik → file picker → kies .md → server-side parse +
opslaan; idea-status springt naar PLAN_READY. Vandaaruit de bestaande
'Maak PBI' knop voor materialize.

Server (uploadPlanMdAction):
- Toegestaan vanuit DRAFT, GRILLED, PLAN_FAILED, PLAN_READY
- DRAFT → skip-grill: status gaat direct naar PLAN_READY
- PLAN_READY overschrijft het bestaande plan (consistent met
  updatePlanMdAction, geen confirmation)
- Geblokkeerd in GRILLING/PLANNING (job loopt), PLANNED (al gematerialiseerd)
- Parse-failure → 422 + details (NIET opslaan, zodat een onparseerbaar plan
  nooit in de DB belandt)
- Empty / >100k chars → 422
- Schrijft IdeaLog NOTE met from_status + length
- Rate-limit + demo-guard + ownership-check via loadOwnedIdea (zelfde
  patroon als updatePlanMdAction)

UI (idea-row-actions.tsx):
- Hidden <input type=file accept=".md,.markdown,text/markdown,text/plain">
- FileReader → text → action
- Toast bij success + router.refresh()
- Blocked-tooltip in andere statussen

Tests: 10 nieuwe in __tests__/actions/ideas-crud.test.ts dekkend voor:
happy paths (DRAFT/GRILLED/PLAN_READY-overwrite/PLAN_FAILED), blocks
(PLANNED/GRILLING), validation (empty/oversized/parse-fail), 404.
Full suite groen: 849/849.

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

* Add reviews for Bootstrap-wizard plans v3.2 to v3.4

- Review v3.2: Addressed executor model, fire-and-forget issues, and PAT handling.
- Review v3.3: Improved transaction handling, stale recovery, and ID generation.
- Review v3.4: Finalized GitHub permissions, catalog versioning, and E2E verification queries.
- Updated recommendations for each version to enhance implementation readiness.

* fix(ideas): respecteer YAML-volgorde bij plan-materialize

Tasks erven nu story-priority i.p.v. eigen task.priority bij
materializeIdeaPlanAction. Worker sorteert op `priority ASC, sort_order ASC`;
gemixte task-priorities binnen één story zouden anders de YAML-volgorde
verstoren (e.g. tasks met priority 1/1/1/2/1/2 → worker-volgorde 1,2,3,5,4,6
i.p.v. 1,2,3,4,5,6).

- actions/ideas.ts: priority = s.priority bij task-create
- lib/schemas/idea.ts: task.priority optional (geaccepteerd, genegeerd)
- lib/idea-prompts/make-plan.md: documenteer dat task.priority genegeerd wordt
- __tests__/lib/idea-schemas.test.ts: test dat omitted task.priority slaagt

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

* docs(make-plan): documenteer backtick-format voor implementation_plan-paden

verify_task_against_plan extraheert paden uit implementation_plan via twee
regex-patronen (backticks + bullet). Paden inline in genummerde tekst-stappen
worden niet herkend → planPaths.length=0 → bij diff >50 regels DIVERGENT.

Voeg sectie "Bestandspaden in implementation_plan — verplicht format" toe
die uitlegt welke formats werken (backticks, bullets) en welke niet (inline).
Plus verband met verify_required-keuze: default ALIGNED_OR_PARTIAL behouden
voor ADR-stubs en multi-file edits.

Voorkomt herhaling van T-963 cancelled_by_self-symptoom waar implementatie
slaagde maar verifier DIVERGENT teruggaf.

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

* docs(plans): M8 bootstrap-wizard upload-variant v1.4 — backtick-paden

Upload-variant van het volledige technische plan (docs/plans/M8-bootstrap-wizard.md),
bedoeld voor de "Upload plan"-functie. Genereert 1 PBI + 4 Stories + 22 Tasks
via materializeIdeaPlanAction.

v1.4-aanpassingen tov eerdere generatie-iteratie:
- Alle bestandspaden in implementation_plan in backticks (path-extractor matchen)
- Expliciete "Bestanden:" blok per task vóór de stappen
- Alle tasks op verify_required: ALIGNED_OR_PARTIAL (was deels ALIGNED — te strict
  voor ADR-stubs en multi-file edits)

Fixt forward-only: T-963 cancelled_by_self door DIVERGENT verifier-verdict.
Re-upload van dit bestand produceert tasks die door verify_task_against_plan
als ALIGNED of PARTIAL geclassificeerd kunnen worden.

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

* PBI-67: Add review-plan support to Idea model and job config

- Add plan_review_log and reviewed_at fields to Idea model
- Add REVIEWING_PLAN, PLAN_REVIEW_FAILED, PLAN_REVIEWED to IdeaStatus enum
- Add IDEA_REVIEW_PLAN to ClaudeJobKind enum
- Add IDEA_REVIEW_PLAN config to job-config.ts with model=opus, thinking_budget=6000
- Create migration record for schema changes (applied via db push)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* PBI-67 Phase 2: Add update-idea-plan-reviewed MCP tool

- Create src/tools/update-idea-plan-reviewed.ts: saves review-log and transitions idea status to PLAN_REVIEWED
- Add PLAN_REVIEW_RESULT to IdeaLogType enum (both repos)
- Register tool in src/index.ts
- Update Prisma schemas (both repos): add plan_review_log and reviewed_at fields to Idea model
- Add REVIEWING_PLAN, PLAN_REVIEW_FAILED, PLAN_REVIEWED to IdeaStatus enum (MCP schema)
- Add IDEA_REVIEW_PLAN to ClaudeJobKind enum (MCP schema)
- Tool includes transaction safety and convergence metrics logging

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* feat(PBI-67): IDEA_REVIEW_PLAN Phases 3-6 — server actions, UI components, prompt & tests

- Phase 3: startReviewPlanJobAction, cancelIdeaJobAction, status transitions
  (REVIEWING_PLAN / PLAN_REVIEWED / PLAN_REVIEW_FAILED), status colors,
  job-card/jobs-column filters, idea-list status tabs
- Phase 4: review-plan-job.md prompt (multi-model orchestration with codex
  injection + active plan revision via update_idea_plan_md after each round),
  runbook, 13 unit tests
- Phase 5: ReviewLogViewer component (rounds, convergence, approval, issues),
  idea-detail integration, proper ReviewLog TypeScript types exported from component
- Phase 6.1: wait-for-job discriminator wired (IDEA_REVIEW_PLAN), plan-revision
  step made mandatory in prompt (was previously optional/missing)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 17:59:27 +02:00
Janpeter Visser
1de872298d
docs(plans): PBI-84 plan — code als bindende volgorde voor stories & taken (#202)
Goedgekeurd plan voor PBI-84: code wordt de bindende sorteersleutel voor
stories/taken, drag-and-drop herordening verdwijnt. Herzien na multi-model
review (P0/P1/P2) + onderzoek van het plan->onderdelen-mechanisme.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 16:30:53 +02:00
Janpeter Visser
d84cdf664f
feat(PBI-67): IDEA_REVIEW_PLAN — iterative multi-model plan review (#199)
* feat(ideas): upload-plan knop — short-circuit van Make-Plan AI-flow

Voegt een 'Upload plan' knop toe in idea-row-actions (verschijnt in zowel
list als idea-detail). Klik → file picker → kies .md → server-side parse +
opslaan; idea-status springt naar PLAN_READY. Vandaaruit de bestaande
'Maak PBI' knop voor materialize.

Server (uploadPlanMdAction):
- Toegestaan vanuit DRAFT, GRILLED, PLAN_FAILED, PLAN_READY
- DRAFT → skip-grill: status gaat direct naar PLAN_READY
- PLAN_READY overschrijft het bestaande plan (consistent met
  updatePlanMdAction, geen confirmation)
- Geblokkeerd in GRILLING/PLANNING (job loopt), PLANNED (al gematerialiseerd)
- Parse-failure → 422 + details (NIET opslaan, zodat een onparseerbaar plan
  nooit in de DB belandt)
- Empty / >100k chars → 422
- Schrijft IdeaLog NOTE met from_status + length
- Rate-limit + demo-guard + ownership-check via loadOwnedIdea (zelfde
  patroon als updatePlanMdAction)

UI (idea-row-actions.tsx):
- Hidden <input type=file accept=".md,.markdown,text/markdown,text/plain">
- FileReader → text → action
- Toast bij success + router.refresh()
- Blocked-tooltip in andere statussen

Tests: 10 nieuwe in __tests__/actions/ideas-crud.test.ts dekkend voor:
happy paths (DRAFT/GRILLED/PLAN_READY-overwrite/PLAN_FAILED), blocks
(PLANNED/GRILLING), validation (empty/oversized/parse-fail), 404.
Full suite groen: 849/849.

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

* Add reviews for Bootstrap-wizard plans v3.2 to v3.4

- Review v3.2: Addressed executor model, fire-and-forget issues, and PAT handling.
- Review v3.3: Improved transaction handling, stale recovery, and ID generation.
- Review v3.4: Finalized GitHub permissions, catalog versioning, and E2E verification queries.
- Updated recommendations for each version to enhance implementation readiness.

* docs(plans): M8 bootstrap-wizard upload-variant v1.4 — backtick-paden

Upload-variant van het volledige technische plan (docs/plans/M8-bootstrap-wizard.md),
bedoeld voor de "Upload plan"-functie. Genereert 1 PBI + 4 Stories + 22 Tasks
via materializeIdeaPlanAction.

v1.4-aanpassingen tov eerdere generatie-iteratie:
- Alle bestandspaden in implementation_plan in backticks (path-extractor matchen)
- Expliciete "Bestanden:" blok per task vóór de stappen
- Alle tasks op verify_required: ALIGNED_OR_PARTIAL (was deels ALIGNED — te strict
  voor ADR-stubs en multi-file edits)

Fixt forward-only: T-963 cancelled_by_self door DIVERGENT verifier-verdict.
Re-upload van dit bestand produceert tasks die door verify_task_against_plan
als ALIGNED of PARTIAL geclassificeerd kunnen worden.

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

* PBI-67: Add review-plan support to Idea model and job config

- Add plan_review_log and reviewed_at fields to Idea model
- Add REVIEWING_PLAN, PLAN_REVIEW_FAILED, PLAN_REVIEWED to IdeaStatus enum
- Add IDEA_REVIEW_PLAN to ClaudeJobKind enum
- Add IDEA_REVIEW_PLAN config to job-config.ts with model=opus, thinking_budget=6000
- Create migration record for schema changes (applied via db push)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* PBI-67 Phase 2: Add update-idea-plan-reviewed MCP tool

- Create src/tools/update-idea-plan-reviewed.ts: saves review-log and transitions idea status to PLAN_REVIEWED
- Add PLAN_REVIEW_RESULT to IdeaLogType enum (both repos)
- Register tool in src/index.ts
- Update Prisma schemas (both repos): add plan_review_log and reviewed_at fields to Idea model
- Add REVIEWING_PLAN, PLAN_REVIEW_FAILED, PLAN_REVIEWED to IdeaStatus enum (MCP schema)
- Add IDEA_REVIEW_PLAN to ClaudeJobKind enum (MCP schema)
- Tool includes transaction safety and convergence metrics logging

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* feat(PBI-67): IDEA_REVIEW_PLAN Phases 3-6 — server actions, UI components, prompt & tests

- Phase 3: startReviewPlanJobAction, cancelIdeaJobAction, status transitions
  (REVIEWING_PLAN / PLAN_REVIEWED / PLAN_REVIEW_FAILED), status colors,
  job-card/jobs-column filters, idea-list status tabs
- Phase 4: review-plan-job.md prompt (multi-model orchestration with codex
  injection + active plan revision via update_idea_plan_md after each round),
  runbook, 13 unit tests
- Phase 5: ReviewLogViewer component (rounds, convergence, approval, issues),
  idea-detail integration, proper ReviewLog TypeScript types exported from component
- Phase 6.1: wait-for-job discriminator wired (IDEA_REVIEW_PLAN), plan-revision
  step made mandatory in prompt (was previously optional/missing)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 03:35:02 +02:00
Janpeter Visser
2b4b5bf719
feat(PBI-80): demo-user mag eigen UI-voorkeuren wijzigen (#194)
* feat(PBI-80): SprintSwitcher demo-fork (ST-1345)

Demo-sessies navigeren bij sprint-wissel direct via router.push, zonder
de geblokkeerde setActiveSprintAction aan te roepen. De server-action
behoudt zijn 403-guard als defense in depth.

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

* feat(PBI-80): NavBar demo-fork + URL-derived actief product (ST-1346)

Demo: product-switch in de NavBar navigeert direct via router.push zonder
setActiveProductAction. Voor de weergave (label + dropdown-highlight +
nav-links) leiden we voor demo de actieve product af uit pathname, zodat
de UI consistent is met de URL — de server-render houdt de seed-default
prop maar die wordt voor demo overschreven.

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

* docs(PBI-80): ADR-0006 addendum + demo-client-state patroon (ST-1347)

ADR-0006 krijgt een "Updated 2026-05-12"-sectie die de PBI-80-uitzondering
documenteert: client-side UI-prefs (filters, sort, layout, scope-keuze) zijn
voor demo toegestaan via in-memory store, terwijl alle data-mutaties three-layer
beschermd blijven. Patroon-doc beschrijft wanneer en hoe `isDemo` te gebruiken
in nieuwe componenten. CLAUDE.md quickref + docs/INDEX.md ge-update.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:03:40 +02:00
Janpeter Visser
2bef1a4c20
fix(ci): docs:check-links groen — exclude docs/old/ + archiveer stale plans (#193)
CI faalde sinds #191 (docs cleanup) op pre-existing broken links:
- docs/old/ bevat archief-docs met by-design stale paden
- docs/plans/PBI-79*, M9*, M11* hadden geprojecteerde paden naar
  ../backlog/index.md (verplaatst naar docs/old/backlog/) en naar
  app-bestanden die nooit met de juiste relatieve prefix waren geschreven
- docs/adr/0000* verwees naar docs-restructure-ai-lookup.md (verplaatst)
- docs/glossary.md verwees naar /docs/backlog/index.md (verplaatst)

Fixes:
- scripts/check-doc-links.mjs: skip docs/old/ recursief
- Move docs/plans/{PBI-79,M9,M11}*.md → docs/old/plans/ (allemaal merged PBIs;
  plans waren historisch)
- docs/adr/0000-record-architecture-decisions.md: update pad naar archief
- docs/glossary.md: verwijder dode "backlog index"-link

Verificatie: `npm run docs:check-links` → ✓ All doc links valid (105 files)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 21:33:47 +02:00
Janpeter Visser
0a842e6841
docs(PBI-12 + /init): sprint-lifecycle runbook + MCP-tools plan + CLAUDE.md fixes (#192)
* docs(PBI-12 T-54): voeg sprint-tools toe aan mcp-integration.md

Documenteert mcp__scrum4me__create_sprint en mcp__scrum4me__update_sprint
onder de Authoring-sectie, met verwijzing naar plan-to-pbi-flow.md voor
de werkwijze.

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

* docs(PBI-12 T-55): promote plan-to-pbi-flow.md naar active

- frontmatter status: draft → active
- ⚠️-tooling-banner verwijderd; tools live sinds adbea3f in scrum4me-mcp
- korte note die naar mcp-integration.md verwijst voor tool-reference

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

* docs(CLAUDE.md): /init improvements (scripts, proxy hardstop, versies)

- Stack-tabel: exacte versies Next.js 16.2, React 19.2, Tailwind v4, Prisma v7.8
- Hardstop bullet voor proxy.ts (géén middleware.ts, demo-write blokkering)
- MCP-sectie: bewuste duplicatie van lib/job-config.ts ↔ scrum4me-mcp toegelicht
- Verificatie-sectie: npm scripts-tabel (dev/test/seed/create-admin/docs/diagrams)
  + Vitest exclude + server-only mock note
- Orientatie-tabel: verwijzing toegevoegd naar docs/runbooks/plan-to-pbi-flow.md
- frontmatter last_updated: 2026-05-11

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

* docs(PBI-12): implementatieplan voor sprint MCP-tools

Plan-file voor de twee tools (create_sprint + update_sprint) die de
sprint-lifecycle uit plan-to-pbi-flow.md ondersteunen. Beslissingen:
- Eén generieke update_sprint (geen losse close/fail/archive tools)
- Géén state-machine validatie (resubmit-mechanisme zit elders)
- Auto-end_date bij CLOSED/FAILED/ARCHIVED
- Cron + create_story sprint-param uit scope (apart later)

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 21:13:06 +02:00
Janpeter Visser
b39c3ec2e1
docs(cleanup): archief verouderde plannen, backlog en root-duplicaten (#191)
* docs(cleanup): archief verouderde plannen, backlog en root-duplicaten

- 6 plans naar docs/old/plans/ (PBI-11/75/78, user-settings-store, Local github setup, lees-de-readme — laatste was verkeerde repo)
- docs/backlog/ naar docs/old/backlog/ (pre-MCP statische registry; live werk loopt via Scrum4Me-MCP)
- 6 root-level duplicaten naar docs/old/ (functional, {pbi,story,task}-dialog, product-backlog, backlog)
- 2 landing plans (niet uitgevoerd) krijgen archived: true frontmatter — blijven op plek maar uit INDEX
- scripts/generate-docs-index.mjs: skip docs/old/** + skip archived: true
- CLAUDE.md: rijen docs/backlog/, docs/plans/<key>-*.md, docs/manual/ weg; Track B-sectie verwijderd
- README.md / CHANGELOG.md / docs/plans/v1-readiness.md: link-fixes naar nieuwe locaties

Verify groen (lint + typecheck + 718 tests). docs/INDEX.md geregenereerd.

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

* docs(cleanup): registreer handmatige verplaatsingen en fix referenties

- 4 plans verplaatst naar docs/old/plans/ (M10-qr-pairing-login, auto-pr-deploy-sync, docs-restructure-ai-lookup, v1-readiness)
- 3 archive-plans verplaatst naar docs/old/plans/ (archive-map nu leeg)
- ST-1114-copilot-reviews + 3 research-docs naar nieuwe docs/Ideas/ map
- Duplicaat docs/old/2026-04-27-m8-realtime-solo.md verwijderd (origineel zit in docs/old/plans/)
- Link-fixes naar nieuwe locaties:
  - CHANGELOG.md → docs/old/plans/v1-readiness.md
  - docs/runbooks/deploy-control.md → docs/old/plans/auto-pr-deploy-sync.md (2x)
  - docs/runbooks/worker-idempotency.md → docs/old/plans/auto-pr-deploy-sync.md
  - docs/plans/docs-restructure-pbi-spec.md → docs/old/plans/docs-restructure-ai-lookup.md (4x text + 2x href)
- docs/INDEX.md geregenereerd (96 docs, was 100)

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:46:00 +02:00
Janpeter Visser
d587be2fb3
feat(PBI-79): Product Backlog sprint-membership via vinkjes (#190)
* feat(PBI-79/ST-1333): active-sprint null-contract + clearActiveSprintAction

- lib/user-settings.ts: activeSprints values nullable in Zod-schema.
  Key-aanwezigheid heeft nu betekenis (key+null = bewust geen sprint;
  key ontbreekt = fallback-cascade).
- lib/active-sprint.ts: nieuwe readStoredActiveSprintState helper +
  resolveActiveSprint respecteert expliciet 'cleared' state zonder fallback.
  clearActiveSprintInSettings schrijft null i.p.v. de key te verwijderen.
- actions/active-sprint.ts: nieuwe clearActiveSprintAction met auth +
  membership-check.
- components/shared/sprint-switcher.tsx: '— Geen actieve sprint —'-optie
  in dropdown, disabled wanneer er geen actieve sprint is.
- Tests: nieuwe active-sprint.test.ts (resolver-paden + clear),
  active-sprint-action.test.ts (action-laag), uitbreiding user-settings.test.ts.

Plan: docs/plans/PBI-79-backlog-sprint-workflow.md

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

* feat(PBI-79/ST-1334): user-settings pendingSprintDraft-slot

- lib/user-settings.ts: nieuw workflow.pendingSprintDraft veld met
  compacte intent-shape (pbiIntent + per-PBI storyOverrides).
- actions/sprint-draft.ts: setPendingSprintDraftAction +
  clearPendingSprintDraftAction met product-membership-check + Zod-validatie.
- stores/user-settings/store.ts: setPendingSprintDraft / clearPendingSprintDraft
  optimistic acties + fine-grained mutators upsertPbiIntent / upsertStoryOverride.
  Sprint-draft actions worden dynamisch geïmporteerd zodat jsdom-tests
  zonder DATABASE_URL niet falen.
- Tests: nieuwe sprint-draft.test.ts (action-laag), uitbreiding
  user-settings store-tests (5 nieuwe cases) en schema-tests (4 cases).

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

* feat(PBI-79/ST-1343): sprint-conflicts helper-library

- lib/sprint-conflicts.ts: drie pure/server-side helpers voor eligibility
  + cross-sprint detectie.
  - isEligibleForSprint(story): sprint_id IS NULL en status != DONE
  - partitionByEligibility(prisma, storyIds, excludeSprintId): split in
    eligible / notEligible / crossSprint met reden per story
  - getBlockingSprintMap(prisma, productId, storyIds, excludeSprintId):
    map storyId → { sprintId, sprintName } voor stories in andere OPEN sprint
- Tests: __tests__/lib/sprint-conflicts.test.ts (16 cases) — alle eligibility
  paden + cross-sprint scoping + CLOSED-sprint filtering.

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

* feat(PBI-79/ST-1335): sprint-membership-summary + cross-sprint-blocks endpoints

Twee nieuwe GET-route handlers, beide verplicht gescoped op pbiIds (geen
product-brede aanroepen).

- app/api/products/[id]/sprint-membership-summary/route.ts
  Response: { [pbiId]: { total, inSprint } } via twee prisma.groupBy calls
  (totaal + binnen actieve sprint). Voor state-B tri-state.

- app/api/products/[id]/cross-sprint-blocks/route.ts
  Response: { [storyId]: { sprintId, sprintName } } voor stories in andere
  OPEN sprints. UX-hint voor disabled-vinkjes; commit-acties blijven
  autoritatief.

Tests: 13 cases dekken happy path, 400 zonder pbiIds, 400 zonder sprintId,
404 zonder product-access, auth-fail, en NOT-clause voor excludeSprintId.

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

* feat(PBI-79/ST-1336): product-workspace sprint-membership slice + selectors

Datalaag voor de vinkje-UI van state A′ en state B.

types.ts:
- PbiSummaryEntry, CrossSprintBlock, SprintMembershipSlice toegevoegd.

store.ts:
- Nieuwe slice `sprintMembership` met pbiSummary, crossSprintBlocks,
  pending: { adds[], removes[] }, loadedSummaryForSprintId.
- Acties: setPbiSummary, setCrossSprintBlocks, toggleStorySprintMembership
  (cancel-out logic), resetSprintMembershipPending, fetchSprintMembershipSummary,
  fetchCrossSprintBlocks.
- hydrateSnapshot reset óók de membership-slice.

selectors.ts:
- selectPbiTriState (aggregate-only zolang stories niet geladen; rekent
  pending mee bij loaded PBI's).
- selectStoryEffectiveInSprint (DB ⊕ pending).
- selectStoryIsBlocked (cross-sprint hint).
- selectIsDirty, selectPendingCount.

Tests: 25 cases in nieuwe sprint-membership.test.ts dekken alle selector-
paden, toggle-cancel-out, fetch-helpers, en pbiId-scoping.

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

* feat(PBI-79/ST-1337): state A′ UI — metadata dialog + sticky banner + PbiList ombouw

UI-laag voor de sprint-definitie-flow (state A′).

Nieuw:
- NewSprintMetadataDialog (stap 1): sprint_goal + optionele dates;
  'Verder' schrijft via useUserSettingsStore.setPendingSprintDraft.
- SprintDefinitionBanner (sticky): toont doel + X PBI's / Y stories teller;
  'Annuleren' → AlertDialog confirm → clearPendingSprintDraft;
  'Sprint aanmaken' nog niet aangesloten (wacht op ST-1339).
- NewSprintTrigger: button in page header die de metadata-dialog opent;
  verbergt zichzelf zolang er al een draft loopt.
- SprintDraftBanner: client-wrapper, rendert banner alleen als draft bestaat.

Wijzigingen:
- lib/user-settings.ts: pendingSprintDraft startAt/endAt → z.string().date().
- PbiList: oude selectionMode + selectedIds + NewSprintDialog vervangen door
  hasDraft-afgeleide A′-mode met tri-state vinkjes; togglen muteert
  upsertPbiIntent('all'|'none') en wist storyOverrides per PBI.
- StoryPanel: in A′-mode toont elke story een cherrypick-checkbox die
  upsertStoryOverride('add'/'remove'/'clear') aanroept; cross-sprint-blocked
  stories krijgen disabled-icoon met sprint-naam tooltip.
- app/(app)/products/[id]/page.tsx: StartSprintButton vervangen door
  NewSprintTrigger; SprintDraftBanner gepositioneerd boven split-pane.

Tests: bestaande tests blijven groen (806 cases) — UI-specifieke component
tests volgen later. ST-1339 sluit createSprintWithSelectionAction aan.

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

* feat(PBI-79/ST-1339): createSprintWithSelectionAction + banner wire-up

actions/sprints.ts:
- Nieuwe createSprintWithSelectionAction(productId, metadata, pbiIntent,
  storyOverrides).
- Server-side intent-resolve:
  1. Voor elke PBI met intent='all': fetch child-story-IDs minus
     storyOverrides[pbi].remove.
  2. Plus storyOverrides[*].add (cross-PBI cherrypick toegestaan).
- Eligibility-filter via partitionByEligibility (sprint_id IS NULL + status
  != DONE; stories in andere OPEN sprint → conflicts.crossSprint).
- Transactie wrapt sprint.create + story.updateMany (status='IN_SPRINT') +
  task.updateMany (sprint_id cascade) — alles atomair.
- setActiveSprintInSettings na success.
- Return: { success, sprintId, affectedStoryIds, affectedPbiIds,
  affectedTaskIds, conflicts: { notEligible, crossSprint } } of error.

components/backlog/sprint-definition-banner.tsx:
- 'Sprint aanmaken'-knop sluit aan op createSprintWithSelectionAction;
  toast bij conflicts, success-toast anders, router.refresh() voor SSR
  cycle. Pending draft wordt door de action zelf nog niet expliciet gewist
  — dat gebeurt via revalidatePath en kan in ST-1340 finetuned worden.

Tests: __tests__/actions/create-sprint-with-selection.test.ts (6 cases)
dekken intent-resolve, override-respect, cross-sprint conflict, transactie-
binding van story.status + task.sprint_id, return-shape, en error-pad.

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

* feat(PBI-79/ST-1340): commitSprintMembershipAction + gerichte client-store patches

actions/sprints.ts:
- Nieuwe commitSprintMembershipAction(activeSprintId, adds[], removes[]).
- Eligibility-filter voor adds via partitionByEligibility (sprint_id IS NULL
  en niet DONE; cross-sprint conflicts → notEligible).
- Race-safety voor removes: alleen stories met huidige sprint_id ==
  activeSprintId; rest → conflicts.alreadyRemoved.
- Transactie wrapt twee updateMany-paren (story status mee, task.sprint_id
  cascade). Update-paren overgeslagen wanneer leeg.
- Return: { success, affectedStoryIds, affectedPbiIds, affectedTaskIds,
  conflicts: { notEligible, alreadyRemoved } }.

stores/product-workspace/store.ts:
- applyMembershipCommitResult({ activeSprintId, addedStoryIds,
  removedStoryIds }) patcht entities.storiesById met juiste sprint_id +
  status; ledigt sprintMembership.pending. Geen task-veld omdat
  BacklogTask geen sprint_id-kolom heeft in de store.

Tests: __tests__/actions/commit-sprint-membership.test.ts (8 cases) — happy
path, DONE-conflict, cross-sprint, race-safety voor removes, transactie-
inhoud (status='IN_SPRINT'/'OPEN'), task-cascade, return-shape, auth-fail.

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

* feat(PBI-79/ST-1338): state B vinkjes-UI + 'Sprint opslaan'-knop met teller

State B (actieve sprint geselecteerd, geen draft) hangt nu aan dezelfde
vinkje-UI als state A′, maar muteert de transient pending-buffer in plaats
van de draft.

- PbiList: nieuwe prop activeSprintId. selectionMode = hasDraft ||
  stateBMode. togglePbiInDraft routeert naar upsertPbiIntent (A′) of bulk-
  toggleStorySprintMembership over eligible child-stories (B, skip blocked).
- StoryPanel: idem prop activeSprintId. StoryBlockWithCherrypick muteert
  draft via upsertStoryOverride in A′ of pending buffer via
  toggleStorySprintMembership in B (cross-sprint blocked = disabled).
- SaveSprintButton (nieuw): client component in page header, alleen
  zichtbaar als er een actieve sprint is. Disabled bij clean buffer,
  enabled met teller bij dirty. Klikken calls commitSprintMembershipAction
  → applyMembershipCommitResult gericht in store + toast bij conflicts.
- page.tsx: activeSprintItem.id wordt doorgegeven aan PbiList, StoryPanel
  en SaveSprintButton.

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

* feat(PBI-79/ST-1341+ST-1342): SprintEditDialog metadata-edit + multi-OPEN sprints

ST-1341 (T-946):
- actions/sprints.ts: nieuwe updateSprintAction(sprintId, fields) — JSON
  input, accepteert optionele goal/startAt/endAt; auth + product-access
  check, prisma.sprint.update, revalidatePath. Type-safe return.
- components/backlog/sprint-edit-dialog.tsx: Entity-Dialog-pattern voor
  metadata-edit van een sprint. Velden: sprint_goal, start_date, end_date.
  Link 'Sprint afronden… →' naar bestaande /products/[id]/sprint/[sprintId]
  zodat de completion-flow (per-story DONE/OPEN beslissing + PBI-promotie)
  niet wordt geduplicereerd. useDirtyCloseGuard.

ST-1342 (T-947):
- actions/sprints.ts: OPEN-uniqueness check in createSprintAction
  verwijderd. Een product mag nu meerdere OPEN sprints tegelijk hebben;
  cross-sprint-conflicts per story worden afgevangen door
  partitionByEligibility in de membership-commit-flow.

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

* test(PBI-79/ST-1344): updateSprintAction regression coverage

Audits van de geplande non-regressie-tests laten zien dat alle invarianten
uit het ST-1344 plan reeds gedekt zijn door eerder toegevoegde tests:

- clearActiveSprintAction null-not-delete → __tests__/lib/active-sprint.test.ts
  + __tests__/actions/active-sprint-action.test.ts
- Endpoints rejecten zonder pbiIds (400) → __tests__/api/sprint-membership-summary.test.ts
  + __tests__/api/cross-sprint-blocks.test.ts
- Status-mutaties story.status=IN_SPRINT/OPEN met task.sprint_id cascade
  in dezelfde transactie → __tests__/actions/create-sprint-with-selection.test.ts
  + __tests__/actions/commit-sprint-membership.test.ts
- Cross-sprint conflicts + DONE-eligibility → __tests__/lib/sprint-conflicts.test.ts

Nieuw: __tests__/actions/update-sprint.test.ts (6 cases) dekt
updateSprintAction die nog geen tests had — goal alleen, dates alleen,
null-clear, 403 zonder access, lege goal weigering, leeg fields-object
weigering.

Handmatige E2E checklist (T-949) blijft staan voor menselijke browser-
validatie tijdens PR-review.

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

* fix(PBI-79): PBI-rij selecteert weer in A′/B-modus; vinkje is aparte trigger

Voor PBI-79 maakte het hele PBI-kaartje in selectionMode (state A′ én B)
de toggle. Daardoor:
- klik op rij = bulk-toggle stories (teller liep op);
- geen setActivePbi, dus StoryPanel kreeg geen content.

Fix: in selectionMode wordt onClick = onSelect (PBI activeren → stories
laden) en de tri-state-iconen verhuizen naar een eigen <button> in de
actions-slot met stopPropagation. Toggle gedrag (bulk add/remove in B,
upsertPbiIntent in A′) blijft ongewijzigd via die knop.

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

* fix(PBI-79): cascade-restore alleen als hint-story bij nieuwe PBI hoort

Bug: setActivePbi reset activeStoryId/activeTaskId, maar het cascade-
restore-pad zette daarna een hint-story actief zonder te valideren of die
story bij de nieuw-geselecteerde PBI hoort. Bij PBI-switch bleef daardoor
de task-kolom de taken van de vorige story tonen.

Fix: alleen setActiveStory(hint) als entities.storiesById[hint].pbi_id ===
pbiId. Bij mismatch blijft activeStoryId null en is de task-kolom leeg
totdat de gebruiker een story uit de nieuwe PBI kiest.

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

* feat(PBI-79): sprint-switch auto-select PBI/story + user-settings persist

Bij sprint-switch wordt de sprint-content server-side opgevraagd. Wanneer
de sprint precies één PBI (en die PBI exact één story binnen de sprint)
heeft, worden PBI en story automatisch geselecteerd. Alle drie keuzes
(sprint, pbi, story) worden atomair in user-settings opgeslagen zodat ze
cross-device blijven hangen.

- lib/user-settings.ts: layout krijgt nullable activePbis +
  activeStories per product.
- lib/active-sprint.ts: setActiveSelectionInSettings schrijft de drie
  keys atomair + notify pg_notify.
- actions/active-sprint.ts: switchActiveSprintAction(productId, sprintId)
  doet de server-side auto-select-resolutie (single PBI → single story)
  en returnt { sprintId, pbiId, storyId }.
- components/shared/sprint-switcher.tsx: handleSwitchSprint roept de
  nieuwe action aan en synchroniseert de workspace-store gelijk zodat
  de UI geen flash krijgt voor de SSR-refresh.
- components/backlog/active-selection-hydrator.tsx (nieuw): client-side
  effect dat user-settings.activePbis/activeStories naar workspace-store
  spiegelt; wint van de localStorage hint-restore.
- app/(app)/products/[id]/page.tsx: ActiveSelectionHydrator gemount
  binnen BacklogHydrationWrapper.

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

* docs(PBI-79): plan-update met implementatie-stand + scope-aanpassing

Documenteert wat er sinds de eerste implementatie-pass is gebeurd:
- Tabel van 14 commits met hun rol.
- Twee bugs die tijdens testen boven kwamen (PBI-rij-klik, cascade-restore).
- Nieuwe feature sprint-switch auto-select (server resolveert single-PBI/
  single-story; user-settings persist).

En kondigt scope-aanpassing aan voor de volgende implementatie-ronde:
- pendingSprintDraft wordt session-only (geen server-persist meer).
- useDirtyCloseGuard wist draft op leave-with-confirm.
- Sprint-switcher krijgt concept-entry zolang er een draft loopt.

De rest van het plan beneden blijft van kracht behalve waar deze sectie
het overruled.

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

* feat(PBI-79): pendingSprintDraft session-only + concept-entry + leave-guard

Scope-aanpassing uit plan-revisie: drafts persisten niet meer server-side.

Wijzigingen:
- stores/user-settings/store.ts:
  - hydrate() strip nu workflow.pendingSprintDraft uit serverstate
    (legacy DB-entries blijven harmless aanwezig maar worden niet
    gehydreerd → effectief unreachable voor de UI).
  - setPendingSprintDraft / clearPendingSprintDraft worden lokale-only;
    geen import van sprint-draft-actions, geen server-roundtrip.
  - upsertPbiIntent / upsertStoryOverride blijven via setPendingSprintDraft
    routeren → ook session-only.
- components/shared/sprint-switcher.tsx: leest draft-goal uit user-settings
  store en toont '⚙ Concept — [goal]' als niet-selecteerbare entry
  bovenaan de dropdown zolang er een draft loopt.
- components/backlog/sprint-draft-leave-guard.tsx (nieuw): registreert
  een beforeunload-listener zolang er een draft is. Browser-refresh,
  tab-close en back-navigatie tonen daarmee de standaard confirm. In-app
  route-changes blijven via de banner-Annuleren-knop lopen.
- app/(app)/products/[id]/page.tsx: SprintDraftLeaveGuard gemount naast
  de banner.
- Tests: user-settings store-tests aangepast (geen server-call assert
  meer, hydrate strip-assert toegevoegd; upsert-tests seed nu via
  setPendingSprintDraft i.p.v. legacy hydrate).

setPendingSprintDraftAction + clearPendingSprintDraftAction blijven bestaan
voor eventuele toekomstige opruim-flows, maar worden niet meer aangeroepen
vanuit de UI.

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

* docs(PBI-79): mark scope-aanpassing afgerond + localStorage overzicht

- Drie open punten uit plan-revisie afgevinkt (commit 2a4ee6a).
- Sectie 'Bewust niet geïmplementeerd': server-persist van manuele
  PBI/story-klikken — op vraag van user nu out-of-scope voor deze PR.
- Tabel localStorage-gebruik in de codebase voor toekomstige referentie.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 18:56:46 +02:00
Janpeter Visser
a1e6ec35e5
feat(PBI-78): cost-analysis widget on insights page (#187)
* feat(PBI-78): cost-analysis data layer (T-902)

- New lib/insights/cost-analysis.ts with 5 query functions:
  getCostKpi, getCostByDay, getCostByModel, getCostByKind, getCacheEfficiency
- Period type: 7d | 30d | 90d | mtd
- Cost formula reused from token-stats.ts (input + output + cache + thinking)
- Cache savings: cache_read_tokens × (input_price - cache_read_price) / 1M
- Plan: docs/plans/PBI-78-cost-analysis-widget.md

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

* feat(PBI-78): cost-analysis UI component (T-903)

- New app/(app)/insights/components/cost-analysis.tsx
- Period selector (7d/30d/90d/MTD) via URL ?period= with useTransition + router.replace
- KPI strip: total cost, avg per day, cache savings, top model
- 2x2 chart grid: daily cost (BarChart), per-model + per-kind (vertical BarCharts), cache efficiency (PieChart)
- Empty state for kpi.jobCount === 0
- Uses MD3 tokens (var(--chart-N), var(--status-done))

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

* feat(PBI-78): wire CostAnalysisCard onto insights page (T-904)

- Parse ?period= from searchParams (default 30d, validates against 7d/30d/90d/mtd)
- Parallel-fetch 5 cost queries via Promise.all alongside existing widgets
- New "Cost analyse" section between Sprint Health and Plan-quality
- Existing TokenUsageCard ("Token gebruik" section) stays as sprint detail

verify (lint+typecheck+test) and build pass.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 12:59:45 +02:00
Janpeter Visser
a0e5867857
feat(PBI-76): user-settings DB-store infrastructure (Phase 0) (#185)
* docs(PBI-76): plan for user-settings DB-store

Persists view/filter prefs in User.settings (Json) instead of
localStorage. SSR-correct hydration, cross-tab sync via
LISTEN/NOTIFY + SSE, cross-device persistence.

Phased: 0=infra, 1=migrate flicker sources, 2=cookie consolidation.

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

* feat(PBI-76): User.settings json column + migration

Adds JSONB column to users table for persistent user prefs.
Idempotent SQL — safe on databases where column already exists.

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

* feat(PBI-76): user-settings types and merge helpers

Zod schema for User.settings shape (views/devTools), deep-merge
helper that replaces arrays and merges nested objects, and a
safe parser that returns defaults on invalid input.

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

* feat(PBI-76): updateUserSettingsAction with notify

Validates patch via Zod, deep-merges with current settings in
a transaction, persists to DB, and emits pg_notify on
scrum4me_changes for cross-tab/cross-device sync. Demo accounts
get 403, unauthenticated 401, invalid input 422.

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

* feat(PBI-76): user-settings zustand store with optimistic flow

Hydrate from prop (SSR-correct), setPref via path with optimistic
update + rollback on server error, applyServerPatch for SSE-driven
cross-tab updates. Demo accounts skip server-write entirely.

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

* feat(PBI-76): SSE route for user-settings

User-scoped /api/realtime/user-settings stream that filters
scrum4me_changes notifications on kind=user_settings and matching
userId. Forwards the patch as a data: event so other tabs can
applyServerPatch without re-fetching settings.

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

* feat(PBI-76): user-settings bridge mounted in app layout

Hydrates the zustand store with the user's persisted settings via
prop (SSR-correct, no flicker). Opens an EventSource to
/api/realtime/user-settings so changes from other tabs/devices
flow into the same store. Demo accounts skip the SSE subscription.

Layout now selects user.settings alongside the other user fields,
no extra DB roundtrip.

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

* test(PBI-76): user-settings lib/action/store coverage

22 vitest cases covering merge semantics (no mutation, array
replace, nested merge), Zod schema strictness, server action
auth/demo/validation paths, and the optimistic store flow
including rollback and demo-mode skip.

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

* chore(PBI-76): sync package-lock to v1.3.3

Lockfile drifted after @prisma/client reinstall during the
schema regenerate. No dependency changes — just the version
field tracking package.json bumped in #184.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 12:44:32 +02:00
Janpeter Visser
a9b53dedf0
feat(PBI-75): sprint task-edit client-side via workspace-store (#183)
Klik op een taak in het sprint-scherm opent de edit-dialog nu
client-side via setActiveTask op de sprint-workspace-store.
Geen URL-navigatie, geen volledige server re-render — alleen
GET /api/tasks/{id} voor het detail. SSE propageert server-saves
automatisch terug.

- TaskDialog: optionele onClose/onSaved callbacks (closePath
  optional gemaakt — backwards compatible)
- SprintTaskDialogMount: nieuwe client-component die
  selectActiveTask consumeert en TaskDialog rendert
- SprintUrlTaskSync: deeplink (?editTask=<id>) → store
- Sprint page: mounts toegevoegd, editTask searchParam +
  EditTaskLoader-Suspense verwijderd
- TaskList.openEditDialog roept setActiveTask aan ipv router.push
- Vitest integratie-test voor SprintTaskDialogMount

Out-of-scope (follow-up PBIs): newTask-flow, mobile, product-backlog.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 08:21:42 +02:00
Janpeter Visser
3b5cee823c
Load/render workspace alignment (#182)
* docs: plan load render workspace alignment

* fix: normalize workspace status hydration

* fix: avoid duplicate backlog hydration load

* refactor: use sprint store active story

* refactor: migrate solo to workspace store

* chore: stabilize verification ignores
2026-05-10 07:34:58 +02:00
Janpeter Visser
98ee05d458
feat(PBI-74): sprint-workspace-store (Story 9) (#181)
* feat(PBI-74): sprint-workspace-store skelet (Story 9 / T-879)

- stores/sprint-workspace/{types,store,selectors,restore}.ts conform
  product-workspace blueprint
- ContextSlice: activeProduct, activeSprintId, activeStoryId, activeTaskId
- EntitiesSlice: sprintsById, storiesById, tasksById
- RelationsSlice: sprintIdsByProduct, storyIdsBySprint, taskIdsByStory
- LoadingSlice met activeRequestId voor race-safe ensure*Loaded
- SyncSlice: realtimeStatus, lastResyncAt, resyncReason
- Realtime applyRealtimeEvent voor sprint/story/task entities + unknown-event
  fallback, parent-move handling, child-cleanup bij D op sprint/story
- Optimistic mutations: sprint-story-order, sprint-task-order, entity-patch
- LocalStorage hints (storage key sprint-workspace-hints) per product/sprint
- 45 unit-tests groen — verplicht 13 cases uit workspace-store.md §Tests

* feat(PBI-74): sprint hydratie + realtime SSE (Story 9 / T-880)

- app/api/realtime/sprint/route.ts: SSE-stream LISTEN/NOTIFY op
  scrum4me_changes, filter entity ∈ {sprint, story, task} per product_id;
  ready-event, heartbeat 25s, hard-close 240s
- lib/realtime/use-sprint-realtime.ts: client-hook met backoff-reconnect;
  ready-cycle telt; geen close op hidden; setRealtimeStatus
- lib/realtime/use-sprint-workspace-resync.ts: visibility + online triggers
  resyncActiveScopes('visible' | 'reconnect')
- components/sprint/sprint-hydration-wrapper.tsx: hydrateSnapshot via
  useEffect met fingerprint-check; mount realtime + resync
- app/(app)/products/[id]/sprint/[sprintId]/page.tsx: wrap SprintBoardClient
  in SprintHydrationWrapper; bouw SprintWorkspaceTask-shape voor
  tasksByStoryWorkspace en SprintHydrationData voor de wrapper

Schaduw-fase: useSprintStore blijft parallel werken in board components
totdat T-881 die migreert en T-883 de oude store opruimt.

* feat(PBI-74): migreer sprint-board componenten naar workspace-store (Story 9 / T-881)

- TaskList: leest tasks via selectTasksForStory met useShallow; DnD via
  applyOptimisticMutation('sprint-task-order') + settle/rollback
- SprintBacklogLeft: leest stories via selectStoriesForActiveSprint met
  useShallow; props 'stories' verwijderd
- SprintBoardClient: leest sprintStories uit selector i.p.v. lokale state;
  add/remove via direct setState met manuele snapshot-rollback;
  reorder via applyOptimisticMutation('sprint-story-order'); assignee-
  change via store entity-mutation; tasksByStory en sprintStoryIdList
  props weg
- app/(app)/.../sprint/[sprintId]/page.tsx: bouwt SprintHydrationData voor
  wrapper; geeft alleen non-store props door aan SprintBoardClient

useSprintStore wordt nergens meer geïmporteerd — alleen comment-referentie
in SprintHydrationWrapper. Cleanup van het bestand zelf in T-883.

Verify groen (671 tests, typecheck, lint clean).

* feat(PBI-74): read-routes voor sprint-workspace + cache-headers (Story 9 / T-882)

- GET /api/products/[id]/sprints — lijst sprints per product
  (ensureProductSprintsLoaded). force-dynamic, productAccessFilter,
  start_date/end_date naar ISO-date string.
- GET /api/sprints/[id]/workspace — sprint snapshot met sprint-meta,
  stories (incl. taskCount/doneCount/assignee), tasks gegroepeerd per
  story (ensureSprintLoaded). force-dynamic, productAccessFilter via
  product, status-vertaling via taskStatusToApi/storyStatusToApi.

Race-safe loaders (activeRequestId-guard), restore-flow (cascade-restore
via writeProductHint/writeSprintHint/writeStoryHint/writeTaskHint),
resync-laag (useSprintWorkspaceResync visibility + online), unknown-event
filter (isUnknownEntityEvent → resyncActiveScopes('unknown-event')) zijn
allemaal in T-879/T-880 al ingebouwd; T-882 sluit het loop met de
ontbrekende API-endpoints + cache-headers (cache: 'no-store' op fetches,
force-dynamic op routes).

* feat(PBI-74): cleanup oude sprint-store (Story 9 / T-883)

- rm stores/sprint-store.ts — alle componenten lezen nu via
  useSprintWorkspaceStore (T-881 voltooide imports-migratie)
- update SprintHydrationWrapper-comment: schaduw-fase referenties
  verwijderd

Verify: 671 tests groen, typecheck clean, build groen.
Grep useSprintStore = 0.

* docs(PBI-74): update Story 9 status in implementatieplan (T-884)

- Frontmatter: ready-to-execute → in-progress; revision 1 → 2;
  last_updated 2026-05-09 → 2026-05-10
- Stories-tabel: kolom Status toegevoegd (Stories 1-8 DONE via PR #180,
  Story 9 met T-884 op review)
- §Story 9: per-taak status + acceptatie-checklist voor T-884 manuele
  staging-checks
- Aanbeveling-blokje: noteert dat Story 9 vroeger gestart is dan het
  ontwerpdoc adviseerde
2026-05-10 06:53:04 +02:00
Janpeter Visser
5df04feb11
feat(PBI-74): Zustand product-workspace rearchitecture (Stories 1-8) (#180)
* feat(PBI-74): product-workspace store skelet + test-infra (Story 1)

Skelet voor de nieuwe `product-workspace-store` die op termijn de gefragmenteerde
`backlog-store`/`planner-store`/`selection-store`/`product-store` vervangt. Deze
PR levert alleen het skelet + tests; UI-consumers worden in latere stories
omgezet.

- vitest naar jsdom + tests/setup.ts (MemoryStorage, default fetch-stub) — G6/G8
- stores/product-workspace/{types,store,selectors,restore}.ts — immer-middleware,
  alle slices en acties (hydrate, setActive*, ensure*Loaded met activeRequestId-
  guard, applyRealtimeEvent, resyncActiveScopes/loadedScopes, optimistic
  mutations). Restore-wiring in setters volgt in Story 4 (T-857/T-858).
- selectors gebruiken module-level EMPTY refs (G1) en documenteren useShallow-
  vereiste (G2)
- 34 nieuwe unit-tests dekken §Testing setup-checklist uit het ontwerp:
  hydrateSnapshot, selection-cascade, applyRealtimeEvent (I/U/D + parent-move +
  ander-product + unknown-entity → resync), delete-cleanup, race-safe loaders,
  ensureTaskLoaded _detail-flag, resyncActiveScopes ensure-keten, restore-hints
  read/write/clear, optimistic mutation rollback/settle/SSE-echo idempotent
- docs/api/rest-contract.md: audit-sectie met de vier ontbrekende
  ensure*Loaded-endpoints (worden toegevoegd in Story 7 / T-870)

Refs: PBI-74, ST-1318, T-837..T-843
Bron-ontwerp: docs/plans/zustand-store-rearchitecture.md
Implementatieplan: docs/plans/zustand-workspace-store-implementation.md

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

* feat(PBI-74): dual-dispatch hydratie + realtime naar workspace-store (Story 2)

Story 2 — schaduw-fase: BacklogHydrationWrapper en useBacklogRealtime voeden
nu ook de nieuwe product-workspace-store, terwijl de oude useBacklogStore /
useProductStore leidend blijft voor componenten. Story 3 verschuift consumers
één voor één; Story 8 ruimt de oude stores op.

- T-844: BacklogHydrationWrapper roept naast useBacklogStore.setInitialData
  ook useProductWorkspaceStore.hydrateSnapshot aan. Productname-prop optioneel
  toegevoegd voor activeProduct-context.
- T-845: useBacklogRealtime onmessage dispatcht events naar zowel oude store
  (applyChange) als nieuwe store (applyRealtimeEvent). Geen wijziging aan
  reconnect/visibility — Story 5.
- T-846: dev-only logWorkspaceFingerprint helper vergelijkt counts tussen
  oude en nieuwe store na hydrate en na elk realtime-event. console.warn bij
  mismatch; opt-in debug log via NEXT_PUBLIC_DEBUG_WORKSPACE_FINGERPRINT=1.
  Bestand TODO-marked voor verwijdering in Story 8 (T-878).
- T-847: SetCurrentProduct schrijft naast oude useProductStore ook
  useProductWorkspaceStore.setActiveProduct({id, name}); cleanup cleart beide.
  setActiveProduct triggert ensureProductLoaded — fetch-stub tot Story 7
  (T-870) de LIST-endpoints toevoegt.

Verify: lint+typecheck clean, 636/636 tests groen (geen UI-regressie omdat
oude store leidend blijft).

Refs: PBI-74, ST-1319, T-844..T-847

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

* feat(PBI-74): migreer backlog-componenten naar workspace-store (Story 3)

Story 3 verplaatst alle UI-consumers van de oude vier stores
(useBacklogStore/usePlannerStore/useSelectionStore/useProductStore) naar de
nieuwe product-workspace-store. De oude stores blijven nog bestaan voor
hydration-wrapper en realtime-hook (dual-dispatch); Story 8 ruimt ze op.

- T-848 backlog-split-pane.tsx: leest activePbiId/activeStoryId uit
  context-slice (primitives, geen useShallow nodig).
- T-849 pbi-list.tsx: selectVisiblePbis(useShallow); DnD via
  applyOptimisticMutation('pbi-order' + optionele 'entity-patch' bij
  cross-priority drag), met settle/rollback per server-result.
- T-850 story-panel.tsx: selectStoriesForActivePbi(useShallow); DnD via
  applyOptimisticMutation('story-order' + entity-patch bij priority change).
- T-851 task-panel.tsx: selectTasksForActiveStory(useShallow); DnD via
  applyOptimisticMutation('task-order'); detail-view (ensureTaskLoaded +
  isDetail) zit in de task-dialog (apart component, niet in deze lijst).
- T-852 start-sprint-button.tsx: selectActivePbi + selectStoriesForActivePbi
  voor free-story count.
- T-853 set-current-product.tsx: alleen workspace-store.setActiveProduct
  (oude useProductStore-import verwijderd).
- T-854 G1/G2-audit: alle nieuwe selectors gebruiken module-level EMPTY
  refs (G1) en useShallow voor lijsten (G2). Geen 'Maximum update depth'-
  warnings tijdens npm test.
- T-855 tests bijgewerkt: backlog-split-pane.test, task-panel.test,
  integration.test gebruiken nu setState op workspace-store (helpers
  resetWorkspace/setActiveStoryAndTasks/selectPbi/selectStory).

Verify: lint+typecheck clean, 636/636 tests groen. UI-consumers van
oude stores zijn nu nul (uitgezonderd dual-dispatch in hydration-wrapper en
realtime-hook + dev-fingerprint-helper, die in Story 8/T-873/T-878 verdwijnen).

Refs: PBI-74, ST-1320, T-848..T-855

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

* feat(PBI-74): race-safe loaders + restore-hints + URL-prioriteit (Story 4)

- T-856: activeRequestId-guard zat al in store.ts uit Story 1; bevestigd door
  de race-safety test (in-flight ensurePbiLoaded mag niet overschrijven).
- T-857: restore-hint flow toegevoegd in setActiveProduct/setActivePbi/
  setActiveStory. Async chain: await ensureXxxLoaded → guard check →
  readHints → valideer hint via entities.byId → setActiveYyy(hint).
  Geen setTimeout-trick — chain is alleen await-based.
- T-858: writeProductHint/writePbiHint/writeStoryHint/writeTaskHint
  aangeroepen direct na set(...) zodat de hint-persistentie altijd
  consistent is met de in-store selectie.
- T-859: nieuwe components/backlog/url-task-sync.tsx — leest
  ?editTask=&lt;id&gt; uit useSearchParams, schrijft de hint en roept
  setActiveTask aan zodat de URL wint boven een eerder gepersisteerde
  task-hint. Gemount in beide product-pages (desktop + mobile) binnen
  BacklogHydrationWrapper.
- T-860: 6 nieuwe vitest-cases — 4 voor hint-persist per setter, 2 voor de
  restore-flow chain (hint die niet in entities zit wordt genegeerd; hint
  die wel in entities zit wordt toegepast). Bestaande race-safety test
  blijft groen.

Verify: lint+typecheck clean, 642/642 tests groen.

Refs: PBI-74, ST-1321, T-856..T-860

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

* feat(PBI-74): hidden-tab + reconnect resync (Story 5)

Per ontwerp samen in één commit zodat geen vangnet wegvalt zonder vervanging.

- T-861: useBacklogRealtime sluit niet meer op visibilitychange hidden;
  EventSource blijft open zolang browser/netwerk dit toelaten. Reconnect bij
  netwerkfout blijft via backoff. visibilitychange fungeert nog wel als
  re-connect-trigger als de stream tussentijds is gesloten (b.v. 240s
  hard-close server-side).
- T-862: 'ready'-event-handler telt connect-cycles. De eerste 'ready' is de
  initial connect (geen resync). Bij latere 'ready' (post-reconnect) wordt
  resyncActiveScopes('reconnect') aangeroepen om gemiste events op te halen.
- T-863: nieuwe lib/realtime/use-workspace-resync.ts — luistert op
  document.visibilitychange (hidden→visible) en window.online; dispatcht
  resyncActiveScopes('visible') resp. 'reconnect'. Mounted in
  BacklogHydrationWrapper na useBacklogRealtime.
- T-864: 4 nieuwe vitest-cases voor useWorkspaceResync (jsdom): visible→
  visible event, online event, hidden negeren, cleanup-bij-unmount.

Daarnaast lint-cleanup: ongebruikte 'order'-variabelen in pbi-list en
story-panel weggehaald.

Verify: lint+typecheck clean, 646/646 tests groen.

Refs: PBI-74, ST-1322, T-861..T-864

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

* feat(PBI-74): unknown-event fallback tests (Story 6)

T-865 (isUnknownEntityEvent filter) en T-866 (resync-trigger in
applyRealtimeEvent) zijn al in Story 1 geïmplementeerd in store.ts;
deze story breidt de test-coverage uit met expliciete negatieve cases
voor het type-veld noise pattern.

T-867 — 5 nieuwe vitest-cases:
- unknown entity met ANDER product_id → geen resync
- claude_job_status (type) → geen resync
- worker_heartbeat (type) → geen resync
- claude_job_enqueued (type) → geen resync
- payload zonder entity en zonder type → genegeerd
- question-entity (entity-veld, geen type, niet pbi/story/task) → resync trigger

Verify: lint+typecheck clean, 651/651 tests groen.

Refs: PBI-74, ST-1323, T-865..T-867

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

* feat(PBI-74): cache-headers + LIST endpoints (Story 7)

- T-868: cache: 'no-store' was al ingebouwd in fetchJson helper (Story 1).
  Bevestigd door bestaande ensureProductLoaded test die de fetch-init
  controleert.
- T-869: force-dynamic toegevoegd op alle vier nieuwe LIST-endpoints.
- T-870: vier nieuwe routes voor ensure*Loaded:
  - GET /api/products/:id/backlog → ProductBacklogSnapshot
  - GET /api/pbis/:id/stories → BacklogStory[]
  - GET /api/stories/:id/tasks → BacklogTask[]
  - GET /api/tasks/:id (nieuwe handler naast bestaande PATCH) → TaskDetail
    met _detail: true marker
  Auth via authenticateApiRequest (Bearer of iron-session); access-control
  via productAccessFilter (gebruiker is owner of member van het product).
  Statussen worden via taskStatusToApi/storyStatusToApi/pbiStatusToApi
  vertaald naar lowercase API-vorm.
- T-871: SSE-route /api/realtime/backlog stuurt al ready-event direct na
  LISTEN (regel 106) — geen wijziging nodig.

Verify: lint+typecheck clean, 651/651 tests groen.

Refs: PBI-74, ST-1324, T-868..T-871

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

* feat(PBI-74): oude stores opruimen (Story 8)

Workspace-store is nu de enige bron voor product-backlog client-state. De
vier voorgangers en de dual-dispatch-infrastructuur zijn verwijderd.

- T-872: grep over codebase op useBacklogStore/usePlannerStore/
  useSelectionStore/useProductStore is leeg.
- T-873..T-876: stores/{backlog,planner,selection,product}-store.ts deleted.
- T-877: __tests__/realtime/payload-contract.test.ts en
  __tests__/api/backlog-realtime.test.ts deleted — pbi/story/task I|U|D
  payload-handling wordt al gedekt door
  __tests__/stores/product-workspace/store.test.ts (incl. parent-move,
  idempotent inserts, delete-cleanup).
- T-878: lib/realtime/dev-workspace-fingerprint.ts deleted, dual-dispatch
  uit BacklogHydrationWrapper en lib/realtime/use-backlog-realtime.ts
  weggehaald. stores/products-store.ts (lijst van producten ≠ active
  product) blijft ongewijzigd.

Bijwerkingen:
- BacklogPbi en BacklogStory types in components/backlog/story-panel.tsx en
  components/sprint/sprint-backlog.tsx krijgen sort_order zodat ze met de
  workspace-types overeenkomen.
- Server-pages /products/[id]/page.tsx (desktop+mobile) en
  /products/[id]/sprint/[sprintId]/page.tsx selecteren sort_order op story
  en mappen het door in de hydration-payload.

Verify: lint+typecheck clean, 626/626 tests groen (verlies van 25 redundante
oude-store tests; workspace-store tests dekken hetzelfde gedrag).

Refs: PBI-74, ST-1325, T-872..T-878

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

* docs(PBI-74): richtlijn workspace-store + realtime patroon

Documenteert het patroon dat in Stories 1-8 is opgeleverd, zodat een
volgende workspace-store (sprint, of een nieuwe bounded context) hetzelfde
recept volgt.

- docs/patterns/workspace-store.md (nieuw): wanneer een workspace-store, de
  vijf state-slices, selectors-regels (G1/G2), race-safe ensure*Loaded met
  activeRequestId-guard (G4), SSE-hook + applyRealtimeEvent met
  unknown-event filter, hidden-tab + reconnect resync via
  useWorkspaceResync, restore-hint flow met await-chain en URL-prioriteit,
  optimistic mutations (applyOptimisticMutation/rollback/settle), API
  endpoint-vereisten (force-dynamic, cache: no-store), test-setup met
  MemoryStorage + originalActions snapshot + mockImplementation, gotchas
  G1-G8 als comment-template, en het 8-staps migratiepad.
- docs/patterns/zustand-optimistic.md: bijgewerkt voor de nieuwe
  workspace-store API; verwijst voor het bredere patroon naar
  workspace-store.md. Voorbeelden voor pbi-order + entity-patch.
- CLAUDE.md: patterns quickref aangevuld met workspace-store-rij.

Verify: typecheck clean.

Refs: PBI-74

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

* fix(PBI-74): solo + notifications hooks volgen ook hidden-tab/resync patroon

Het uitgangspunt van PBI-74 (robuust tegen gemiste SSE-events, hidden tabs
en onbekende notify-vormen) gold universeel — niet alleen voor
product-workspace. use-solo-realtime en use-notifications-realtime hadden
nog dezelfde bug die use-backlog-realtime in Story 5 al opgelost kreeg:
sluit stream op hidden, geen resync.

Reproductie (zoals gemeld): solo-screen open in tab A, product-backlog
open in tab B; bewerk task-title in tab B → tab A's solo-SSE was gesloten
(hidden) en kreeg het NOTIFY-event nooit. Tab terug naar solo →
EventSource reconnect maar geen resync → oude title persisteert. Postgres
NOTIFY heeft geen replay, dus zonder resync zijn die events permanent
verloren.

Fix in beide hooks (zelfde patroon als Story 5 voor backlog):
- Stream blijft open op visibilitychange hidden — geen close() meer.
- Bij hidden→visible én bij window 'online': router.refresh() zodat de
  server-component opnieuw fetcht en de initial-state-prop ververst (wat
  voor solo de tasks-record reset via initTasks; voor notifications de
  questions-bel-state).
- Bij latere 'ready'-events na reconnect (use-solo-realtime): zelfde
  router.refresh() trigger zodat we niet vertrouwen op alleen het
  visibility-pad.

Verify: lint + typecheck clean, 626/626 tests groen.

Refs: PBI-74

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

* docs: fix broken links in research-repo plan

docs/plans/lees-de-readme-md-validated-book.md beschrijft een research-
repo migratiepad. De links waren geschreven vanuit het research-repo-
perspectief (paden als stores/data-store.ts, ../Scrum4Me/CLAUDE.md,
docs/plans/zustand-store-rearchitecture.md zonder relative-prefix), wat
de doc-link-checker hier laat falen.

- Header-note toegevoegd dat het document voor de research-repo is.
- Interne refs (zustand-store-rearchitecture.md, CLAUDE.md) → relatieve
  paden die in deze repo wél resolven (./zustand-..., ../../CLAUDE.md).
- Research-repo-only refs (stores/data-store.ts,
  hooks/use-event-stream.ts, components/*-select.tsx, etc.) → inline
  code-tags met "(research-repo)" suffix; de link-checker slaat ze over
  en de leesbaarheid blijft.

Verify: npm run docs:check-links → ✓ All doc links valid (118 files).

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 02:25:19 +02:00
0d126695db docs: add plans and recommendations
- docs/plans/Local github setup.md
- docs/plans/lees-de-readme-md-validated-book.md
- docs/plans/zustand-store-rearchitecture.md
- docs/recommendations/beelink-ubuntu-scrum4me-server-caveman-plan.md
- docs/recommendations/claude-vm-job-flow-git-strategy.md
- docs/INDEX.md updated

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 22:54:23 +02:00
Janpeter Visser
d292e445d9
Sprint: Verbeteren debug mode (#179)
* feat(PBI-49): add debugProps helper + Vitest test

Adds lib/debug.ts with debugProps(id, component, file) that returns
data-debug-id and data-debug-label attrs in dev mode, empty object in
production. Adds __tests__/lib/debug.test.ts covering both modes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(PBI-49): add debug-id pattern doc + CLAUDE.md reference

Adds docs/patterns/debug-id.md documenting the named-component boundary
rule (6 punten), helper-voorbeeld, skip-criteria en motivatie voor
handmatige pad-argumenten. Voegt verwijzing toe aan CLAUDE.md
patterns-tabel.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(PBI-49): migrate 17 shared/ components to debugProps helper

Replace hardcoded data-debug-id + data-debug-label attribute pairs with
{...debugProps(id, component, file)} spread in all 17 components/shared/
files. Existing debug-ids preserved unchanged.

* feat(PBI-49): add debugProps to backlog/, sprint/, solo/ components

* feat(PBI-49): add debugProps to jobs/ + ideas/ components

* feat(PBI-49): add debugProps to products/ + settings/ + notifications/ components

* feat(PBI-49): add debugProps to admin/ + dashboard/ + dialogs/ + mobile/ + split-pane/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(PBI-49): use attr(data-debug-id) for debug tooltip in globals.css

* refactor(PBI-49): remove data-debug-label from debugProps helper + test

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(PBI-49): strip unused component/file args from debugProps in shared/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-49): add BEM sub-element data-debug-id to StatusBar, NavBar, PanelNavBar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-49): add BEM sub-element data-debug-id to components/sprint/*

- new-sprint-dialog: __submit on submit button
- sprint-backlog: __list on SprintBacklogLeft + SprintBacklogRight scroll areas
- sprint-board-client: root wrapper div (display:contents) + __drag-overlay
- sprint-header: __title on goal button, __dates on dates button, __actions on action cluster
- sprint-run-controls: root on controls div, __start/__cancel on action buttons; __blockers-dialog on dialog content
- start-sprint-button: root on trigger button, __dialog on dialog content, __submit on submit button
- sync-active-sprint-cookie: no debug-id (returns null, side-effect only), comment added

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-49): add BEM sub-element data-debug-id to components/backlog/*

* feat(PBI-49): add BEM sub-element data-debug-id to components/ideas/*

* feat(PBI-49): add BEM sub-element data-debug-id to components/dashboard/* + components/markdown.tsx

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-49): add BEM sub-element data-debug-id to new-product-button

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-49): add BEM sub-element data-debug-id to components/solo/*

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-49): add BEM sub-elements to nav-status-indicators

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-49): add BEM sub-element data-debug-id to components/jobs/*

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-49): add BEM sub-element data-debug-id to components/products/*

* feat(PBI-49): add BEM sub-element data-debug-id to components/notifications/*

- answer-modal: __content (scroll area), __submit (footer)
- notifications-bridge: skip comment (bridge, non-rendering wrapper)
- notifications-realtime-mount: skip comment (returns null)
- notifications-sheet: __header, __items (questions list)
- push-toggle: __switch (button), __label (button text) on subscribed/unsubscribed states

* feat(PBI-49): add BEM sub-element data-debug-id to components/settings/*

- leave-product-button: root only (single-button component)
- min-quota-editor: __input (number input), __save (save button)
- profile-editor: __username (bio/short-description input), __save (submit)
- role-manager: __roles (checkbox list), __add (save button)
- token-manager: __tokens (active tokens list), __generate (create button)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(PBI-49): add BEM sub-element data-debug-id to admin, auth, dialogs, entity-dialog, mobile, split-pane

* docs(PBI-49): add debug-labels BEM pattern doc + CLAUDE.md entry

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 22:46:29 +02:00
Janpeter Visser
c2633695d2
fix(KIND_DEFAULTS): permission_mode acceptEdits voor idea-kinds + PLAN_CHAT (#172)
* fix(KIND_DEFAULTS): permission_mode acceptEdits voor idea-kinds + PLAN_CHAT

Spiegel van scrum4me-mcp PR #40. Symptoom: IDEA_GRILL job IDEA-047 werd
3x geclaimd, Claude liep telkens succesvol (exit 0 na 600-900s) maar
deed nooit update_job_status('done'). Lease verliep, retry_count >= 2 →
status FAILED met "agent did not complete job within 2 attempts".

Root cause: KIND_DEFAULTS.permission_mode='plan' voor idea-kinds en
PLAN_CHAT. In autonome batch-mode wacht plan-mode op een human "go" na
elke planning-fase — geen mens in de loop in deze runner-context.

Fix:
- IDEA_GRILL.permission_mode: plan → acceptEdits
- IDEA_MAKE_PLAN.permission_mode: plan → acceptEdits
- PLAN_CHAT.permission_mode: plan → acceptEdits
- PLAN_CHAT.allowed_tools krijgt mcp__scrum4me__update_job_status (ontbrak)

De allowed_tools-lijsten doen de echte sandboxing (geen Bash, geen Edit
voor IDEA_GRILL/PLAN_CHAT). Plan-mode's "veiligheid" wordt al door
tool-allowlists geleverd; acceptEdits is hier puur om Claude door zijn
eigen update_job_status loop te laten lopen zonder approval-wachttijd.

Plus: docs/runbooks/{job-model-selection,worker-idempotency}.md tabellen
bijgewerkt. last_updated note in job-model-selection.md.

Verify: 587 tests in 78 files passed (incl. nieuwe lib/job-config tests).

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

* chore: remove .claude/scheduled_tasks.lock per ongeluk meegecommit

Lokale tooling-lock-file van de cowork-skills, hoort niet in de repo.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 11:33:03 +02:00
Janpeter Visser
00c5045558
feat(PBI-4/ST-006): mirror job-config naar webapp + runbook-fix CLI-flags (#171)
Spiegelt de scrum4me-mcp wijzigingen naar de Scrum4Me web-app zodat
enqueue-laag (lib/job-config-snapshot.ts) en claim-laag dezelfde
defaults gebruiken. Plus runbook-correctie van een eerder gedocumenteerde
maar niet-bestaande Claude CLI-flag.

- T-25: lib/job-config.ts — mapBudgetToEffort export + KIND_DEFAULTS
  .allowed_tools voor TASK/SPRINT/IDEA_GRILL/IDEA_MAKE_PLAN omgezet
  naar expliciete lijsten zonder wait_for_job/check_queue_empty/
  get_idea_context. Comment-block over CLI-flag-mapping en sync met
  scrum4me-mcp.
- T-26: docs/runbooks/worker-idempotency.md sectie "Config doorgeven aan
  Claude Code (PBI-67)" herschreven. --thinking-budget vervangen door
  --effort (mapping-tabel toegevoegd); --max-turns geschrapt (CLI heeft
  die flag niet — audit-only). Sectie "Wie doet wat in de runner-
  architectuur" toegevoegd.
- T-27: docs/runbooks/job-model-selection.md — notes over max_turns,
  thinking_budget en allowed_tools onder de matrix. Nieuwe sectie
  "Runner-architectuur" met verwijzing naar plan + worker-idempotency.
- T-28: __tests__/lib/job-config.test.ts (nieuw) — 22 tests:
  mapBudgetToEffort grenswaarden + KIND_DEFAULTS.allowed_tools structurele
  checks + cascade regression.

Plus: docs/plans/queue-loop-extraction.md (geschreven in plan-mode,
nu gepubliceerd in repo).

Verify: lint OK, typecheck OK, 587 tests in 78 files passed.
Build niet lokaal uitgevoerd (vereist DATABASE_URL voor "Collecting page
data" — diff raakt geen API-route).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 07:11:52 +02:00
Janpeter Visser
8c63ba377d
feat(PBI-67): model + mode-selectie per ClaudeJob-kind (#169)
* feat(PBI-67/ST-1297): datamodel-velden voor job-model-selectie

Voegt 8 nieuwe optionele velden toe verspreid over Product, Task en
ClaudeJob ten dienste van de override-cascade:

  task.requires_opus → job.requested_* → product.preferred_* → kind-default

Bestaande rijen krijgen NULL (Product/ClaudeJob) of false (Task) en
vallen daarmee terug op de kind-defaults uit de resolver (ST-1298).

Migration is additief: alleen ALTER TABLE ADD COLUMN, geen RENAME of
DROP. Bestaande factories en seed-script blijven werken zonder
aanpassing omdat alle nieuwe velden default-waardes hebben.

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

* feat(PBI-67/ST-1299): job-config snapshot bij enqueue + worker-flag-runbook

T-789: Snapshot van resolved JobConfig in ClaudeJob.requested_*
bij elke job-creatie. Helper in lib/job-config-snapshot.ts laadt
product (preferred_*) en task (requires_opus) en draait de resolver
uit lib/job-config.ts (mirror van scrum4me-mcp/src/lib/job-config.ts —
zelfde matrix, sync-comment in bestand). Toegepast op alle 5
enqueue-locaties:

  - actions/user-questions.ts          (PLAN_CHAT)
  - actions/sprint-runs.ts × 3         (SPRINT_IMPLEMENTATION x2,
                                        TASK_IMPLEMENTATION loop)
  - actions/ideas.ts                   (IDEA_GRILL / IDEA_MAKE_PLAN)

Test-mocks uitgebreid met product.findUnique en task.findUnique zodat
de helper bij unit tests veilig terugvalt op kind-defaults (alle 563
tests groen).

T-790: Sectie 'Config doorgeven aan Claude Code' toegevoegd aan
docs/runbooks/worker-idempotency.md met CLI-flag-mapping en de
verwachte aanroep per kind. Forward-link naar
docs/runbooks/job-model-selection.md (volgt in T-794).

Plus: docs/plans/job-model-selection.md (de approved plan-doc).

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

* feat(PBI-67/ST-1300): cost-attribution voor thinking-tokens + admin UI

T-792: token-stats + token-history rekenen actual_thinking_tokens nu
mee in de totale kosten (tegen input-rate, conform Anthropic billing).
COALESCE-veilig zodat oude rijen 0 bijdragen i.p.v. NaN. Nieuwe export
`getTokenStatsByKind` aggregeert tokens en kosten per ClaudeJob.kind
zodat we relatieve uitgaven van IDEA_GRILL/IDEA_MAKE_PLAN/PLAN_CHAT/
TASK_IMPLEMENTATION/SPRINT_IMPLEMENTATION kunnen zien.

T-793: admin/jobs Kosten-tabel toont:
  - Nieuwe kolom 'Thinking' (aantal verbruikte thinking-tokens)
  - Mismatch-marker (rood) als requested_model afwijkt van actuele
    model_id — duidt op een worker die de CLI-flag niet doorgaf.
    Tooltip toont aangevraagd model. Geen Sentry/log-noise.

Page-level cost-berekening volgt dezelfde formule (input_price ×
thinking_tokens). 563 tests groen.

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

* docs(PBI-67/ST-1301): runbook + CLAUDE.md updates voor model/mode-selectie

T-794: Nieuwe runbook docs/runbooks/job-model-selection.md met
override-cascade, kind-default-matrix, override-voorbeelden,
auditspoor en cost-attribution-formule. 107 regels.

T-795: CLAUDE.md hardstop-bullet voor 'Model/mode per ClaudeJob'
(verwijst naar nieuwe runbook) + patterns-quickref-rij voor
job-config resolver. CLAUDE.md blijft 139 regels (≤ 150).

T-796: docs:check-links groen — 108 files, geen broken links. Twee
externe-repo verwijzingen (scrum4me-mcp/...) ge-de-linked tot plain
text omdat de check-links script de zustertree niet traverseert; de
referenties blijven leesbaar.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 11:20:10 +02:00
Janpeter Visser
eaabec8471
feat(PBI-66): wekelijkse sync van model_prices via Anthropic /v1/models (#167)
Nieuw script `npm run db:sync-model-prices` haalt de actuele Claude 4.x
modellijst op bij de Anthropic API en upsert prijzen in `model_prices`.
Anthropic biedt geen prijs-API, dus prijzen blijven onderhouden in een
PRICE_TABLE constante in het script. Cache-tier-prijzen worden afgeleid
via vaste multipliers (read 0.1x, write 1.25x). Nieuwe Claude 4.x modellen
worden gedetecteerd en gelogd als warning zodat duidelijk is wanneer de
tabel handmatig moet worden bijgewerkt.

- scripts/sync-model-prices.ts: idempotent upsert, --dry-run, retry op 5xx
- ANTHROPIC_API_KEY als optional env-var (.env.example, lib/env.ts)
- scripts/README.md: gebruiksinstructies + edge cases
- docs/plans/sync-model-prices.md: ontwerpdocument

Verificatie: `npm run lint`, `vitest` (563/563), TypeScript clean.
Echt gedraaid tegen DB: 3 created (eerste run) -> 3 unchanged (tweede run).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 09:38:33 +02:00
Janpeter Visser
f7464db837
docs: sync data-model, glossary en specs met huidig schema (#164)
Brengt de docs gelijk met de werkelijkheid na PBI-46/47/50/58/59/61/63
en M12. Belangrijkste fixes:

- data-model.md herschreven naar prisma/schema.prisma: nieuwe entiteiten
  (Idea, IdeaLog, IdeaProduct, UserQuestion, ClaudeQuestion, ClaudeJob,
  SprintRun, SprintTaskExecution, ClaudeWorker, LoginPairing,
  PushSubscription, ModelPrice, ProductMember), nieuwe enums
  (FAILED/EXCLUDED, OPEN/CLOSED/ARCHIVED, ADMIN, etc.) en codes
  (PBI/ST/T/SP-N) toegevoegd; verwijderde todos-tabel verwijderd.
- glossary.md: Sprint zonder "max 1 actief" (PBI-63), Story/Task incl.
  FAILED/EXCLUDED, Todo verwijderd, Idea/SprintRun/ClaudeJob/
  verify_result toegevoegd.
- project-structure.md: app/(app)/todos vervangen door
  ideas/insights/jobs/manual/admin/solo; api-tree volledig.
- overview.md: "geen realtime in v1" en Docker-rationale herschreven —
  Postgres LISTEN/NOTIFY + SSE, claude_jobs als queue, opt-in
  Docker-deploy-flow.
- functional.md: F-08 Todo-lijst -> Ideeen-laag, F-09 multi-sprint,
  F-10 Task-status incl. FAILED/EXCLUDED, F-11 endpoint-lijst,
  navigatiestructuur, datamodel-schets en Flow 3 bijgewerkt.
- README.md API-tabel: /api/todos weg, ideas/jobs/users/profile/health
  toegevoegd, kort over realtime/auth-pair/internal/cron.
- patterns + mcp-integration runbook: Todo-/ACTIVE-references vervangen
  door Idea/OPEN; create_todo MCP-tool note over verwijdering.

Linkcheck groen (105 files), INDEX hergegenereerd (98 docs).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 08:16:44 +02:00
Janpeter Visser
7ae8a24372
Sprint: pbi-55 (#156)
* ST-cmovs79lt: Schema + migratie PushSubscription model

Voeg PushSubscription model toe aan prisma/schema.prisma met
snake_case-conventie, relation field op User, en bijbehorende
migratie (push_subscriptions tabel, FK + index op user_id).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ST-cmovs7e3o: web-push dependency + VAPID env vars feature-gated

Voeg web-push + @types/web-push toe aan package.json.
Registreer NEXT_PUBLIC_VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY,
VAPID_SUBJECT en INTERNAL_PUSH_SECRET als .optional() in lib/env.ts.
Documenteer alle vier in .env.example en README.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ST-cmovs7jgr: lib/push-server.ts met sendPushToUser + stale-cleanup

Server-only push-lib met VAPID feature-gate, send naar alle
subscriptions van een user, en automatische cleanup bij 404/410.
Unit tests: success-pad, 410 verwijdert sub, 404 verwijdert sub,
andere errors loggen zonder delete.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ST-cmovs7ouz: lib/push-client.ts client-side push helpers + stub actions/push.ts

Client-side helpers: isPushSupported, isIOSSafari, isStandalonePWA,
urlBase64ToUint8Array, subscribeToPush, unsubscribeFromPush.
Stub actions/push.ts zodat imports resolven (implementatie volgt
in volgende taak). Unit tests voor urlBase64ToUint8Array.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ST-cmovs7ut4: actions/push.ts subscribeToPushAction + unsubscribeFromPushAction

Vervangt stub met volledige implementatie: requireUser via getSession,
demo-block, Zod-validatie, upsert met user_id-scoping en user-scoped
deleteMany. Tests (8): idempotentie, demo-block, unauthenticated, invalid input.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ST-cmovs80c1: POST /api/internal/push/send met constant-time Bearer check

Route: 503 als INTERNAL_PUSH_SECRET uitstaat, 401 bij verkeerd secret
(timingSafeEqual), 400 bij invalid JSON, 422 bij Zod-fout, 204 bij succes.
push-server.ts: env-import vervangen door process.env om SESSION_SECRET
validatie tijdens build te omzeilen. Tests aangepast.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ST-cmovs862j: Admin test-send route + public/sw.js service worker

POST /api/internal/push/test-send: requireAdmin check (redirect bij
niet-admin), optioneel body met defaults, roept sendPushToUser aan, 204.
public/sw.js: push-handler met showNotification, notificationclick met
same-origin guard, focus bestaand venster of openWindow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ST-cmovs8jvq: PushToggle component met 3 states + iOS-banner

Client component met states loading/unsupported/ios-needs-install/
denied/subscribed/unsubscribed. useEffect detecteert initial status,
permission-prompt alleen via user-click. iOS-banner NL, denied-uitleg,
subscribe/unsubscribe knoppen met sonner-toasts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ST-cmovs8psg: notifications-sheet + iOS meta-tags in layout

notifications-sheet.tsx: PushToggle onderin met sectie
'Notificatie-instellingen' en visuele scheidslijn.
app/layout.tsx: appleWebApp.capable, statusBarStyle en
mobile-web-app-capable meta-tags toegevoegd via Next.js Metadata API.
manifest.json had al display: standalone.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ST-cmovs8vxj: docs/patterns/web-push.md pattern-documentatie

Architectuur-diagram, payload-shape, foutcodes, VAPID-config,
iOS-quirks, demo-users blokkade, trigger-voorbeelden (server +
HTTP) en admin-testroute curl-voorbeeld.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 21:46:01 +02:00
Janpeter Visser
00dbbb4f94
chore(ci): gate auto-deploy behind AUTO_DEPLOY_ENABLED repo-variable (#154)
Voorkomt automatische Vercel-deploys op PR-preview en push-naar-main
zolang \`vars.AUTO_DEPLOY_ENABLED == 'true'\` ontbreekt. Default-staat:
auto-deploy UIT, scheelt Actions-minuten op het free-plan.

Handmatig deployen blijft werken via workflow_dispatch (Actions tab →
"Run workflow" → kies preview of production). Die job (\`deploy-manual\`)
is niet aan de flag gebonden.

Aanzetten van auto-deploy: Settings → Secrets and variables → Actions
→ Variables → New repository variable: \`AUTO_DEPLOY_ENABLED\` = \`true\`.

\`changes\` job (path-filter) staat ook achter de flag — die wordt alleen
gebruikt door de twee auto-deploy jobs.

Runbook bijgewerkt met de nieuwe default + uitleg.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:17:15 +02:00
Janpeter Visser
bd7478861b
PBI-58: Developer manual + in-app /manual page (#148)
* docs(PBI-58): add developer manual chapters under docs/manual/

Adds a 7-file English-language manual targeted at new human contributors:
index, overview, statuses & transitions (with mermaid state diagrams),
git workflow, MCP integration, docker, and troubleshooting. The manual
is the *map* — it cross-references existing runbooks/ADRs/architecture
docs rather than duplicating their content.

Regenerates docs/INDEX.md and validates with check-doc-links.mjs.

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

* chore(PBI-58): add markdown rendering deps + manual:build script

Adds mermaid, rehype-slug, rehype-autolink-headings for the in-app
/manual page. Wires manual:build into prebuild so production builds
always regenerate the chapter TOC.

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

* feat(PBI-58): codegen script for in-app manual TOC

scripts/build-manual.mjs walks docs/manual/, parses YAML front-matter,
strips it from the body, and emits lib/manual.generated.ts with a typed
ManualEntry[] containing slug, title, description, filePath, and the
embedded markdown body. Pure Node 20, mirrors generate-docs-index.mjs.

Inlining the markdown at build time keeps runtime serverless functions
free of filesystem reads, which avoids whole-project NFT tracing.

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

* feat(PBI-58): /manual route renders developer manual chapters in-app

Catch-all route at app/(app)/manual/[[...slug]]/page.tsx with
generateStaticParams covering every TOC entry. Server-side
MarkdownView uses react-markdown with remark-gfm, rehype-slug, and
rehype-autolink-headings; mermaid code blocks are routed to a
client-only MermaidBlock that dynamic-imports mermaid on mount.

ManualSidebar (client) reads the typed TOC and highlights the active
chapter via usePathname.

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

* feat(PBI-58): add Manual link to main nav bar

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:00:10 +02:00
Janpeter Visser
fffa5a47d2
docs: archiveer sprint-pr-worktree state-machines advies (#140)
Het advies-document dat als input diende voor PBI-50 is nu in docs/plans/
opgeborgen voor traceability. INDEX.md regenerated om hem op te nemen.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:32:30 +02:00
Janpeter Visser
07749ad9fb
PBI-50: SPRINT_IMPLEMENTATION single-session sprint runner (Scrum4Me-side) (#139)
* PBI-50 F1: SPRINT_BATCH execution-strategy + cross-repo blocker + branch-resume

Schema-migratie + Scrum4Me-side wiring voor de nieuwe SPRINT_IMPLEMENTATION-flow:

- prisma: PrStrategy ADD VALUE 'SPRINT_BATCH'; ClaudeJobKind ADD VALUE
  'SPRINT_IMPLEMENTATION'; nieuwe enum SprintTaskExecutionStatus; ClaudeJob.lease_until
  + status_lease_until index; SprintRun.previous_run_id (self-relation
  SprintRunChain) voor branch-hergebruik bij resume; nieuwe sprint_task_executions
  tabel met frozen plan_snapshot + verify_required_snapshot per task in scope.
- actions/sprint-runs.ts startSprintRunCore: nieuwe blocker-type 'task_cross_repo'
  voor SPRINT_BATCH (pre-flight rejecteert sprints met cross-repo task_url).
  Bij SPRINT_BATCH: één SPRINT_IMPLEMENTATION ClaudeJob (geen per-task loop).
- actions/sprint-runs.ts resumePausedSprintRunAction: SPRINT_BATCH-pad met
  remaining-execution-check; bij onafgemaakt werk → nieuwe SprintRun met
  previous_run_id + run.branch hergebruikt + nieuwe SPRINT_IMPLEMENTATION-job.
  Oude SprintRun → CANCELLED. Bestaande PBI-49 P0 scope-DONE pad ongewijzigd.
- actions/products.ts updatePrStrategyAction: accepteert SPRINT_BATCH.
- components/products/pr-strategy-select.tsx: drie opties met helptekst,
  gebruikt @prisma/client PrStrategy ipv lokaal type.
- components/sprint/sprint-run-controls.tsx: BLOCKER_LABELS + blockerHref
  voor task_cross_repo.

Migratie applied op Neon. Type-check + 532 tests groen.

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

* PBI-50 F5: cross-repo blocker test voor SPRINT_BATCH

- task_cross_repo blocker fires bij task.repo_url ≠ product.repo_url
- happy path: tasks zonder repo_url-override of met match → één
  SPRINT_IMPLEMENTATION-job (niet per-task).

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

* PBI-50 F5: docs/architecture/sprint-execution-modes.md

Vergelijking PER_TASK vs SPRINT_BATCH met trade-offs, datamodel-
toevoegingen (SprintTaskExecution, lease_until, SprintRunChain) en
MCP-tools-matrix per modus. Toegevoegd aan breadcrumb in
docs/architecture.md.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:05:02 +02:00
Janpeter Visser
3a61a8ddc1
Landing v3: van idee tot pull request + 6 nieuwe screenshots (#127)
* feat(landing): tour uitbreiden naar 6 screenshots incl. Ideas/Insights

Vervangt de oude 3 .jpg-screenshots door 6 nieuwe .png's met de huidige
visuele staat van de app. Volgorde toont de hele flow: ideeën binnen →
producten → backlog → sprint → solo → insights.

- Tour-array van 3 naar 6 figures, grid blijft lg:grid-cols-3 (2x2-rijen)
- Intro-tekst aangepast: "Zes weergaven van Scrum4Me — van inkomende
  ideeën tot persoonlijk Kanban-bord en voortgangs-inzichten"
- Bestandsnamen gehernoemd naar lowercase-dash-conventie:
  Sprint.png → sprint.png, Solo.png → solo.png, "Product Backlog.png" →
  product-backlog.png, Producten.png → producten.png, Insights.png →
  insights.png, Ideas-table.png → ideas-table.png,
  Ideas-detail.png → ideas-detail.png (alleen voorbereiding, nog niet in
  tour gebruikt)
- Oude .jpg-bestanden verwijderd

Niet onderdeel: hero/architectuur/handleiding-rewrite voor v3 — die staan
gepland onder ST-1224 en blijven open.

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

* feat(landing): v3 — van idee tot pull request, met procesflow + Idea-laag

Verbreedt de landing-propositie van "executie lokaal" (v2) naar de
volledige cyclus idee → grill → plan → execute → PR. Eert M12 Ideas dat
sinds 2026-05-04 het kernconcept van het product is, plus auto-PR en
Sync-tab.

Hero:
- H1 "Van idee tot pull request — op je eigen hardware."
- Subhead benoemt grill→plan→materialise→agent→PR-cyclus en houdt het
  lokaal-anker uit v2 vast.

Nieuw §3 "Van idee tot pull request":
- 4 procesflow-kaarten (Idee/Grill/Plan/Execute) met status-chips
  (DRAFT/GRILLING/PLAN_READY/DONE→PR) en pijlen tussen kaarten op md+.
- Samenvattende paragraaf over state-machine, materializeIdeaPlanAction
  en auto-PR.

Architectuur:
- docs/diagrams/architecture.mmd Worker-label uitgebreid met
  "jobs: GRILL · PLAN · IMPL"; beide SVG's geregenereerd.
- Callout "Lokale worker" benoemt nu de drie jobsoorten expliciet.

Feature-grid (set D, 6 kaarten):
- Vervang "Hiërarchisch plannen" door "Ideas — Grill & Plan" op positie 1.
- Lokale Claude-agents: + auto-PR/SQUASH-merge zin.
- Realtime updates: + Sync-tab zin.
- Async vraagkanaal: + Grill-vragen zin.

Quickstart:
- Extra regel over UI-route (/ideas → Nieuw idee → Grill me) onder de
  bestaande MCP-installatie code-block.

Scrum-samenvatting:
- Hiërarchie wordt twee-rij-systeem: Idea (DRAFT → GRILLED → PLAN_READY)
  als bovenste rij, daaronder Product → PBI → Story → Taak met
  "materialiseert ↓"-pijl.
- Terminologie krijgt 2 nieuwe tegels (Idea, Grill/Plan).

Handleiding (10 → 12 stappen):
- Nieuwe stappen 3-5 voor Idea-route (vastleggen, grillen, plan + materialiseren),
  visueel gemarkeerd met tertiary-border en chip "Idea-route".
- Oude 7+8 (token + MCP-koppeling) samengevoegd tot stap 10.
- Stap 11 uitgebreid met Sync-tab-volgen.
- Stap 6 hernoemd "opbouwen" → "finetunen".

Plan-doc + grilling-context vastgelegd in docs/plans/landing-v3-idea-flow.md.

Tracked: ST-1224 onder PBI Marketing & Landingspagina.

Verified: lint 0 errors / 7 warnings (alle pre-existing), 564/564 tests,
productie-build slaagt.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 09:05:22 +02:00
Janpeter Visser
31dc429b61
feat(M13 PBI-31 T-519b/T-520b): NavBar stand-by badge + quota-check runbook (#119)
* feat(M13 T-519b): SSE worker_heartbeat + NavBar stand-by badge

Aanvulling op scrum4me-mcp PR #25 (worker_heartbeat MCP-tool).

- app/api/realtime/solo/route.ts: WorkerHeartbeatPayload type +
  isWorkerHeartbeatPayload guard + shouldEmit-routing op user_id.
- stores/solo-store.ts: workerQuotaPct + workerQuotaCheckAt state +
  setWorkerQuota action. Reset bij decrementWorkers naar 0.
- lib/realtime/use-solo-realtime.ts: handle worker_heartbeat-event,
  roep setWorkerQuota.
- components/solo/nav-status-indicators.tsx: stand-by badge wanneer
  workerQuotaPct < minQuotaPct + tooltip met drempel.
- components/shared/nav-bar.tsx + app/(app)/layout.tsx: minQuotaPct
  prop plumbing van User.min_quota_pct naar NavStatusIndicators.

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

* docs(M13 T-520b): pre-flight quota-check sectie in mcp-integration

Documenteert de batch-loop-uitbreiding:
1. get_worker_settings → min_quota_pct
2. bin/worker-quota-probe.sh → pct + reset
3. worker_heartbeat naar server (NavBar stand-by-badge)
4. Sleep tot reset bij low quota; anders wait_for_job

Verwijst naar bin/worker-quota-probe.sh in scrum4me-docker (zie
PR daar).

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 04:34:48 +02:00
c15719164a docs(auto-pr): runbook voor end-to-end auto-PR flow
Beschrijft hoe agent-jobs met Product.auto_pr=true automatisch
commit → push → PR → auto-merge → deploy doorlopen. Documenteert
welke laag wat doet (worker, scrum4me-mcp, GitHub Actions, Vercel),
setup-vereisten per product, foutpaden en wanneer auto_pr UIT
laten.

Onderdeel van PBI-36 ST-1220. De auto-PR-implementatie zelf zit
in scrum4me-mcp; deze runbook documenteert de bestaande flow plus
de nieuwe auto-merge-stap (scrum4me-mcp PR #23).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 00:37:18 +02:00
ee793e9af4 docs(T-557): deploy-control runbook + CLAUDE.md verwijzing
docs/runbooks/deploy-control.md beschrijft triggers (push/PR/dispatch),
path-filter, labels (skip-deploy/force-deploy), beslismatrix per
scenario, voorbeelden en troubleshooting. CLAUDE.md hardstop-regel
"Deployment" verwijst naar runbook.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 23:36:48 +02:00
084ca81090 docs(T-575): worker-idempotency runbook + CLAUDE.md verwijzing
Beschrijft beslissingsboom verify_result × diff-staat × branch-staat
→ JobStatus, met SKIPPED gereserveerd voor al-gemergd werk en FAILED
voor échte fouten. Plus StoryLog-verplichting (log_implementation,
log_commit, log_test_result) en idempotency-protocol vóór schrijven.

PBI-33 batch (5-5 22:22) gedocumenteerd als case-study: drie
protocol-overtredingen die deze runbook + de nieuwe SKIPPED-status
aanpakken.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 23:13:49 +02:00
deb70a9e20 feat(T-572): map SKIPPED in lib/job-status + alle terminal-checks
- lib/job-status.ts: SKIPPED ↔ 'skipped' mapping in beide richtingen
- components/shared/job-status.ts: label "Overgeslagen" + neutrale italic styling
- actions/admin/jobs.ts: cancel-guard erkent SKIPPED als eindstatus
- app/api/cron/cleanup-agent-artifacts: SKIPPED ook opruimen na 7d
- lib/insights/agent-throughput: SKIPPED telt mee als terminal

ACTIVE_JOB_STATUSES bewust ongewijzigd — SKIPPED is afgerond.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 23:10:14 +02:00
2f41f8917a docs: idea-dialog profile (M12 T-513)
docs/specs/dialogs/idea.md:
- Velden-table with bron-zod links
- URL/state-pattern: dedicated route /ideas/[id] (afwijking van generieke
  modal-spec — rationale documented)
- 4-tab layout spec
- Full state-machine table with transition triggers + server actions
- Server-action catalog with preconditions + foutcodes
- 3-layer demo-policy (proxy + isDemo-guard + DemoTooltip), incl. wat
  demo WEL mag (download-md is read-only)
- Special behaviors: Cmd/Ctrl+S, localStorage draft (lazy seed),
  useMemo-derived validation, status-badge tokens, connectedWorkers
  via solo-store
- Realtime routing notes
- Test-fixture inventory (90+ cases across 7 test files)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 21:41:00 +02:00
300e426a4e schema: add Idea + IdeaLog models, extend ClaudeJob/Question for ideas (M12 T-491)
- new enums IdeaStatus, ClaudeJobKind, IdeaLogType
- new models Idea (with @@unique([user_id, code]) + pbi_id @unique) and IdeaLog
- User.idea_code_counter Int @default(0) for IDEA-{nnn} code generation
- ClaudeJob.task_id nullable; new idea_id + kind fields + index
- ClaudeQuestion.story_id nullable; new idea_id field + index
- existing call sites narrowed to story-questions / task-jobs (idea-paths come in T-502+)
- includes the M12 plan doc copied from /Users/janpetervisser/.claude/plans

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 19:25:07 +02:00
b225c83ace docs: v1.0 smoke-test checklist + readiness-doc bijgewerkt
docs/runbooks/v1-smoke-test.md (NIEUW): 11-secties handmatige checklist
voor de v1.0-pre-launch verificatie — auth, mobile UA-redirect, happy-path
flow, mobile shell, edit-flows, demo-policy, rate-limiting steekproef,
realtime, debug-routes 404 in productie, Lighthouse a11y per pagina,
rollback-trigger.

v1-readiness.md: 4 Before-launch items afgevinkt (demo-policy, privacy,
README, CHANGELOG); smoke-test verwijst nu naar de checklist; PWA-test
en v1.0.0-bump zijn de twee resterende handmatige items.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 14:18:25 +02:00
Scrum4Me Agent
b79510f5c6 docs: voeg flow-per-scherm toe aan Mobile shell sectie (ST-cmolqa8ma001xq517ree6u5v5)
Acceptatiecriteria vroeg om 'flow per scherm' beschrijving in de
Mobile shell sectie. Toegevoegd: stap-voor-stap flow voor Settings,
Backlog en Solo schermen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 11:28:11 +02:00
222928b1b4 docs: v1.0 readiness checklist
Living document onder docs/plans/v1-readiness.md. Vier secties (Now/Next/
Before launch/Later) met concrete actions voor de stap van v0.9.0 → v1.0.0.

Now-kandidaten:
- Edit-icoon op Product (todo cmoq3ox51 — UI-gat)
- Sentry/error-monitoring
- Rate-limiting op alle mutation-endpoints
- Accessibility-audit (Lighthouse a11y >=95)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:15:09 +02:00
3887e07af2 docs(PBI-11): canoniek plan voor mobile-shell met drie architectuur-beslissingen
Vorige planlocatie (~/.claude/plans/twinkly-plotting-wombat.md) was overschreven
met ST-1209-plan; deze doc neemt het over.

Drie aanbevelingen verwerkt na evaluatie tegen huidige codebase:
- A. Gedeelde entityDialogContentClasses muteren (dekt ST-1133 + ST-1138 in één edit)
- B. Eigen route group app/(mobile)/ — nested layout kan parent-NavBar niet onderdrukken
- C. Gescheiden SplitPane cookie-key voor mobile (backlog-3-mobile)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 09:40:03 +02:00
4b0ab8e4b2 feat(answer-modal): conform aan dialog-pattern + entity-profile
Story 7 van PBI "Alle dialogen conform docs/patterns/dialog.md".

- lib/schemas/question-answer.ts — gedeeld zod-schema +
  ANSWER_MAX_CHARS constant
- actions/questions.ts gebruikt het gedeelde schema
- AnswerModal: entityDialog* layout-classes, useDirtyCloseGuard,
  useDialogSubmitShortcut, DemoTooltip rond submit + multiple-choice
  knoppen
- docs/specs/dialogs/answer-modal.md — entity-profile

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 07:34:56 +02:00
0a58557e9d feat(solo-dialogs): layout-conformance + entity-profielen
Story 6 van PBI "Alle dialogen conform docs/patterns/dialog.md".

- batch-enqueue-blocker-dialog: entityDialog* layout-classes
- task-detail-dialog: entityDialog* layout-classes (rest van interne
  layout blijft custom — hybride detail+blur-save view)
- docs/specs/dialogs/task-detail.md — profiel dat het blur-save +
  PATCH-route patroon documenteert (afwijking van klassieke
  Server-Action+form flow)
- docs/specs/dialogs/batch-enqueue-blocker.md — profiel voor
  informational confirm-dialog

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 07:32:57 +02:00
784791d8f9 feat(sprint-dialogs): conform aan dialog-pattern + entity-profile
Story 5 van PBI "Alle dialogen conform docs/patterns/dialog.md".

- lib/schemas/sprint.ts — gedeelde zod-schemas (create/dates/goal)
- actions/sprints.ts — code+fieldErrors voor 422; code: 403 voor
  auth/demo errors
- StartSprintButton dialog: useDirtyCloseGuard, useDialogSubmitShortcut,
  entityDialog* layout-classes; DemoTooltip op trigger; veld-niveau
  errors via fieldErrors
- SprintHeader's date- en complete-dialogen: zelfde behandeling; date-
  dialog krijgt dirty-guard, complete-dialog krijgt DemoTooltip op
  bevestigen
- docs/specs/dialogs/sprint.md — entity-profile dat alle drie de modes
  documenteert; consolidatie naar één SprintDialog component bewust
  uitgesteld
- Sprint-dates tests aangepast aan nieuwe action-shape

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 07:30:46 +02:00
01e77fc560 feat(story-dialog): conform aan dialog-pattern + AlertDialog delete
Story 4 van PBI "Alle dialogen conform docs/patterns/dialog.md".

- lib/schemas/story.ts — gedeeld zod-schema
- actions/stories.ts — code+fieldErrors voor 422; code: 403 voor auth/demo
- StoryDialog adopt useDirtyCloseGuard, useDialogSubmitShortcut,
  entityDialog* layout-classes
- Inline delete-confirm vervangen door AlertDialog (§10.4)
- docs/specs/dialogs/story.md — gaps weggewerkt; alleen bewuste
  afwijkingen blijven (header met badges, geen char-counter)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 07:26:56 +02:00
97dc4ee553 feat(pbi-dialog): conform aan dialog-pattern + DemoTooltip + dirty-guard
Story 3 van PBI "Alle dialogen conform docs/patterns/dialog.md".

- lib/schemas/pbi.ts — gedeeld zod-schema (createPbiSchema/updatePbiSchema)
- actions/pbis.ts — returnen nu code+fieldErrors (422) en code: 403 voor
  auth/demo errors
- PbiDialog adopt useDirtyCloseGuard, useDialogSubmitShortcut,
  entityDialog* layout-classes; submit-knop + Annuleren in DemoTooltip
- isDemo-prop toegevoegd, pbi-list geeft 'm door
- docs/specs/dialogs/pbi.md — "Bekende gaps" weggewerkt; alleen bewuste
  uitsluitingen blijven

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 07:23:14 +02:00
03a248b0fb feat(product-dialog): conform aan dialog-pattern + entity-profile
Story 2 van PBI "Alle dialogen conform docs/patterns/dialog.md".

- lib/schemas/product.ts — gedeeld zod-schema (Dialog API)
- actions/products.ts — createProductAction/updateProductAction returnen
  nu code+fieldErrors voor 422-validatie en code: 403 voor demo/auth
- ProductDialog adopt useDirtyCloseGuard, useDialogSubmitShortcut,
  entityDialog* layout-classes; 422-fieldErrors mappen naar form.setError
- docs/specs/dialogs/product.md — entity-profile

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 07:18:39 +02:00
Janpeter Visser
95c5bd1086
Merge pull request #70 from madhura68/feat/landing-local-first
Landing v2: lokaal-first propositie + architectuurdiagram
2026-05-04 06:29:41 +02:00
Janpeter Visser
d8e6a68d69
Merge pull request #69 from madhura68/docs/runbook-agent-flow-pitfalls
docs(runbook): agent-flow open issues & decision log
2026-05-03 20:38:20 +02:00
4ff50cb87e feat(landing): rewrite around local-first proposition + architecture diagram
Reframe the landing page around what makes Scrum4Me unique: code execution
stays on the developer's own hardware (laptop, NAS or VM). The Vercel UI and
Neon DB are a metadata coordination layer; source code never leaves the local
worker. Adds a mermaid-rendered architecture diagram (two-zone: Scrum4Me-stack
vs Jouw kant) with light/dark variants generated via mmdc.

Page changes (app/page.tsx):
- Hero: H1 "Plannen in de cloud. Uitvoeren op je eigen machine." + new
  subhead; CTA "Hoe het werkt" replaces "Demo bekijken" (anchors to
  #architectuur).
- New section §3 "Architectuur" with light/dark SVG and 4 callout-cards
  (Vercel · Neon · Lokale worker · GitHub) honestly describing what each
  component stores or runs.
- Feature grid: 6 cards (set C) — combines Sprint Board + Solo Paneel,
  adds Lokale Claude-agents, Realtime updates, Async vraagkanaal, Todo's.
  LIVE callout removed (folded into Realtime card).
- "Twee manieren"-section replaced by a Quickstart with concrete git-clone
  snippet for the MCP-server.
- Handleiding: 9 → 10 steps; MCP recommended in step 8; new step 9
  "Story laten uitvoeren" describing the Voer-uit / job-queue flow;
  step 1 mentions QR-pairing as alternative login.
- Footer: adds link to madhura68/scrum4me-mcp alongside the app repo.

Tooling:
- New docs/diagrams/architecture.mmd (mermaid source).
- New npm script "diagrams" generates light + dark SVG via mmdc; output
  committed to public/diagrams/. No prebuild hook (manual regenerate, like
  prisma generate / docs:index).
- Plan + grilling outcomes captured in docs/plans/landing-local-first.md.

Tracked under Scrum4Me story cmoq2qoik0001qa175iynfnaa
(PBI Marketing & Landingspagina, cmoq2q50s0000qa174rmrjove).

Verified: npm run lint (0 new errors), npm test (379/379), npm run build OK.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 20:24:10 +02:00
8d6bdef57e docs(runbook): agent-flow open issues & decision log
Bundelt vier valkuilen in de huidige agent-flow: PBI-ordering,
schema-conflicten bij parallelle migraties, branch-naam-collisies via
8-char suffix, cross-product orchestratie. Eerste is al gedekt door de
merge-policy PBI; de andere drie zijn entries onder anchor-PBI
"Agent-flow: openstaande beslissingen" (prio 4).

Lokaal commit; PR pas wanneer er meer aanverwante docs-changes zijn.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 17:09:39 +02:00
Janpeter Visser
c357c662e7
Agent batch-flow: lokaal committen, push + PR aan het eind (#66)
* docs(ST-1115): agent-batch flow — branch-only-at-start, commit lokaal per taak

- CLAUDE.md Track A: voeg stap 1 (branch aanmaken) toe, splits stap 6
  (commit, geen push) af, voeg stap 7-8 (herhaal / push+PR bij lege queue)
- Hardstop Push-regel: verduidelijkt dat commits lokaal accumuleren per
  taak; push pas bij lege queue of expliciete gebruikersbevestiging
- docs/runbooks/branch-and-commit.md: nieuwe subsectie "Agent-batch flow"
  met tabel (run-start / na taak / queue leeg) en single-task edge case

* docs(ST-1115): AGENTS.md branch-and-PR quick-reference tabel

* docs(ST-1115): end-to-end verificatie checklist 1 batch = 1 Vercel-deploy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 15:51:24 +02:00