docs(ST-1108): document M11 question-channel — API + architecture + pattern
docs/API.md — twee nieuwe secties: - 'Notifications' met /api/realtime/notifications SSE-endpoint (event-shapes, filter-rules, voorbeeld) - 'Cron — Expire questions' met /api/cron/expire-questions (Bearer-auth, schedule, response-shape, manual curl) docs/scrum4me-architecture.md — nieuw hoofdstuk 'Vraag-antwoord-kanaal Claude ↔ user' tussen QR-pairing-flow en Projectstructuur: - Mermaid sequence-diagram (Claude → DB → trigger → SSE → user → answer → trigger → Claude polls) - Threat-model-tabel (race, demo-misbruik, cross-product leak, cron-misbruik, growth, log-leakage) - Subsectie 'Waarom hergebruik scrum4me_changes-kanaal' met trade-off vs M10's eigen-kanaal-aanpak docs/patterns/claude-question-channel.md — herbruikbaar pattern 'Bidirectionele async-comms tussen MCP-agent en interactieve user' met de vier eindpunten, vier security-uitgangspunten, channel-strategie-tabel, TTL-richtlijn, en sjabloon-bestanden per laag (DB / server / client / MCP-tools). CLAUDE.md — extra rij in Implementatiepatronen-tabel die naar het nieuwe pattern-doc verwijst. Acceptatie 6 scenario's: 1. Sync happy path (MCP wait_seconds + UI submit) — handmatig getest tijdens ST-1105 acceptance-loop met de q-test injection 2. Async happy path — gedekt door get_question_answer-tool in ST-1102 + list_open_questions 3. Demo-block — actions/questions.test.ts (case 2: demo-user) + AnswerModal tooltip (visueel) 4. Access-isolation — notifications-stream.test.ts (case 'access-isolation') 5. Expiry — cron-expire-questions.test.ts (case '200 met juiste secret') 6. Race — actions/questions.test.ts (case 'al-answered' via atomic updateMany) Quality gates: lint 0 errors, tsc clean, vitest 151/151 (19 files), npm run build groen. M11 is hiermee feature-compleet. feat/M11-claude-questions heeft 12 commits lokaal, klaar voor user-acceptatie en PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
eeed5d7506
commit
e5819ee079
4 changed files with 282 additions and 0 deletions
67
docs/API.md
67
docs/API.md
|
|
@ -417,6 +417,73 @@ curl -i -X POST -b /tmp/jar -c /tmp/jar \
|
|||
|
||||
---
|
||||
|
||||
## Notifications — Vraag-antwoord-kanaal (M11)
|
||||
|
||||
Endpoints voor de Claude vraag-antwoord-flow. De **MCP-tools** in de scrum4me-mcp-repo (`ask_user_question`, `get_question_answer`, `list_open_questions`, `cancel_question`) zijn de primaire schrijf-interface; de endpoints hieronder zijn voor de browser-UI en cron.
|
||||
|
||||
### `GET /api/realtime/notifications`
|
||||
|
||||
Server-Sent Events stream voor de notifications-bell in de NavBar. **User-scoped** — geen `product_id`-param; filtert server-side op alle producten waar de gebruiker eigenaar of teamlid is.
|
||||
|
||||
**Auth:** iron-session cookie. Demo-gebruikers mogen lezen.
|
||||
**Response:** `text/event-stream`. Stream blijft open tot client sluit of server na 240s een hard-close doet (client herconnect).
|
||||
|
||||
**Events:**
|
||||
- `event: state` — eenmalig direct na connect, met `{ questions: [...] }` als payload (zelfde shape als de live updates).
|
||||
- `data: {...}` — bij elke status-overgang in `claude_questions`. Payload-shape:
|
||||
```json
|
||||
{
|
||||
"op": "I" | "U",
|
||||
"entity": "question",
|
||||
"id": "cmoh...",
|
||||
"product_id": "cmoh...",
|
||||
"story_id": "cmoh...",
|
||||
"task_id": "cmoh..." | null,
|
||||
"assignee_id": "cmoh..." | null,
|
||||
"status": "open" | "answered" | "cancelled" | "expired"
|
||||
}
|
||||
```
|
||||
Het is een delta — voor de volledige vraag-tekst en options reconnect de client (initial-state-event levert ze opnieuw).
|
||||
- `: heartbeat` — SSE-comment elke 25s.
|
||||
|
||||
**Server-side filter:**
|
||||
- `payload.entity === 'question'` (`task` en `story` events horen op `/api/realtime/solo`)
|
||||
- `payload.product_id` zit in de set producten met user-access (productAccessFilter)
|
||||
|
||||
**Voorbeeld:**
|
||||
```js
|
||||
const source = new EventSource('/api/realtime/notifications', { withCredentials: true })
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cron — Expire questions
|
||||
|
||||
### `POST /api/cron/expire-questions`
|
||||
|
||||
Vercel cron handler die elke 6 uur draait. Markeert verlopen open vragen als `expired` en verlopen pending login_pairings als `cancelled`.
|
||||
|
||||
**Auth:** `Authorization: Bearer ${CRON_SECRET}` — header die Vercel automatisch injecteert wanneer de env-var op de project-omgeving staat. Zonder secret of bij mismatch: 401.
|
||||
|
||||
**Schedule:** `0 */6 * * *` (4× per dag).
|
||||
|
||||
**Response 200:**
|
||||
```json
|
||||
{
|
||||
"expired_questions": 0,
|
||||
"expired_pairings": 0,
|
||||
"ran_at": "2026-04-28T00:00:00.000Z"
|
||||
}
|
||||
```
|
||||
|
||||
**Voorbeeld (handmatige trigger):**
|
||||
```bash
|
||||
curl -X POST -H "Authorization: Bearer $CRON_SECRET" \
|
||||
https://your-app.vercel.app/api/cron/expire-questions
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Voorbeeldworkflow voor Claude Code
|
||||
|
||||
1. **Probe:** `GET /api/health?db=1` — bevestig dat de service en DB bereikbaar zijn.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue