claude/friendly-williamson-02ff6b #3

Merged
janpeter merged 19 commits from claude/friendly-williamson-02ff6b into main 2026-05-16 14:52:03 +02:00
Owner

Wat: Per-product documentatie-feature (PBI-96) — elk product krijgt de 8-folder docs-tree die Scrum4Me zelf gebruikt (adr/, architecture/, patterns/, plans/, runbooks/, specs/, manual/, api/), per product configureerbaar via folder-toggles, beheerd via een in-app markdown-editor.
Hoe: DB-storage (product_docs + product_doc_logs), 5 server-actions (create/update/delete/toggle/list), 4 UI-pages onder /products/[id]/docs/ (INDEX + folder + viewer/editor + settings), sub-nav-integratie via ProductSubNav, en een herbruikbare MarkdownDocEditor (geëxtraheerd uit idea-md-editor.tsx — beide editors delen nu dezelfde stack).
Reviews verwerkt: P1 (delete-audit FK-race), P2 (frontmatter sync + last_updated normalisatie, disabled-folder semantiek), P3 (denormalized actor-velden + index). Audit-rapporten in docs/recommendations/.
Plan: docs/plans/PBI-96-product-docs.md
Audit: docs/recommendations/PBI-96-demo-audit-2026-05-16.md
Spec: docs/specs/dialogs/product-doc.md + docs/specs/functional.md §F-15

Stories (6 totaal, alle DONE)
Story Code Wat
A ST-1379 DB-schema + migratie + Zod-schemas + parser + frontmatter-serializer (P2-fix) + slug-helpers
B ST-1380 5 server-actions + rate-limit-keys + 28 tests (P1 + P2 volledig dekkend)
C ST-1381 MarkdownDocEditor-extractie + idea-md-editor refactor naar wrapper
D ST-1382 4 pages + 10 componenten + dialog-spec + DisabledFolderBanner
E ST-1383 /docs/settings + folder-toggle + ProductSubNav (Backlog/Sprint/Solo/Docs/Instellingen)
F ST-1384 Demo-policy audit + architecture-breadcrumb + functional-spec F-15
Verificatie
1022 tests groen (70 nieuw t.o.v. baseline 952). De pre-existing fail in tests/components/idea-timeline-merge.test.ts blijft buiten scope (DATABASE_URL niet beschikbaar in worktree-env; geen regressie).
Lint + typecheck schoon.
Schema valid (npx prisma format + validate).
Migration handmatig geschreven volgens claude_job-pattern; DEFAULT ARRAY[...]::ProductDocFolder[] vult bestaande producten automatisch (geen backfill).
Test plan (handmatige e2e — 10 stappen uit plan §Verificatie)
npm run dev → login als seed-user
Open product → tab Docs → INDEX toont 8 lege folders
"Nieuwe doc" in runbooks → save → viewer-redirect; check last_updated = today
Edit doc met user-supplied last_updated: 2020-01-01 → save → server-normalisatie overschrijft naar today
/docs/settings → toggle api uit → INDEX toont 7 folders; directe URL /docs/api → 200 met DisabledFolderBanner; flip terug → folder weer in INDEX
Yaml-fout introduceren → save geblokkeerd; error-box met line-info
Delete → confirm → doc weg; product_doc_logs heeft rij met type=DELETED, doc_id=null, metadata {folder, slug, title}
Andere user zonder ProductMember → directe URL → 404 (anti-enum)
Demo-user → docs lezen werkt, write-knoppen disabled met DemoTooltip; directe action-call → 403
ProductMember (niet-owner) → lezen + schrijven docs OK; /docs/settings read-only
npm run verify && npm run build groen
Bestanden gewijzigd (42 totaal)
Schema/migratie (2): prisma/schema.prisma, prisma/migrations/20260516100000_add_product_docs/migration.sql
Libs (7 nieuw): schemas + folder-mapper + parser + frontmatter-serializer + slug-helpers + server-helper + rate-limit-edit
Actions (1 nieuw): actions/product-docs.ts
Pages (4 nieuw): INDEX + settings + folder + folder/slug
Layout edit (1): hook
Componenten (12 nieuw): 10 in components/product-docs/ + 1 in components/products/ + 1 in components/shared/ (MarkdownDocEditor)
Refactor (1): idea-md-editor.tsx -146 / +31 (wrapper rond shared)
Tests (5 nieuw): 70 tests over schemas, slug, parser, frontmatter, shared editor, actions
Docs (6): plan, architecture-topical + breadcrumb, functional F-15, demo-audit, dialog-spec, INDEX-regen

