* feat(PBI-76): one-shot localStorage→user-settings migration helper
Reads all legacy keys (sprint_pb_*, pbi_*, story_sort, debug-mode,
and dynamic *_filter_kind/*_filter_status for jobs columns) and
returns a typed UserSettings patch plus the keys to clear.
Idempotent via scrum4me:settings_migrated=v1 marker. Skips invalid
values silently so existing corrupt entries do not block migration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(PBI-76): bridge runs one-shot localStorage migration
After hydrate, scans legacy localStorage keys via buildMigrationPatch
and, if any data is found, pushes one bulk patch to the server,
applies it locally, then removes the legacy keys. Demo accounts skip
the migration entirely. Cancellable on unmount to avoid setState on
unmounted component.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(PBI-76): migrate sprint-backlog to user-settings store
Replaces six useState+useEffect+localStorage flows with selectors
from useUserSettingsStore. Defaults are applied at the selector
level (filterStatus 'OPEN', sort 'code', etc) so the component
matches its previous behaviour. The collapsed Set is derived from
the persisted array, falling back to auto-collapse-DONE when no
preference exists yet. setPref calls are fire-and-forget — the
optimistic flow handles the local state update.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(PBI-76): migrate pbi-list to user-settings store
Same pattern as sprint-backlog: replaces local useState +
localStorage hydration/persist with selectors from
useUserSettingsStore. filterPopoverOpen blijft lokaal — die
was nooit gepersisteerd in pbi-list.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(PBI-76): migrate story-panel sort to user-settings store
Single pref (sortMode) — replaces sync localStorage useState
initializer with a selector. Default 'priority' applied at
the read site.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(PBI-76): migrate jobs-column to user-settings store
Per-instance filter state (kinds + statuses) now lives under
views.jobsColumns[storageKeyPrefix] in user-settings. Removes
the local CSV-encoding helpers — store keeps arrays natively.
A single persist() call writes both fields together so the
two arrays cannot drift in optimistic mid-flight updates.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(PBI-76): migrate debug-mode to user-settings store
DebugToggle reads debugMode from user-settings.devTools and
toggles via setPref. Removes the standalone stores/debug-store.ts
(no consumers left). Body classlist update only fires after the
store is hydrated to avoid a flash on initial paint.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(PBI-76): remove unused readLocalStoragePref helper
No consumers left after migrating sprint-backlog, pbi-list,
story-panel, jobs-column, and debug-store to user-settings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(PBI-76): mock user-settings action in backlog integration test
PbiList now imports the user-settings store, which transitively
loads actions/user-settings.ts → lib/prisma. The vitest jsdom
environment has no DATABASE_URL, so we add a mock alongside the
existing action mocks.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(docs): allow balanced parens in markdown link URLs
Previously the link-checker regex stopped at the first ')',
breaking on Next.js route-group paths like `app/(app)/...`. The
new regex matches one level of balanced parens inside the URL.
Caught by CI on PR #188 — pre-existing breakage from PBI-78 plan
doc that was already merged on main.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Move sprint switcher into sprint header, centered between title and actions
- Extract BacklogFilterPopover as shared component used by sprint and product backlog
- Add sort options (code/priority/status) with single-pill asc/desc toggle
- Default sprint backlog status filter to OPEN, remove "alleen niet klaar" button
- Persist collapsed state and filter popover open in localStorage
- Fix hydration flicker: defer localStorage read to useEffect with prefsLoaded gate for writes
- Increase sprint switcher text size for readability
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
* 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
* 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
* 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=<id> 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>
- StartSprintButton dialog toont 3-state banner: info met accurate vrije-
stories count + PBI-context, of waarschuwing als geen PBI geselecteerd
is, of waarschuwing als de geselecteerde PBI 0 vrije stories heeft
- Voeg sprint_id toe aan BacklogStory/Story/SprintStory + select in PB-
pagina's en sprint-board mappings, zodat de banner accuraat kan tellen
- createSprintAction: revalidatePath met 'layout' flag voor consistency
met createSprintWithPbisAction (top-nav 'Sprint' link ververst direct)
Sprint-switch data-refresh op alle relevante pagina's:
- BacklogHydrationWrapper: fingerprint-based re-hydratie zodat PB-data
na router.refresh opnieuw uit nieuwe initialData komt (was: useEffect
met lege deps draaide alleen 1x)
- SprintBoardClient: key={sprint.id} forceert remount bij sprint-switch
zodat lokale sprintStories/sprintStoryIds-state vers ge-init wordt
- Solo (desktop + mobile): gebruik resolveActiveSprint(id) ipv eerste
OPEN-sprint, plus key={sprint.id} op SoloBoard voor remount
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: verplaats sprint-switcher van NavBar naar product-header
Sprint-pulldown zit nu in de bestaande balk op de product backlog
(naast Sprint starten / Instellingen) i.p.v. in het midden van de
NavBar. Alleen zichtbaar wanneer het product ook het actieve product
van de gebruiker is.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: sync package-lock.json version naar 1.2.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: centreer sprint-switcher en verwijder badges uit dropdown items
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: vervang sprint-status badge door subtle tekst
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: toon code + titel + status in sprint-switcher dropdown items
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: cookie-write uit Server Component (Next.js 16 verbiedt dit)
setActiveSprintCookie werd direct aangeroepen in app/(app)/products/[id]/sprint/[sprintId]/page.tsx,
wat in Next.js 16 een runtime-error oplevert ('Cookies can only be modified in a Server Action
or Route Handler'). Vervangen door een client-side bridge die syncActiveSprintCookieAction
aanroept na mount, zodat de active-sprint cookie nog steeds gesynced blijft met de URL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: filter 'toon afgeronde sprints' in sprint-switcher dropdown
Default verbergt de switcher gesloten/gearchiveerde/mislukte sprints
(toont alleen open + de huidige actieve sprint). Toggle bovenaan de
lijst om alle sprints te tonen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: nieuwe sprint wordt direct geselecteerd zonder redirect
createSprintAction zet nu de active-sprint cookie naar de zojuist
aangemaakte sprint, en de StartSprintButton refresht de huidige
pagina i.p.v. te redirecten naar /sprint. Resultaat: gebruiker blijft
op de product backlog en ziet de nieuwe sprint direct geselecteerd
in de sprint-pulldown.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: verplaats Manual en Admin naar user-menu dropdown
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: voeg geselecteerde PBI automatisch toe aan nieuwe sprint
Bij sprint-aanmaak wordt de pbi_id uit de selection-store als hidden
form-field meegestuurd. Server-side worden alle stories van die PBI
(zonder sprint) en hun taken aan de nieuwe sprint gekoppeld; stories
krijgen status IN_SPRINT met incrementele sort_order.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: sprint-switcher op solo- en sprint-board pagina's
Sprint-switcher is nu beschikbaar op de drie hoofdpagina's: product
backlog, solo board en sprint board. Allen renderen 'm in een
gecentreerde balk net onder de NavBar. Sprint-data via gedeelde helper
getSprintSwitcherData.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Voegt een verplicht code-veld toe aan Sprint, sequentieel per product
(consistent met PBI-N, ST-NNN, T-N).
- **Schema** — `Sprint.code String @db.VarChar(30)` + `@@unique([product_id, code])`
- **Migratie** — voegt kolom toe als nullable, backfillt bestaande sprints
via `ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY created_at)`
als `SP-N`, en zet daarna NOT NULL + UNIQUE.
- **Generator** — `generateNextSprintCode(productId)` in lib/code-server.ts
volgt het patroon van story/pbi/task; createSprintAction gebruikt
`createWithCodeRetry` voor race-bescherming.
- **Seed** — sprint-counter per product (`SP-1`, `SP-2`, ...).
Zichtbaar in:
- Sprint-header (`Product › Sprint actief · SP-3`)
- JobCard + JobDetailPane voor SPRINT_IMPLEMENTATION jobs
- Insights: VelocityChart x-axis (compacter dan goal-truncated),
AlignmentTrend tooltip, SprintInfoStrip
- actions/jobs-page.ts: `sprintCode` is weer een echte code i.p.v. null
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(PBI-33): chat-kanaal UI — IdeaTimeline merge + UserChatInput
Voltooit de UI-laag van PLAN_CHAT (gebruikersvragen over plan, Claude
antwoordt async). Backend (UserQuestion model, createUserQuestionAction,
SSE-handling, server-side prop-passing) was al aanwezig — alleen de
UI-koppeling ontbrak waardoor userQuestions ongebruikt bleven.
- IdeaDetailLayout geeft userQuestions/planMd/ideaId/isDemo door aan
IdeaTimeline en telt user-questions mee in de tab-count
- IdeaTimeline mergt user-questions chronologisch met logs+questions,
rendert ze met MessageCircle-icoon en pending/answered status, en
toont onderaan UserChatInput wanneer plan_md aanwezig is
- UserChatInput nieuw component met textarea + verzend-knop dat
createUserQuestionAction aanroept en op success router.refresh()
triggert zodat SSE de pending-state oppikt
- useNotificationsRealtime: router toegevoegd aan useEffect-deps zodat
router.refresh() op user_question/idea-job events werkt zonder
stale-closure waarschuwing
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(lint): unused vars/imports + react-hook-form watch incompatibility
Resolves de overige lint-warnings van de gefaalde sprint-build die los
staan van PBI-33. Eslint-config staat unused vars/args toe als ze met
'_' prefixen, dus required interface-params krijgen een prefix terwijl
losse dode constantes/imports verwijderd worden.
- sprint-header: productId is required prop maar nog niet gebruikt
→ prefix _productId i.p.v. verwijderen (caller passeert het door)
- agent-throughput: STATUSES-constante was dood — verwijderd, queries
gebruiken hardcoded status-velden in de perDay-loop
- claude-jobs: productAccessFilter en enforceUserRateLimit waren
dode imports — verwijderd
- story-log.test: ongebruikte 'data' binding vervangen door bare
await res.json() zodat de stream nog wel geconsumeerd wordt
- product-dialog: form.watch('auto_pr') vervangen door useWatch met
control-prop — useWatch is veilig voor React Compiler memoization
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 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>
Scrum4Me-side counterpart of scrum4me-mcp@f7f5a48 (PBI-9 + PBI-47):
- prisma migration: ClaudeJob.{base_sha,head_sha} + SprintRun.pause_context
- lib/pause-context.ts: Zod schema + parsePauseContext + pauseReasonLabel
helper; single source of truth for the JSON pause_context shape produced
by the mcp sprint-run flow (MERGE_CONFLICT pause)
- actions/sprint-runs.ts: resumePausedSprintRunAction — separate from the
existing FAILED-resume flow, requires SprintRun.status === PAUSED, closes
the linked ClaudeQuestion, clears pause_context, sets RUNNING/QUEUED based
on whether a claim is still active
- components/sprint/sprint-run-controls.tsx: PAUSED banner with reason label,
PR link, conflict-files list (max 5 + "+N more"), Resume button with
confirm() guard
- app/(app)/products/[id]/sprint/page.tsx: load pause_context from active
SprintRun and pass through to SprintRunControls
All MD3 tokens (warning-container, on-warning-container, primary). No raw
Tailwind utility colours.
Tests: 532 passing across 72 files (Scrum4Me side).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ST-1243: F1 schema + propagateStatusUpwards-helper voor sprint-flow
Schema-uitbreidingen voor de sprint-niveau jobflow (PBI-46):
- TaskStatus, StoryStatus, PbiStatus, SprintStatus krijgen FAILED
- Nieuwe enums: SprintRunStatus, PrStrategy
- Nieuw SprintRun-model dat per-task ClaudeJobs groepeert
- ClaudeJob.sprint_run_id koppeling + index
- Product.pr_strategy (default SPRINT)
- Bijhorende Prisma-migratie
propagateStatusUpwards vervangt updateTaskStatusWithStoryPromotion en
herevalueert de keten Task → Story → PBI → Sprint → SprintRun bij elke
task-statuswijziging. Bij FAILED cancelt het sibling-jobs in dezelfde
SprintRun. PBI-status BLOCKED blijft handmatig en wordt niet overschreven.
Status-mappers + theme krijgen failed-token + label-uitbreidingen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ST-1244: F2 sprint-runs actions + deprecate per-task enqueues
actions/sprint-runs.ts (nieuw):
- startSprintRunAction met pre-flight (impl_plan / open ClaudeQuestion / PBI BLOCKED|FAILED)
- Maakt SprintRun + ClaudeJobs in PBI→Story→Task volgorde
- resumeSprintAction zet FAILED tasks/stories/PBIs terug en start nieuwe SprintRun
- cancelSprintRunAction breekt lopende SprintRun af zonder cascade
actions/claude-jobs.ts:
- enqueueClaudeJobAction, enqueueAllTodoJobsAction, previewEnqueueAllAction,
enqueueClaudeJobsBatchAction nu deprecation-stubs (UI-cleanup volgt in F4)
- cancelClaudeJobAction blijft beschikbaar voor losse jobs
Tests bijgewerkt: 11 nieuwe sprint-runs tests, claude-jobs(-batch) tests
herzien naar deprecation-asserties.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ST-1246: F4 UI Start/Resume/Cancel sprint + pr_strategy dropdown
- components/sprint/sprint-run-controls.tsx: knoppen Start Sprint
(sprintStatus=ACTIVE), Hervat sprint (sprintStatus=FAILED) en
Annuleer sprint-run (lopende run). Pre-flight blocker-modal toont
blockers met directe links naar de relevante pagina's.
- components/products/pr-strategy-select.tsx: dropdown SPRINT|STORY in
product-settings, met optimistic update + sonner-toast op fail.
- actions/products.ts: updatePrStrategyAction (eigenaar-only, demo-block).
- Sprint-page: query op actieve SprintRun + tonen van controls-balk.
Live cascade-visualisatie (T-634) staat als follow-up genoteerd —
huidige sprint-board statusbadges volstaan voor MVP. De Solo-board
"Voer uit"-knoppen zijn niet expliciet verwijderd; ze tonen nu de
deprecation-error van de gestubde actions tot de Solo-flow opnieuw
ontworpen wordt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Voegt todayLocalDate()-helper toe (toLocaleDateString('en-CA') voor YYYY-MM-DD
zonder UTC-drift) en gebruikt hem als defaultValue op start_date en end_date.
Dialog unmount bij sluiten zorgt automatisch voor reset naar vandaag bij heropenen.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-n1csfo4j): AlertDialog imports, state en transition voor Alles-op-done knop
Voegt AlertDialog-imports, setAllSprintTasksDoneAction-import, productId-prop
(hernoemd van _productId) en showAllDoneConfirm/isSettingAllDone state toe aan
sprint-header.tsx als voorbereiding op de Alles-op-done AlertDialog-knop.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-n1csfo4j): handleAllDone + AlertDialog + Alles-op-done knop in sprint-afronden-dialog
Voegt handleAllDone toe (roept setAllSprintTasksDoneAction aan en zet alle
per-story decisions op DONE in de UI), een bevestigende AlertDialog en een
'Alles op done'-knop bovenaan de story-lijst in de sprint-afronden-dialog.
Voegt setAllSprintTasksDoneAction ook toe aan actions/sprints.ts omdat die
branch nog niet op main staat.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Hover-zichtbaar ✎-icoon rechts uitgelijnd op iedere taak-rij; opent dezelfde
edit-dialog als een rij-klik (visuele cue, consistent met PBI/story-rijen).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- PBI-rij in Product Backlog-kolom: ✎-icoon rechts uitgelijnd, opent PbiDialog
(rij is nu div role=button i.p.v. nested-button)
- Story-rij in Sprint Backlog-kolom: ✎-icoon vóór de Trash, opent StoryDialog
- SprintStory + PbiWithStories verrijkt met velden die de dialogen lezen
(description / acceptance_criteria / pbi_id / created_at op story; priority /
status / description op PBI)
- pbi.status via pbiStatusToApi → PbiStatusApi (DB UPPER_SNAKE → API lowercase)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spiegelt het filter-patroon van de Product Backlog-pagina (PbiList) naar de
Sprint-Product-Backlog-kolom: prioriteit + status pills, actieve-filter chips,
localStorage-persistentie. Bestaande collapse-/expand-/alleen-niet-klaar-knoppen
blijven naast de popover staan.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- TaskDialog: code-input boven titel (font-mono, optional,
placeholder "auto"), CodeBadge in dialog header bij edit
- EditTaskLoader: select code uit DB voor de dialog
- Solo page: vervang inline deriveTaskCode-logica door directe
task.code uit DB
- Sprint page + TaskList + SprintBoardClient: Task-type krijgt
verplicht code-veld; TaskList laat ongebruikte storyCode prop
vallen omdat code-derivatie niet meer nodig is
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
* chore(ST-1112): add deps for task dialog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-1112): add shared zod schema for task dialog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-1112): add missing MD3 tokens for task dialog
outline-variant, on-error-container, status-review (light + dark)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-1112): add saveTask and deleteTask server actions for TaskDialog
Unified create/edit action (saveTask) replaces separate formData-based
actions for the new TaskDialog. Uses shared zod schema, structured
SaveTaskResult union type, and context-aware revalidatePath for both
sprint and backlog routes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-1112): add TaskDialog component (create & edit mode)
Builds the full TaskDialog on top of the existing @base-ui/react
Dialog primitive. Covers create mode, edit mode (status field +
created_at metadata + delete), dirty-check AlertDialog, delete
confirm AlertDialog, Cmd+Enter submit, and per-field char counters.
Uses react-hook-form + zodResolver against the shared taskSchema.
Priority and status are extracted to PrioritySegmented and
StatusSelect sub-components using MD3 tokens throughout.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-1112): refactor task-list to open TaskDialog via URL params
Replaces inline create/edit forms with router.push navigation:
- Clicking a task row → ?editTask=<id>
- "+ Taak" button → ?newTask=1&storyId=<storyId>
Removes CreateTaskForm, EditSubmitButton, updateTaskAction, and
createTaskAction from the component. Status toggle and DnD remain
unchanged. Rows now have cursor-pointer and keyboard a11y.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-1112): wire TaskDialog into sprint page via searchParams
Sprint page now reads ?newTask, ?storyId, and ?editTask query params.
For edit mode: fetches the task server-side with productAccessFilter
scope (invalid/foreign IDs redirect to closePath). Renders TaskDialog
when either param is present. closePath is the sprint route without
query params.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-1112): add Suspense skeleton for edit-mode task loading
Extracts task fetch into EditTaskLoader (async server component) so
the sprint board renders immediately while the task loads.
TaskDialogSkeleton shows 3 grey bars during the fetch. Invalid or
out-of-scope task IDs redirect to closePath.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-1112): render description as markdown in task-detail-dialog
Solo task detail now renders description via react-markdown +
remark-gfm with prose styling. Sanitizes script/iframe elements.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(ST-1112): add saveTask/deleteTask server action tests
Covers all three demo-policy layers and cross-tenant scope:
demo blocked (403), unauthenticated blocked, validation 422,
edit cross-tenant forbidden, create cross-tenant forbidden,
and happy-path for both edit and create.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-1112): add updateTaskStatusWithStoryPromotion helper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-1112): wire story-promotion into saveTask and PATCH /api/tasks/:id
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs(ST-1112): add task-dialog doc and architecture note
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: extend allowed tools in settings.local.json
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-1113): add 200ms animation-delay to TaskDialogSkeleton to prevent flicker
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-1114): add DirtyCloseGuard reusable component for dirty-form close confirmation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-1114): add shared Markdown wrapper, apply to task-detail and story-dialog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: allow grep -E pattern in settings.local.json
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(tooling): extend backlog parser to support PBI-x milestone headers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(backlog): mark ST-801–806 as done
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(backlog): sorteer PBI's en stories op prio/code/datum, onthoud keuze in localStorage; vergroot sprint-afronden dialoog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-901): add user.active_product_id with FK to Product
- Nullable relation User → Product with onDelete: SetNull
- Index on active_product_id for join performance
- Migration: 20260427165329_add_user_active_product_id
- Install @tanstack/react-table (was missing from node_modules)
- Fix PRIORITY_COLORS ref removed in earlier refactor
- Note: User schema change affects vendor/scrum4me-mcp submodule — run prisma generate + tsc --noEmit there after merge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: restore priority color on PBI filter pill
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-902): add setActiveProduct + clearActiveProduct server actions
- actions/active-product.ts: setActiveProductAction validates access via
productAccessFilter, rejects archived products and demo users
- archiveProductAction: clears active_product_id for all affected users in transaction
- removeProductMemberAction: clears active_product_id for removed member
- leaveProductAction: clears active_product_id for leaving user
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-903): load active product in layout, replace cookie with DB lookup in solo
- layout.tsx: fetch active_product_id, resolve product, clear stale ref server-side
- NavBar: add activeProduct prop (rendering changes in ST-904)
- solo/page.tsx: redirect via user.active_product_id instead of lastProductId cookie
- proxy.ts: remove lastProductId cookie logic
- lib/cookies.ts: deleted (no longer used)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-904): split NavBar into 5 tabs with disabled-states and product-switcher dropdown
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ST-905): add Activeer button per product row in dashboard and product header
* feat(ST-906): redirect to dashboard with toast when active product becomes inaccessible
* feat(ST-907): tests for active-product actions and functional spec update for M9
* docs(M9): add implementation plan document and link from backlog
* feat: active PB indicator, Maak actief button and new product link in settings
* feat: apply priority-color card style to sprint story rows
* fix: move add-to-sprint click from entire card to + Toevoegen button
* feat: apply priority-color card style to sprint task rows
* fix(sprint-backlog): prevent text selection on PBI collapse button
* chore: bump version to 0.4.0 (M9 active product backlog)
* fix(landing): align logged-in nav left to match app NavBar
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- TriplePane component with two resizable dividers, localStorage persistence, mobile tabs
- SprintBoardClient replaces SprintBacklogClient + PlanningRightClient
- Left panel: Product Backlog (PBIs with stories to add to sprint)
- Middle panel: Sprint Backlog (stories in sprint, click to select, sortable)
- Right panel: TaskList for selected story
- /sprint/planning redirects to /sprint
- Remove PlanningLeft, PlanningRightClient, SprintBacklogClient
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Vervangt de '+ Sprint' knop door cross-panel drag-and-drop:
- Sleep story van rechts (PB) naar links (SB) om toe te voegen
- Sleep story van links (SB) naar rechts (PB) om te verwijderen
- Gedeelde DndContext in SprintBacklogClient voor beide panelen
- Visuele dropzone-highlight bij hoveren
- Optimistische UI-updates met rollback bij fouten
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- useSprintStore met sprintStoryOrder/taskOrder (ST-301)
- Sprint aanmaken modal met Sprint Goal validatie (ST-302)
- Sprint Backlog pagina SplitPane layout met Sprint Goal header (ST-303)
- Stories toevoegen aan Sprint via knop in rechterpaneel (ST-304)
- Sprint Backlog volgorde aanpassen via dnd-kit (ST-305)
- Story uit Sprint verwijderen met status terug naar OPEN (ST-306)
- Sprint Planning pagina SplitPane met story selectie (ST-307)
- Taken aanmaken inline in rechterpaneel (ST-308)
- Taak drag-and-drop verticaal met optimistische update (ST-309)
- Taakstatus toggle TO_DO/IN_PROGRESS/DONE met voortgangsindicator (ST-310)
- Taak inline bewerken en verwijderen (ST-311)
- Sprint afronden dialoog met per-story Done/Terug keuze (ST-312)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>