--- 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) }`. 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.