Wat: Per-product documentatie-feature (PBI-96) — elk product krijgt de 8-folder docs-tree die Scrum4Me zelf gebruikt (adr/, architecture/, patterns/, plans/, runbooks/, specs/, manual/, api/), per product configureerbaar via folder-toggles, beheerd via een in-app markdown-editor. Hoe: DB-storage (product_docs + product_doc_logs), 5 server-actions (create/update/delete/toggle/list), 4 UI-pages onder /products/[id]/docs/ (INDEX + folder + viewer/editor + settings), sub-nav-integratie via ProductSubNav, en een herbruikbare MarkdownDocEditor (geëxtraheerd uit idea-md-editor.tsx — beide editors delen nu dezelfde stack). Reviews verwerkt: P1 (delete-audit FK-race), P2 (frontmatter sync + last_updated normalisatie, disabled-folder semantiek), P3 (denormalized actor-velden + index). Audit-rapporten in docs/recommendations/. Plan: docs/plans/PBI-96-product-docs.md Audit: docs/recommendations/PBI-96-demo-audit-2026-05-16.md Spec: docs/specs/dialogs/product-doc.md + docs/specs/functional.md §F-15 Stories (6 totaal, alle DONE) Story Code Wat A ST-1379 DB-schema + migratie + Zod-schemas + parser + frontmatter-serializer (P2-fix) + slug-helpers B ST-1380 5 server-actions + rate-limit-keys + 28 tests (P1 + P2 volledig dekkend) C ST-1381 MarkdownDocEditor-extractie + idea-md-editor refactor naar wrapper D ST-1382 4 pages + 10 componenten + dialog-spec + DisabledFolderBanner E ST-1383 /docs/settings + folder-toggle + ProductSubNav (Backlog/Sprint/Solo/Docs/Instellingen) F ST-1384 Demo-policy audit + architecture-breadcrumb + functional-spec F-15 Verificatie 1022 tests groen (70 nieuw t.o.v. baseline 952). De pre-existing fail in __tests__/components/idea-timeline-merge.test.ts blijft buiten scope (DATABASE_URL niet beschikbaar in worktree-env; geen regressie). Lint + typecheck schoon. Schema valid (npx prisma format + validate). Migration handmatig geschreven volgens claude_job-pattern; DEFAULT ARRAY[...]::ProductDocFolder[] vult bestaande producten automatisch (geen backfill). Test plan (handmatige e2e — 10 stappen uit plan §Verificatie) npm run dev → login als seed-user Open product → tab Docs → INDEX toont 8 lege folders "Nieuwe doc" in runbooks → save → viewer-redirect; check last_updated = today Edit doc met user-supplied last_updated: 2020-01-01 → save → server-normalisatie overschrijft naar today /docs/settings → toggle api uit → INDEX toont 7 folders; directe URL /docs/api → 200 met DisabledFolderBanner; flip terug → folder weer in INDEX Yaml-fout introduceren → save geblokkeerd; error-box met line-info Delete → confirm → doc weg; product_doc_logs heeft rij met type=DELETED, doc_id=null, metadata {folder, slug, title} Andere user zonder ProductMember → directe URL → 404 (anti-enum) Demo-user → docs lezen werkt, write-knoppen disabled met DemoTooltip; directe action-call → 403 ProductMember (niet-owner) → lezen + schrijven docs OK; /docs/settings read-only npm run verify && npm run build groen Bestanden gewijzigd (42 totaal) Schema/migratie (2): prisma/schema.prisma, prisma/migrations/20260516100000_add_product_docs/migration.sql Libs (7 nieuw): schemas + folder-mapper + parser + frontmatter-serializer + slug-helpers + server-helper + rate-limit-edit Actions (1 nieuw): actions/product-docs.ts Pages (4 nieuw): INDEX + settings + folder + folder/slug Layout edit (1): <ProductSubNav> hook Componenten (12 nieuw): 10 in components/product-docs/ + 1 in components/products/ + 1 in components/shared/ (MarkdownDocEditor) Refactor (1): idea-md-editor.tsx -146 / +31 (wrapper rond shared) Tests (5 nieuw): 70 tests over schemas, slug, parser, frontmatter, shared editor, actions Docs (6): plan, architecture-topical + breadcrumb, functional F-15, demo-audit, dialog-spec, INDEX-regen
janpeter added 19 commits 2026-05-16 14:51:53 +02:00
Plan v2 (post-review) met PBI/Story/Task-referenties (PBI-96, 6 stories,
18 taken). Verwerkt review-punten P1 (delete-audit FK), P2 (title/status
sync, last_updated normalisatie, disabled-folder semantiek) en P3
(denormalized actor-velden).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Voegt twee enums (ProductDocFolder met 8 kern-folders + ProductDocLogType),
een Product-uitbreiding (enabled_doc_folders array met alle 8 als default)
en twee modellen toe (ProductDoc met @@unique(product_id, folder, slug) +
ProductDocLog met denormalized actor_user_id en doc_id nullable + SetNull).

