Helper: inventariseer veldnaam-gebruik in solo-store + backlog-store (#64)

Grep-resultaat (stores/ + lib/realtime/):
- solo-store.ts leest task_status, task_sort_order, task_title,
  story_status, story_sort_order, story_title, story_code via RealtimeEvent
- backlog-store.ts spreadt payload direct als Partial<BacklogStory/Task> →
  verwacht title/status/sort_order/pbi_id/priority/created_at (base namen)
- notifications-store.ts leest story_title/story_code uit eigen SSE-stroom
  (notifications/route.ts), niet uit pg_notify → blijft onveranderd
- debug-store.ts leest task_status, task_title (debug-only)

Beslissing: harde rename in trigger + store-update in zelfde migratie-set.
Geen dual-emit alias — zie docs/patterns/realtime-notify-payload.md.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-05-03 13:15:41 +02:00 committed by GitHub
parent 1b3f5b0bee
commit add275fa6d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -0,0 +1,149 @@
---
title: "Realtime NOTIFY payload — veldnaam-contract"
status: active
audience: [ai-agent, developer]
last_updated: 2026-05-03
---
# Realtime NOTIFY payload — veldnaam-contract
Dit document beschrijft welke veldnamen de Postgres-triggers emitteren,
welke client-bestanden die velden consumeren, en het gekozen rename-pad.
---
## Huidige trigger-payload (voor rename)
### `notify_task_change` (migratie 20260427000216)
```json
{
"op": "I|U|D",
"entity": "task",
"id": "...",
"story_id": "...",
"product_id": "...",
"sprint_id": "...",
"assignee_id": "...",
"task_status": "TO_DO",
"task_sort_order": 1,
"task_title": "...",
"changed_fields": ["status"] // alleen bij U
}
```
### `notify_story_change` (migratie 20260427000216)
```json
{
"op": "I|U|D",
"entity": "story",
"id": "...",
"product_id": "...",
"sprint_id": "...",
"assignee_id": "...",
"story_status": "OPEN",
"story_sort_order": 1,
"story_title": "...",
"story_code": "SC-1",
"changed_fields": ["status"] // alleen bij U
}
```
**Ontbrekende velden (voor INSERT-rendering in backlog-paneel):**
- story: `pbi_id`, `priority`, `created_at`, `description`
- task: `priority`, `created_at`, `description`, `story_id` ✓ (al aanwezig)
---
## Consumers per veld
| Veld (huidig) | Bestanden die dit lezen |
|---|---|
| `task_status` | `stores/solo-store.ts` (RealtimeEvent + applyChange), `app/debug-realtime/debug-store.ts` |
| `task_sort_order` | `stores/solo-store.ts` |
| `task_title` | `stores/solo-store.ts`, `app/debug-realtime/debug-store.ts` |
| `story_status` | `stores/solo-store.ts` |
| `story_sort_order` | `stores/solo-store.ts` |
| `story_title` | `stores/solo-store.ts`, `stores/notifications-store.ts`* |
| `story_code` | `stores/solo-store.ts`, `stores/notifications-store.ts`* |
\* `notifications-store.ts` leest `story_title`/`story_code` uit een **andere** SSE-stream
(`/api/realtime/notifications`), niet uit de `pg_notify`-stroom. Dat pad
blijft onveranderd — de notificatie-route bouwt zijn payload zelf op
(`app/api/realtime/notifications/route.ts:158-159`).
**`stores/backlog-store.ts`** leest de prefixed velden NIET expliciet —
het doet `{ ...p, ...(data as Partial<BacklogStory>) }`. Daarmee landt
`story_title` als los veld op het object i.p.v. `title` te vullen →
INSERT-events produceren records zonder `title`, `status`, `sort_order`,
`pbi_id`, `priority`, `created_at`.
---
## Gekozen rename-pad: harde rename + store-update in één migratie-set
**Aanpak A — harde rename** (gekozen, conform story-beslissing):
1. **Trigger-migratie**: verander `task_status → status`, `task_sort_order → sort_order`,
`task_title → title`; idem voor story (`story_status → status`, etc.).
Voeg ontbrekende velden toe: `pbi_id`, `priority`, `created_at`, `description`.
2. **`stores/solo-store.ts`**: verander `RealtimeEvent`-interface en alle
`applyChange`-lees-expressies naar de nieuwe namen
(`event.status`, `event.sort_order`, `event.title`).
Let op: `SoloTask` slaat de story-context op als `story_title`/`story_code`
→ dat zijn eigenschappen van `SoloTask`, niet van het event; die mapping
(`event.title → story_title op de task`) blijft expliciet.
3. **`app/debug-realtime/debug-store.ts`**: update `task_status → status`,
`task_title → title`.
**Niet kiezen voor aanpak B (dual-emit alias):** maakt payload groter en
het aliaspad moet later toch worden opgeruimd — twee commits voor één rename.
---
## Na de rename — verwacht payload-contract
### `notify_task_change` (na migratie)
```json
{
"op": "I|U|D",
"entity": "task",
"id": "...",
"story_id": "...",
"product_id": "...",
"sprint_id": "...",
"assignee_id": "...",
"title": "...",
"status": "TO_DO",
"sort_order": 1,
"priority": 1,
"description": null,
"created_at": "2026-05-03T00:00:00Z",
"changed_fields": ["status"]
}
```
### `notify_story_change` (na migratie)
```json
{
"op": "I|U|D",
"entity": "story",
"id": "...",
"pbi_id": "...",
"product_id": "...",
"sprint_id": "...",
"assignee_id": "...",
"title": "...",
"code": "SC-1",
"status": "OPEN",
"sort_order": 1,
"priority": 1,
"description": null,
"created_at": "2026-05-03T00:00:00Z",
"changed_fields": ["status"]
}
```
Dit contract laat `backlog-store.applyChange` de payload direct spreaden
in `BacklogStory`/`BacklogTask` zonder adapter.