Symptoom op feat/ST-801-realtime-triggers initial implementation:
elke task-update sloot de open SSE-stream af en triggerde een
herverbinding met backoff. In de tussentijd gemiste events.
Oorzaak: Server Actions in App Router doen een impliciete
route-tree refresh die client components remount; daarmee killt
React de useEffect die de EventSource beheert.
Fix in twee delen:
1. Hef de realtime-hook op naar de (app)-layout via een nieuwe
`SoloRealtimeBridge`-component. Layouts overleven Server-
Action-refreshes beter dan pages, en de bridge leest het
product-id uit de URL via usePathname. Connection-status
(status, showConnectingIndicator) gaat naar de solo-store
zodat SoloBoard 'm uit een gedeelde plek kan lezen.
2. Vervang updateTaskStatusAction en updateTaskPlanAction in de
Solo-componenten door fetch naar de bestaande Route Handler
`PATCH /api/tasks/[id]`. Route Handlers triggeren geen
page-refresh, dus de SSE-stream blijft staan. lib/api-auth.ts
accepteert nu naast Bearer-tokens ook iron-session cookies
zodat browser-fetches zonder token werken.
Bijkomend: actions/tasks.ts laat /solo bewust niet meer
revalideren (wordt nu via realtime gedekt). Sprint/planning blijft
wel revalidaten — geen realtime daar.
Toegevoegd:
- components/solo/realtime-bridge.tsx — mount in (app) layout
- scripts/realtime-mutate.ts — handige test-helper voor externe
mutaties (alsof MCP/REST schrijft) tijdens acceptance
Debug-logs in app/api/realtime/solo/route.ts staan nog aan voor
ST-806 acceptance; worden later gestript.
Bekend issue: Chrome op localhost (HTTP/1.1) cycle't EventSource
om de paar seconden vanwege de 6-connectie-limiet en retry-
heuristiek. Safari werkt stabiel. Productie op Vercel (HTTP/2
multiplexing) zou beide browsers stabiel moeten houden — Vercel
preview test is volgende stap.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Node.js-runtime route handler die een dedicated pg-Client opent op
DIRECT_URL en LISTEN't op het scrum4me_changes-kanaal. Per inkomende
NOTIFY-payload filtert het server-side op product (uit query-param),
sprint (active sprint van het product) en persoonlijke relevantie
(assignee_id == userId, of assignee_id IS NULL voor stories voor de
claim-lijst).
Auth via iron-session cookie:
- 401 als sessie ontbreekt
- 400 als product_id query-param ontbreekt
- 403 als de user geen toegang heeft tot het product
- demo-tokens mogen lezen (geen write-tools op deze route)
Stream-bouw:
- text/event-stream met juiste headers (no-cache, no-transform,
X-Accel-Buffering: no voor proxy-vrije buffering)
- ready-event bij connect met product_id en active sprint_id
- heartbeat (SSE-comment) elke 25s
- hard-close na 240s als safety-net onder Vercel maxDuration; client
herconnect via EventSource
- cleanup op request.signal abort (tab dicht / refresh)
Cleanup-pad sluit de pg-client en de stream-controller idempotent.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(ST-511): add backlog entry for entity codes feature
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ST-511): add createWithCodeRetry helper to handle P2002 race on auto codes
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ST-511): retry on auto-code unique conflict in story and pbi create
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ST-511): surface field errors for code and title in PBI dialog
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ST-511): read create-state errors in Story dialog fieldError
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(ST-512): add backlog entry for REST API code/description/implementation_plan extensions; mark ST-511 done
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ST-512): extend REST API with code, description and implementation_plan
- GET /api/products returns code, description and definition_of_done
- GET /api/products/:id/next-story returns story.code and per-task code + implementation_plan
- GET /api/sprints/:id/tasks returns description, implementation_plan, story_code and derived per-task code
- POST /api/todos accepts and returns optional description (max 2000)
All changes are additive — existing clients ignore unknown keys.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(ST-512): mark ST-512 as done
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(ST-513): add backlog entry for API hardening for Claude Code
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ST-513): add task and story status mappers for API boundary
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ST-513): expose lowercase status on API and accept lowercase in PATCH /api/tasks
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ST-513): add metadata JSONB column to StoryLog
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ST-513): accept optional metadata in story log and switch validation errors to 422
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ST-513): add GET /api/health endpoint with optional db ping
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ST-513): add GET /api/products/:id/claude-context bundled endpoint
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(ST-513): add docs/API.md and link from CLAUDE.md specs table
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(ST-513): mark ST-513 as done
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ST-513): split 400 (malformed JSON) from 422 (validation), reject 'review'
Codex review on PR #2:
- P2.1: routes treated JSON parse failures as 422 instead of 400, breaking
the contract in docs/API.md. Replace `request.json().catch(() => null)`
with try/catch in 4 routes (tasks, reorder, todos, story-log) so a
malformed body returns 400 and only well-formed-but-invalid bodies
return 422.
- P2.2: PATCH /api/tasks/:id accepted `status: "review"`, but the sprint
task list UI does not render REVIEW (no label/color, the cycle helper
falls back to TO_DO). Reject `review` at the API until the sprint UI
is extended; document the subset in docs/API.md.
Tests in __tests__/api updated for the new contract (29 assertions:
zod-failures now expect 422, status payloads use lowercase API values,
sprint-tasks mocks include the new story relation).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vervang productAccessFilter in de WHERE clause door een expliciete
toegangscheck na het ophalen. findFirst haalt de taak op met product
en members (gefilterd op auth.userId); daarna wordt eigenaarschap of
teamlidmaatschap gecontroleerd en 403 teruggegeven bij geen toegang.
Dit herstelt het onderscheid 404 (taak bestaat niet) vs 403 (taak
bestaat maar geen toegang), zoals de beveiligingstest verwacht.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Schema: bio (160), bio_detail (2000) en avatar_data (bytea) op User
- POST /api/profile/avatar: validatie MIME-type + max 12 MB vóór verwerking,
Sharp resize naar max 700x700 (fit inside), output WebP q85, opgeslagen als bytea in Neon
- GET /api/profile/avatar: serveert avatar met Cache-Control private 1u
- updateProfileAction: slaat bio en bio_detail op via Server Action + Zod
- ProfileEditor client component: avatar preview, upload met client-side validatie,
bio-velden met tekenlimieten
- Settings page: profiel-sectie bovenaan, uitgeschakeld voor demo-gebruiker
- next.config: sharp als serverExternalPackage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add ProductMember model (many-to-many User ↔ Product)
- Add productAccessFilter helper (owner OR member OR clause)
- Replace all ownership checks across actions and API routes
- Add addProductMemberAction / removeProductMemberAction / leaveProductAction
- Add TeamManager component in product settings (owner adds/removes Developers)
- Add LeaveProductButton in user settings (member leaves a product team)
- Regenerate Prisma Client after schema migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- api-auth.ts was al aanwezig; demo-check toegevoegd per endpoint (ST-401)
- Token aanmaken (SHA-256 hash, eenmalig tonen), intrekken, max 10 (ST-402)
- GET /api/products actieve productenlijst (ST-403)
- GET /api/products/:id/next-story hoogst geprioriteerde open story (ST-404)
- GET /api/sprints/:id/tasks met limit parameter (ST-405)
- PATCH /api/stories/:id/tasks/reorder met ID-validatie (ST-406)
- POST /api/stories/:id/log met discriminatedUnion per type (ST-407)
- PATCH /api/tasks/:id status bijwerken met cross-user bescherming (ST-408)
- POST /api/todos via API aanmaken (ST-409)
- StoryLog component met kleurcodering per type in story slide-over (ST-410)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>