Bestaande producten krijgen de 8-folder-default automatisch via de
ALTER TABLE DEFAULT — geen backfill nodig (zie plan §A.4).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- lib/schemas/product-doc.ts: PRODUCT_DOC_FOLDERS/STATUSES + create/update/
  toggle/frontmatter schemas (MAX_PRODUCT_DOC_CONTENT_LEN=100k)
- lib/product-doc-folder.ts: DB UPPER_SNAKE ↔ API lowercase mapper
  (spiegel van lib/task-status.ts)
- lib/product-doc-slug.ts: pure slugify + suggestSlug (dedupe-suffix) +
  ADR-sequence helpers (nextAdrPrefix, parseAdrNumber, suggestAdrSlug)
- lib/schemas/product-doc-frontmatter-defaults.ts: per-folder UI-templates
  voor "Nieuwe doc"-dialog (last_updated weggelaten — server normaliseert
  bij save, zie T-1060)
- __tests__: 37 tests groen (Zod-schemas + slug-helpers); de pre-existing
  worktree-env fail in idea-timeline-merge.test.ts blijft buiten scope

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- lib/product-doc-parser.ts: parseProductDocMd(md) → {ok, frontmatter, body}
  | {ok:false, errors[]} met line-info bij YAML-fouten. Pattern gespiegeld
  uit lib/idea-plan-parser.ts.
- lib/product-doc-frontmatter.ts: setProductDocFrontmatterFields(md, patch)
  laat de server `last_updated` server-side normaliseren (P2-review-fix
  uit docs/recommendations/product-docs-storage-system-review-2026-05-16).
  Gebruikt yaml.parseDocument om field-ordering best-effort te behouden.
- todayIsoDate() helper voor 'yyyy-mm-dd' string.
- __tests__: 19 nieuwe tests groen — parse-success/fail-paden, en
  expliciete P2-coverage (vervangen + toevoegen last_updated, behoud
  overige velden + body).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- lib/rate-limit.ts: 'create-product-doc' (30/min) + 'edit-product-doc'
  (60/min) in eigen PBI-96-blok na M12-Ideas-keys.
