---
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.