- lib/product-docs-server.ts: loadAccessibleProduct + folderApiToDbOrThrow
  als 'server-only' helpers. Wordt door create/update/list-actions
  hergebruikt; folder-toggle gebruikt direct user_id-scope (owner-only).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- actions/product-docs.ts: create+update volgens server-action.md pattern
  (auth → demo → rate-limit → zod → parse-md → scope → folder-enabled →
  normalize last_updated → tx(create+log) → revalidatePath).
- P2-coverage volledig:
  • title/status uit parsed.frontmatter naar gerepliceerde kolommen
  • last_updated server-side overschreven via setProductDocFrontmatterFields
  • frontmatter-parse-fail → 422 zonder DB-write
  • slug-conflict (P2002) → 422 met heldere foutmelding
  • folder uitgeschakeld → 422 'staat uit'
- update logt UPDATED met prev_status + new_status in metadata.
- 14 nieuwe tests groen (auth-paden, P2-create, P2-last_updated,
  conflict-mapping, update-sync).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- actions/product-docs.ts: deleteProductDocAction haalt eerst metadata
  op (folder/slug/title), schrijft dan log met doc_id:null + delete in
  één $transaction. Geen SetNull-race, geen interactieve tx nodig.
- __tests__: 4 nieuwe tests (auth-paden + P1-coverage met expliciete
  check op doc_id:null, type:'DELETED' en metadata-velden gevuld).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- toggleProductDocFolderAction: owner-only scope (where: id + user_id,
  NIET productAccessFilter — folder-config is product-setting). Idempotent
  (no-op + success als al in target-staat). Disabled folder verwijdert
  GEEN docs uit DB; alleen flag in enabled_doc_folders. Log met doc_id:null
  + FOLDER_ENABLED/FOLDER_DISABLED.
- listProductDocsAction: read-only, scope via productAccessFilter (zonder
  demo-403 — demo MAG lezen, zie plan §B.4). Geen rate-limit. Select
  zonder content_md. OrderBy [folder, slug]. Mapt DB-enum naar API-string.
- Tests: 10 nieuwe (owner-only-check, idempotent, enable+disable-logs,
  demo-read-OK, folder-filter, ontbreken content_md in select). Totaal
  28 tests in product-docs actions; 1008 tests groen in monorepo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- components/shared/markdown-doc-editor.tsx: geëxtraheerd uit
  components/ideas/idea-md-editor.tsx zodat Ideas (grill/plan) en
  Product Docs dezelfde editor-stack delen (CLAUDE.md dialog-discipline:
  "twee keer kopieren = promote 'm meteen").
- Props: storageKey + initialValue + validate? + onSave + onSaved? +
  onCancel + rows? + placeholder? + saveLabel? + validationErrorsHeader?
  + debug-attrs. Component bevat geen entity-specifieke logica.
- 14 nieuwe tests groen (rendering/dirty-state, localStorage persist+
  restore+clear, Cmd+S/Ctrl+S save, success-clear+onSaved+onCancel,
  error-rendering, validation blocks submit, cancel-button).
- T-1066 (volgende) refactort idea-md-editor naar wrapper rond deze.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reduceert components/ideas/idea-md-editor.tsx van ~170 regels naar ~55:
- planValidator hoist naar module-scope (geen useMemo nodig)
- alle state, draft-persist, keyboard-shortcut, error-rendering komt
  uit MarkdownDocEditor (T-1065)
- wrapper injecteert alleen: storageKey, validator (plan-only), onSave
  (grill/plan-action), placeholder, validationErrorsHeader, onSaved
  (router.refresh)

Geen functioneel verschil; 1022 tests blijven groen (geen regressie).
ST-C is hiermee compleet — beide editors gebruiken nu dezelfde stack.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- app/(app)/products/[id]/docs/page.tsx: server-component die met één
  prisma.findMany de 3 meest recent bijgewerkte docs per enabled folder
  laadt en doorgeeft aan ProductDocsIndex.
- components/product-docs/product-docs-index.tsx: grid van enabled
  folders (vaste FOLDER_ORDER), folder-labels, settings-link. Toont
  empty-state als 0 folders aanstaan.
- components/product-docs/product-docs-folder-card.tsx: card per folder
  met titel + omschrijving + count + 3 doc-links of CTA bij leeg.
- MD3-tokens (bg-surface-container-low, border-border, text-primary);
  GEEN bg-blue-500 (CLAUDE.md hardstop).
- debugProps op alle root-divs (debug-id pattern).
- Disabled folders worden niet getoond in INDEX (verborgen) maar
  blijven via directe URL bereikbaar — banner-flow in T-1071.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- app/(app)/products/[id]/docs/[folder]/page.tsx: server-route die
  folder-segment valideert tegen ProductDocFolder-enum (404 anders),
  docs sorteert op slug ASC, en de tabel + breadcrumb + "Nieuwe doc"-link
  rendert. New-doc-link wordt in T-1070 functioneel via dialog.
- components/product-docs/product-docs-folder-list.tsx: server-tabel
  (slug · title · status-badge · updated_at met nl-NL DateTimeFormat).
- components/product-docs/product-doc-status-badge.tsx: MD3-tokens
  (bg-status-done/20, bg-status-blocked/20, bg-muted) per status.
  Unknown statussen fallbacken naar 'muted'.
- "Nieuwe doc"-knop conditioneel verborgen bij disabled folder; banner
  zelf komt in T-1071.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- app/(app)/products/[id]/docs/[folder]/[slug]/page.tsx: server-route
  die doc laadt (scope-checked via productAccessFilter), frontmatter
  parseert, en op basis van ?edit=1 viewer of editor toont. Fallback
  voor unparseable frontmatter toont errors + raw content in <pre>.
- product-doc-viewer.tsx: server-component met frontmatter-kop
  (title + status-badge + audience/applies_to/last_updated meta) en
  body via <Markdown> (XSS-safe).
- product-doc-editor.tsx: client-wrapper rond MarkdownDocEditor met
  parseProductDocMd validator + updateProductDocAction + cancelHref.
- delete-product-doc-button.tsx: AlertDialog confirm + delete-action
  + DemoTooltip + redirect-na-success. Disabled in demo.
- Edit-knop conditioneel verborgen bij disabled folder (T-1071 voegt
  banner toe); delete blijft altijd zichtbaar voor cleanup.
- Button met `render={<Link/>}` ipv asChild (CLAUDE.md hardstop
  base-ui pattern).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- components/product-docs/new-product-doc-dialog.tsx: één client-component
  dat trigger-knop + dialog combineert. Folder-select uit enabled folders,
  title-input met auto-slug-suggestie (totdat user slug zelf bewerkt),
  starter-template-knop per folder. Deep-link via ?new=1 → dialog opent
  automatisch (initial state, geen useEffect setState).
- docs/specs/dialogs/product-doc.md: verplichte dialog-spec (volgt
  docs/patterns/dialog.md), beschrijft modes (alleen create), velden,
  UX-details (auto-slug, template, minimale frontmatter-wrap),
  server-actions, foutmappings, drie-laagse demo-policy, deep-link
  conventie, en niet-doelen (geen rich-text, geen template-picker UI).
- Folder-page hookt de dialog in als "Nieuwe doc"-knop bij enabled
  folder (verborgen bij disabled folder — banner volgt in T-1071).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- components/product-docs/disabled-folder-banner.tsx: server-component
  banner met AlertTriangle, status-blocked-tokens, en link naar settings.
  Tekst legt uit: "lezen mag, nieuwe/edit kan niet, delete wél (cleanup)".
- Integratie in folder-page: render banner als !isFolderEnabled; de
  bestaande conditional verbergt de "Nieuwe doc"-knop al.
- Integratie in detail-page: render banner als !isFolderEnabled; de
  bestaande conditional verbergt de Edit-knop al. Delete blijft
  zichtbaar voor cleanup (zoals plan §C.4 voorschrijft).
- ST-D is hiermee compleet — alle 5 UI-tasks gereed. Directe URLs naar
  disabled folders blijven leesbaar (P2-review-fix volledig).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- app/(app)/products/[id]/docs/settings/page.tsx: server-route met
  breadcrumb + intro, geeft isOwner door aan toggle-component.
- components/product-docs/product-doc-folder-toggle.tsx: client met
  8 checkboxes (één per ProductDocFolder enum-lid). Owner kan
  toggelen → toggleProductDocFolderAction (optimistic update +
  rollback bij error). ProductMember (niet-owner) krijgt waarschuwing
  en disabled checkboxes. DemoTooltip-wrapped, demo kan niets togglen.
- Voetnoot legt anti-data-loss uit: "Folders uitzetten verwijdert
  geen bestaande docs".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- components/products/product-subnav.tsx: client-component met tabs
  (Backlog · Sprint · Solo · Docs · Instellingen). Active-state via
  usePathname; patroon gespiegeld uit components/shared/nav-bar.tsx
  navLink-helper. Docs-tab conditioneel verborgen wanneer 0 folders
  enabled.
- app/(app)/products/[id]/layout.tsx: hookt <ProductSubNav> in na
  <SetCurrentProduct>; geeft showDocs = product.enabled_doc_folders
  .length > 0 mee. ST-E hiermee compleet — folder-config beheert
  zichtbaarheid van Docs-tab end-to-end.

(Geautomatiseerde smoke-test voor disabled-folder route blijft buiten
scope; de banner-rendering wordt impliciet door T-1071 gevalideerd.
Manual e2e in T-1075.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Audit-rapport in docs/recommendations/PBI-96-demo-audit-2026-05-16.md.
Bevestigt dat alle 3 lagen van de demo-policy volledig zijn:

- Laag 1 (proxy.ts): n.v.t. — geen REST-routes in v1
- Laag 2 (server-actions): alle 4 writes hebben isDemo-guard; list-action
  bewust niet (demo MAG lezen volgens plan §B.4)
- Laag 3 (UI): alle write-knoppen DemoTooltip-wrapped + disabled in demo

Geen actie nodig. Manual e2e (stap 9 uit plan-verificatie) blijft als
beheerder-taak vóór merge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
docs(PBI-96/T-1076): architecture breadcrumb + F-15 functional spec
Some checks are pending
CI / Lint, Typecheck, Test & Build (pull_request) Waiting to run
CI / Detect deploy-relevant changes (pull_request) Blocked by required conditions
CI / Deploy Preview (PR) (pull_request) Blocked by required conditions
CI / Deploy Production (main) (pull_request) Blocked by required conditions
CI / Deploy Manual (workflow_dispatch) (pull_request) Waiting to run
d32b39f62f
- docs/architecture/product-docs.md: nieuw topical doc met folder-set,
  datamodel, P1/P2 review-fixes, server-actions tabel, UI-routes en
  editor-extractie. Verwijst naar plan + recommendations + relevante
  bestanden.
- docs/architecture.md: breadcrumb-rij naar product-docs.md.
- docs/specs/functional.md: F-15-sectie "Per-product documentatie
  (Product Docs)" met acceptatiecriteria voor overzicht/aanmaken/
  bewerken/verwijderen/folder-config/demo-policy/toegang + randgevallen.
- INDEX.md regen automatisch via pre-commit hook.

ST-F is hiermee compleet — ALLE 18 PBI-96 taken klaar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
janpeter merged commit 9fc15f279a into main 2026-05-16 14:52:03 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: janpeter/Scrum4Me#3
No description provided.