Implements AI-powered multi-intent classification for Cortex V2. When Reflex Arc (Layer 1) detects complexity, it escalates to the Orchestrator for intelligent classification using Claude 3.5 Haiku. E2.S1 - System prompt for Orchestrator - ORCHESTRATOR_SYSTEM_PROMPT constant with Dutch instructions - Multi-intent detection (en, daarna, ook, eerst, vervolgens) - Pronoun resolution rules (hij/zij → active patient) - JSON-only output format specification E2.S2 - Context formatting for AI - formatContextForPrompt() with emoji structure - Dutch labels for shifts and views - Graceful null handling, token limiting (max 5 agenda, 3 recent) E2.S3 - AI classification endpoint - classifyWithOrchestrator() calling Claude 3.5 Haiku - Builds IntentChain from AI response - Tracks tokens used and processing time E2.S4 - IntentChain parsing with Zod - parseAIResponse() with Zod schema validation - Strips markdown code blocks - Partial extraction for malformed responses - Graceful fallback to intent: 'unknown' E2.S5 - POST /api/cortex/classify hybrid endpoint - Reflex first, escalate to Orchestrator if needed - Response includes handledBy: 'reflex' | 'orchestrator' - Auth check, feature flag check, request validation E2.S6 - Graceful fallback - buildFallbackChain() with confidence cap (0.6) - buildChainFromReflex() for simple chains - classifyWithTimeout() wrapper (5s default) New files: - lib/cortex/orchestrator.ts (~780 lines) - app/api/cortex/classify/route.ts (~180 lines) Key deliverable: "Zeg Jan af en maak notitie" correctly parsed into 2 actions in an IntentChain. Progress: 27 SP / 48 SP (56%) - E0, E1, E2 complete 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
42 KiB
Bouwplan — Cortex Intent System V2
Projectnaam: Cortex V2 - Agentic Intent Architecture Versie: v1.2 Datum: 01-01-2026 Auteur: Colin Lit
1. Doel en context
Doel: Bouwen van een werkend MVP/prototype van het Cortex V2 Intent System dat de transformatie demonstreert van een reactief "spraakgestuurd toetsenbord" naar een AI Collega die context begrijpt, meerdere intenties verwerkt en proactief meedenkt.
Context: Het huidige Cortex systeem (V1) is reactief: gebruiker geeft commando → systeem voert uit. V2 introduceert een three-layer architectuur die intelligentie boven snelheid prioriteert.
Kernprincipe:
"We stoppen met optimaliseren voor milliseconden en starten met optimaliseren voor intelligentie."
Beoogd resultaat: Een demonstreerbaar prototype dat:
- Multi-intents begrijpt ("Zeg Jan af en maak notitie")
- Context-aware is (snapt wie "hij" is, wat "morgen" betekent)
- Proactief suggesties geeft (na wondzorg → "Controle inplannen?")
- Nooit "Ik snap het niet" zegt (altijd een poging tot begrip)
Referenties:
- PRD:
docs/intent/prd-cortex-v2.md - FO:
docs/intent/fo-cortex-intent-system-v2.md - TO:
docs/intent/to-cortex-v2.md - Architectuur:
docs/intent/architecture-cortex-v2.md - MVP User Stories:
docs/intent/mvp-userstories-intent-system.md
2. Dev Quick Start
Voor je begint
Lees eerst (5 min):
- Dit bouwplan (je bent hier)
- TO sectie 4.1 voor volledige type definities:
docs/intent/to-cortex-v2.md
Codebase oriëntatie:
lib/cortex/ # Cortex logic (V1 + V2)
├── types.ts # ✅ Bestaand - UITBREIDEN
├── intent-classifier.ts # ✅ Bestaand V1 - NIET AANPASSEN
├── intent-classifier-ai.ts # ✅ Bestaand V1 AI - referentie
├── entity-extractor.ts # ✅ Bestaand - hergebruiken
├── reflex-classifier.ts # 🆕 NIEUW in E1
├── orchestrator.ts # 🆕 NIEUW in E2
├── nudge.ts # 🆕 NIEUW in E4
└── logger.ts # 🆕 NIEUW in E0
stores/
└── cortex-store.ts # ✅ Bestaand - UITBREIDEN in E0.S4
components/cortex/
├── chat/
│ ├── action-chain-card.tsx # 🆕 NIEUW in E3
│ └── clarification-card.tsx # 🆕 NIEUW in E3
└── command-center/
└── nudge-toast.tsx # 🆕 NIEUW in E4
app/api/cortex/
├── context/route.ts # 🆕 NIEUW in E0.S2
└── classify/route.ts # 🆕 NIEUW in E2.S5
lib/config/
└── feature-flags.ts # 🆕 NIEUW in E0.S3
Werkwijze per story:
- Lees story + done criteria
- Check bestaande code (zie "Bestaande code" sectie per epic)
- Implementeer
- Run
pnpm lintenpnpm build - Test handmatig of met test command
- Commit met story ID:
feat(cortex): E0.S1 - CortexContext types
Belangrijke conventies:
- TypeScript strict mode
- Nederlandse gebruikersteksten, Engelse code/comments
- Zod voor runtime validatie waar nodig
- Graceful degradation bij AI failures
3. Uitgangspunten
3.1 Technische Stack
| Component | Technologie | Argumentatie |
|---|---|---|
| Frontend | Next.js 15, React, TailwindCSS | Bestaande stack, App Router |
| Backend | Next.js API Routes | Co-located met frontend |
| Database | Supabase (PostgreSQL) | Realtime, RLS, auth included |
| AI Model | Claude 3.5 Haiku | Snel (~400ms), goedkoop, excellent Nederlands |
| State | Zustand | Lightweight, devtools, persist |
| UI | shadcn/ui | Bestaande component library |
3.2 Projectkaders
| Kader | Waarde |
|---|---|
| Type release | MVP / Public Prototype ("Build in Public") |
| Bouwtijd | 11-15 werkdagen |
| Budget | N.v.t. (prototype) |
| Team | 1 developer + AI-assistentie |
| Data | Mock-data, geen productie EPD-koppeling |
| Doel | Demonstratie van "Agency" concept |
3.3 Programmeer Uitgangspunten
Code Quality Principles:
- DRY - Herbruikbare components en utility functions
- KISS - Eenvoudige oplossingen, geen premature optimization
- SOC - UI gescheiden van business logic, API calls in service layers
- YAGNI - Alleen bouwen wat nu nodig is voor MVP
Development Practices:
- TypeScript strict mode
- Zod schemas voor runtime validatie
- Error handling met user-friendly Nederlandse meldingen
- Graceful degradation bij AI failures
4. Epics & Stories Overzicht
MVP Scope (✅ In Scope)
| Epic ID | Titel | Doel | Status | Stories | Story Points |
|---|---|---|---|---|---|
| E0 | Foundation & Context | Types, API, feature flags | ✅ Done | 5 | 8 SP |
| E1 | Reflex Arc (Layer 1) | Snelle lokale classificatie | ✅ Done | 4 | 6 SP |
| E2 | Intent Orchestrator (Layer 2) | AI-gedreven multi-intent | ✅ Done | 6 | 13 SP |
| E3 | UI Components | ActionChainCard, ClarificationCard | ⏳ To Do | 4 | 8 SP |
| E4 | Nudge MVP (Layer 3) | Proactieve suggesties | ⏳ To Do | 3 | 5 SP |
| E5 | Integration & Polish | End-to-end flow, testing | ⏳ To Do | 4 | 8 SP |
Totaal MVP: 26 stories, 48 Story Points
Post-MVP Scope (❌ Niet in Scope)
| Feature | Reden | Prioriteit |
|---|---|---|
| Complete medische protocollen | Te complex voor prototype | Post-MVP |
| Rollback/Undo | Vereist transactie-systeem | Post-MVP |
| Offline mode | Prototype veronderstelt internet | Low |
| Advanced error handling | Retry-mechanismes, circuit breakers | Post-MVP |
| Analytics & learning | Telemetry opslag, model training | Post-MVP |
| NEN7510 compliance | Productie-beveiliging | Post-MVP (kritiek) |
| Externe integraties | Teams, ECD-koppelingen | Post-MVP |
Belangrijk: Voer niet in 1x het volledige plan uit. Bouw per epic en per story.
5. Epics & Stories (Uitwerking)
Epic 0 — Foundation & Context
Epic Doel: Werkende basis met types, context API en feature flags voor gecontroleerde rollout.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP |
|---|---|---|---|---|---|
| E0.S1 | CortexContext types definiëren | Types in lib/cortex/types.ts voor context, intents, chains |
✅ | — | 2 |
| E0.S2 | GET /api/cortex/context endpoint | Retourneert actieve patiënt, agenda, recente acties | ✅ | E0.S1 | 2 |
| E0.S3 | Feature flags setup | CORTEX_V2_ENABLED, CORTEX_MULTI_INTENT, CORTEX_NUDGE, CORTEX_LOGGING |
✅ | — | 1 |
| E0.S4 | CortexStore V2 extensions | Zustand store met context, chains, suggestions state | ✅ | E0.S1 | 2 |
| E0.S5 | Classification logging utility | Dev logging + production sanitization | ✅ | E0.S1 | 1 |
Deliverable: Context beschikbaar, types gedefinieerd, backward compatible
🔧 Dev Context & Instructies — Epic 0
Bestaande code (NIET VERVANGEN, wel uitbreiden):
lib/cortex/
├── types.ts # Bestaande types: CortexIntent, ExtractedEntities, BlockType
├── intent-classifier.ts # V1 classifier (niet aanpassen in E0)
├── entity-extractor.ts # Entity extraction (hergebruiken)
└── index.ts # Exports (uitbreiden)
stores/
└── cortex-store.ts # Bestaande store met ChatMessage, Artifact, RecentAction
E0.S1 — CortexContext types
Bestand: lib/cortex/types.ts
Actie: Voeg NIEUWE types toe NA bestaande types. Behoud alle bestaande exports.
Toe te voegen types:
CortexContext- Context voor AI classificatieIntentChain- Multi-intent containerIntentAction- Enkele actie in een chainNudgeSuggestion- Proactieve suggestieLocalClassificationResult- Reflex output (uitbreiding van bestaandeClassificationResult)EscalationReason- Waarom Reflex escaleert
Voorbeeld signature:
export interface CortexContext {
activePatient: { id: string; name: string; } | null;
currentView: 'dashboard' | 'patient-detail' | 'agenda' | 'reports' | 'chat';
shift: ShiftType; // Hergebruik bestaande type
// ... zie TO sectie 4.1 voor volledige definitie
}
Done criteria:
pnpm lintslaagt- Bestaande imports (
CortexIntent,ExtractedEntities) werken nog - Nieuwe types geëxporteerd via
lib/cortex/index.ts
E0.S2 — Context API endpoint
Bestand: app/api/cortex/context/route.ts (NIEUW)
Actie: Maak GET endpoint die context verzamelt uit store/database.
Endpoint gedrag:
- Haal actieve patiënt uit request context of store
- Haal agenda vandaag uit Supabase (mock data voor MVP)
- Retourneer
CortexContextobject
Voorbeeld response:
{
"context": {
"activePatient": { "id": "123", "name": "Jan de Vries" },
"currentView": "patient-detail",
"shift": "ochtend",
"currentTime": "2025-12-31T10:00:00Z",
"agendaToday": [
{ "time": "14:00", "patientName": "Marie", "type": "intake" }
],
"recentIntents": []
}
}
Done criteria:
GET /api/cortex/contextretourneert 200 met valid JSON- Response matcht
CortexContexttype
E0.S3 — Feature flags
Bestand: lib/config/feature-flags.ts (NIEUW)
Actie: Maak feature flag utility met env var support.
Flags te implementeren:
export const FEATURE_FLAGS = {
CORTEX_V2_ENABLED: process.env.NEXT_PUBLIC_CORTEX_V2 === 'true',
CORTEX_MULTI_INTENT: process.env.NEXT_PUBLIC_CORTEX_MULTI_INTENT === 'true',
CORTEX_NUDGE: process.env.NEXT_PUBLIC_CORTEX_NUDGE === 'true',
CORTEX_LOGGING: process.env.NEXT_PUBLIC_CORTEX_LOGGING === 'true',
};
Done criteria:
- Flags werken in dev (hardcoded
true) - Flags leesbaar vanuit components
E0.S4 — CortexStore V2 extensions
Bestand: stores/cortex-store.ts
Actie: UITBREIDEN met nieuwe state en actions. Behoud bestaande useCortexStore.
Toe te voegen state:
context: CortexContext- Huidige contextactiveChain: IntentChain | null- Actieve multi-intent chainchainHistory: IntentChain[]- Geschiedenissuggestions: NudgeSuggestion[]- Pending nudgespendingClarification- Clarification state
Toe te voegen actions:
setContext(),startChain(),updateActionStatus(),completeChain()addSuggestion(),acceptSuggestion(),dismissSuggestion()
Done criteria:
- Bestaande store werkt nog (backward compatible)
- Nieuwe state observable in React DevTools
pnpm lintslaagt
E0.S5 — Classification logging
Bestand: lib/cortex/logger.ts (NIEUW)
Actie: Logging utility met PII sanitization.
Functies:
logClassification(result)- Log naar console (dev) of API (prod)sanitizeForLogging(input)- Verwijder namen, BSN, telefoonnummers
Voorbeeld:
// Input: "notitie Jan Jansen medicatie"
// Output: "notitie [NAAM] medicatie"
Done criteria:
- Dev logs tonen classificatie resultaten
- Namen worden gesanitized in productie mode
Epic 1 — Reflex Arc (Layer 1)
Epic Doel: Razendsnelle (<20ms) afhandeling van simpele, eenduidige commando's.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP |
|---|---|---|---|---|---|
| E1.S1 | Pattern matching implementeren | Regex patterns voor alle intent types met weights | ✅ | E0.S1 | 2 |
| E1.S2 | Escalatie triggers detectie | Multi-intent signals, context signals, relative time | ✅ | E1.S1 | 2 |
| E1.S3 | Ambiguity detection | Top-2 score delta < 0.1 → escaleer | ✅ | E1.S1 | 1 |
| E1.S4 | Unit tests Reflex classifier | Test suite voor simpele en complexe inputs | ✅ | E1.S1-S3 | 1 |
Deliverable: Simpele commando's werken direct (<20ms)
🔧 Dev Context & Instructies — Epic 1
Bestaande code (ter referentie):
lib/cortex/
├── intent-classifier.ts # V1 classifier - REFERENTIE, niet aanpassen
│ # Bevat INTENT_PATTERNS die je kunt hergebruiken
└── types.ts # ClassificationResult type bestaat al
Relatie V1 → V2:
De bestaande intent-classifier.ts heeft al regex patterns en weights. De nieuwe reflex-classifier.ts bouwt hierop voort maar voegt toe:
- Escalatie logica (wanneer naar AI sturen)
- Ambiguity detection (top-2 vergelijking)
- Multi-intent signal detectie
E1.S1 — Pattern matching
Bestand: lib/cortex/reflex-classifier.ts (NIEUW)
Actie: Maak nieuwe classifier gebaseerd op V1 patterns, maar met escalatie-aware logic.
Kernfunctie:
export function classifyWithReflex(input: string): LocalClassificationResult {
// 1. Check escalatie triggers EERST
// 2. Pattern matching met weights
// 3. Return result met shouldEscalateToAI flag
}
Hergebruik van V1:
- Kopieer
INTENT_PATTERNSuitintent-classifier.ts - Pas weights aan: alleen >= 0.7 is "high confidence"
- Voeg
secondBestIntenttracking toe
Constants:
export const CONFIDENCE_THRESHOLD = 0.7;
export const AMBIGUITY_THRESHOLD = 0.1;
Done criteria:
- "agenda vandaag" →
{ intent: 'agenda_query', shouldEscalateToAI: false } - Processing time < 20ms
- Bestaande V1 classifier blijft werken (backward compatible)
E1.S2 — Escalatie triggers
Bestand: lib/cortex/reflex-classifier.ts
Actie: Voeg detectEscalationTriggers() functie toe.
Trigger patterns:
const MULTI_INTENT_SIGNALS = /\b(en|daarna|ook|eerst|dan|vervolgens)\b/i;
const CONTEXT_SIGNALS = /\b(hij|zij|hem|haar|zijn|die|deze|dat|dezelfde)\b/i;
const RELATIVE_TIME_SIGNALS = /\b(morgen|overmorgen|volgende week|over \d+ dagen?)\b/i;
Logic:
- Als EEN trigger matcht →
shouldEscalateToAI: true - Return
escalationReasonvoor logging
Done criteria:
- "Zeg Jan af en maak notitie" → escalates (
multi_intent_detected) - "Maak notitie voor hem" → escalates (
needs_context) - "Plan afspraak morgen" → escalates (
relative_time)
E1.S3 — Ambiguity detection
Bestand: lib/cortex/reflex-classifier.ts
Actie: Track top-2 matches en vergelijk scores.
Logic:
// Na pattern matching
const delta = bestMatch.confidence - secondBestMatch.confidence;
if (delta < AMBIGUITY_THRESHOLD) {
return { ...result, shouldEscalateToAI: true, escalationReason: 'ambiguous' };
}
Voorbeeld:
- "plan wondzorg" matcht zowel
create_appointment(0.72) alsdagnotitie(0.68) - Delta = 0.04 < 0.1 → escaleer naar AI
Done criteria:
- Ambigue input triggert escalatie
secondBestIntentensecondBestConfidencein result
E1.S4 — Unit tests
Bestand: lib/cortex/__tests__/reflex-classifier.test.ts (NIEUW)
Actie: Test suite met Vitest.
Test cases:
describe('Reflex Classifier', () => {
describe('Simple intents - should NOT escalate', () => {
test('"agenda vandaag"', ...);
test('"zoek marie"', ...);
test('"notitie jan medicatie"', ...);
});
describe('Complex intents - SHOULD escalate', () => {
test('"zeg jan af en maak notitie"', ...); // multi_intent
test('"maak notitie voor hem"', ...); // needs_context
test('"plan afspraak morgen 14:00"', ...); // relative_time
});
describe('Ambiguous intents - SHOULD escalate', () => {
test('"plan wondzorg"', ...); // ambiguous
});
});
Done criteria:
pnpm tsx lib/cortex/__tests__/reflex-classifier.test.tsslaagt (25 tests)- Coverage voor alle escalatie scenarios
Epic 2 — Intent Orchestrator (Layer 2)
Epic Doel: AI-gedreven analyse voor complexe zinnen, multi-intents en context resolution.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP |
|---|---|---|---|---|---|
| E2.S1 | System prompt voor Orchestrator | Prompt met context, intent types, output format | ✅ | E0.S1 | 2 |
| E2.S2 | Context formatting voor AI | formatContextForPrompt() functie |
✅ | E0.S2 | 1 |
| E2.S3 | AI classification endpoint | classifyWithOrchestrator() met Claude 3.5 Haiku |
✅ | E2.S1, E2.S2 | 3 |
| E2.S4 | IntentChain parsing | JSON response naar IntentChain met actions | ✅ | E2.S3 | 2 |
| E2.S5 | POST /api/cortex/classify | Hybrid endpoint: Reflex → Orchestrator fallback | ✅ | E1.S1-S3, E2.S3-S4 | 3 |
| E2.S6 | Graceful fallback | Bij AI failure → fallback naar Reflex-only | ✅ | E2.S5 | 2 |
Deliverable: "Zeg Jan af en maak notitie" wordt correct geparsed naar 2 acties
🔧 Dev Context & Instructies — Epic 2
Bestaande code (ter referentie):
lib/cortex/
├── intent-classifier-ai.ts # V1 AI classifier - bevat Anthropic setup
├── chat-api.ts # Bestaande chat API calls
└── entity-extractor.ts # Entity extraction (hergebruiken)
app/api/cortex/ # Bestaande API routes (indien aanwezig)
Dependencies:
@anthropic-ai/sdkis al geïnstalleerdANTHROPIC_API_KEYin.env.local
E2.S1 — System prompt
Bestand: lib/cortex/orchestrator.ts (NIEUW)
Actie: Definieer ORCHESTRATOR_SYSTEM_PROMPT constant.
Prompt structuur:
const ORCHESTRATOR_SYSTEM_PROMPT = `Je bent de Intent Orchestrator voor Cortex...
## Je Taak
Analyseer de gebruikersinput en extraheer ALLE intenties.
## Context die je krijgt
- Actieve patiënt, Agenda vandaag, Recente acties, Huidige weergave
## Intent Types
1. dagnotitie, 2. zoeken, 3. overdracht, 4. agenda_query,
5. create_appointment, 6. cancel_appointment, 7. reschedule_appointment
## Multi-Intent Detectie
Let op: "en", "daarna", "ook", "eerst", "dan", "vervolgens"
## Pronoun Resolution
"hij/zij" → actieve patiënt
## Output Format
ALLEEN valid JSON (geen markdown):
{ "actions": [...], "reasoning": "...", "needsClarification": false }
`;
Zie TO sectie 8.3 voor volledige prompt.
Done criteria:
- Prompt is duidelijk en in het Nederlands
- JSON output format gedocumenteerd
E2.S2 — Context formatting
Bestand: lib/cortex/orchestrator.ts
Actie: formatContextForPrompt(context: CortexContext): string
Output format:
🧑 Actieve patiënt: Jan de Vries (ID: 123)
📍 Huidige weergave: patient-detail
⏰ Tijd: 10:30 (ochtenddienst)
📅 Agenda vandaag:
- 14:00: Marie (intake)
- 15:30: Piet (follow-up)
Done criteria:
- Context leesbaar voor AI
- Graceful handling van null values
E2.S3 — AI classification
Bestand: lib/cortex/orchestrator.ts
Actie: classifyWithOrchestrator() async functie.
Implementatie outline:
export async function classifyWithOrchestrator(
input: string,
context: CortexContext
): Promise<AIClassificationResult> {
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const response = await anthropic.messages.create({
model: 'claude-3-5-haiku-20241022',
max_tokens: 512,
temperature: 0, // Consistente output
system: ORCHESTRATOR_SYSTEM_PROMPT,
messages: [{ role: 'user', content: `## Context\n${formatContextForPrompt(context)}\n\n## Input\n"${input}"` }],
});
// Parse JSON response
// Build IntentChain
// Return result
}
Done criteria:
- Multi-intent input → meerdere actions in response
- Processing time ~400ms (acceptabel)
- Tokens usage gelogd
E2.S4 — IntentChain parsing
Bestand: lib/cortex/orchestrator.ts
Actie: parseAIResponse() functie met error handling.
Parsing logic:
function parseAIResponse(rawText: string): ParsedResponse {
// 1. Strip markdown code blocks (```json ... ```)
// 2. JSON.parse met try/catch
// 3. Validate tegen schema
// 4. Fallback naar { actions: [{ intent: 'unknown' }] } bij parse error
}
Edge cases:
- AI retourneert markdown code fence → strip
- Invalid JSON → fallback to unknown
- Missing fields → defaults
Done criteria:
- Valid JSON correct geparsed
- Invalid JSON → graceful fallback
- IntentChain correct opgebouwd
E2.S5 — Hybrid classify endpoint
Bestand: app/api/cortex/classify/route.ts (NIEUW)
Actie: POST endpoint die Reflex → Orchestrator flow implementeert.
Flow:
export async function POST(request: NextRequest) {
const { input, context } = await request.json();
// Step 1: Try Reflex
const reflexResult = classifyWithReflex(input);
// Step 2: Escalate if needed
if (reflexResult.shouldEscalateToAI) {
const aiResult = await classifyWithOrchestrator(input, context);
return NextResponse.json({ ...aiResult, handledBy: 'orchestrator' });
}
// Step 3: Return Reflex result
return NextResponse.json({ chain: buildChainFromReflex(reflexResult), handledBy: 'reflex' });
}
Done criteria:
- Simpele input → Reflex (geen AI call)
- Complexe input → Orchestrator
- Response bevat
handledByfield
E2.S6 — Graceful fallback
Bestand: lib/cortex/orchestrator.ts
Actie: try/catch rond AI call met fallback naar Reflex.
Fallback scenarios:
- Anthropic API 503 → use Reflex result
- Timeout (>5s) → use Reflex result
- Parse error → use Reflex result met warning
Voorbeeld:
try {
return await classifyWithOrchestrator(input, context);
} catch (error) {
console.error('[Cortex] AI failed, fallback to Reflex:', error);
return buildFallbackResult(classifyWithReflex(input));
}
Done criteria:
- AI failure → geen crash
- User ziet resultaat (mogelijk minder intelligent)
- Error gelogd voor monitoring
Epic 3 — UI Components
Epic Doel: Visuele feedback voor multi-intent flows en clarification.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP |
|---|---|---|---|---|---|
| E3.S1 | ActionChainCard component | Stacked cards met sequence, status, confidence | ⏳ | E0.S1 | 3 |
| E3.S2 | ActionItem sub-component | Status icons, confirmation buttons, error states | ⏳ | E3.S1 | 2 |
| E3.S3 | ClarificationCard component | Vraag + keuze-knoppen bij ambigue input | ⏳ | — | 2 |
| E3.S4 | Processing indicator | Spinner/skeleton bij AI-acties | ⏳ | — | 1 |
Deliverable: Multi-intent flows visueel weergegeven
🔧 Dev Context & Instructies — Epic 3
Bestaande code (ter referentie):
components/
├── cortex/
│ ├── chat/ # Bestaande chat components
│ ├── blocks/ # Artifact blocks (dagnotitie, agenda, etc.)
│ ├── command-center/ # Command center layout
│ └── artifacts/ # Artifact containers
└── ui/
├── button.tsx # shadcn/ui button
├── card.tsx # shadcn/ui card
└── badge.tsx # shadcn/ui badge
Styling conventies:
- TailwindCSS utility classes
- shadcn/ui component patterns
cn()utility voor conditional classes
E3.S1 — ActionChainCard component
Bestand: components/cortex/chat/action-chain-card.tsx (NIEUW)
Actie: Container voor multi-intent flow weergave.
Props interface:
interface ActionChainCardProps {
chain: IntentChain;
onConfirm: (actionId: string) => void;
onSkip: (actionId: string) => void;
onRetry: (actionId: string) => void;
}
Structuur:
<div className="bg-white rounded-lg border">
{/* Header: "2 acties gedetecteerd" */}
{/* ActionItem per actie */}
{/* Collapsible AI reasoning */}
</div>
Done criteria:
- Toont alle acties in chain
- Header toont aantal acties
- AI reasoning collapsible (details/summary)
E3.S2 — ActionItem sub-component
Bestand: components/cortex/chat/action-chain-card.tsx (in zelfde file of apart)
Actie: Enkele actie row met status en controls.
Status icons mapping:
const STATUS_ICONS = {
pending: <div className="w-2 h-2 rounded-full bg-slate-300" />,
confirming: <AlertCircle className="w-4 h-4 text-amber-500" />,
executing: <Loader2 className="w-4 h-4 text-blue-500 animate-spin" />,
success: <Check className="w-4 h-4 text-green-500" />,
failed: <X className="w-4 h-4 text-red-500" />,
skipped: <X className="w-4 h-4 text-slate-400" />,
};
Confidence badge kleuren:
confidence >= 0.9 → "bg-green-100 text-green-700"
confidence >= 0.7 → "bg-amber-100 text-amber-700"
confidence < 0.7 → "bg-red-100 text-red-700"
Intent labels (Nederlands):
const INTENT_LABELS = {
dagnotitie: 'Notitie',
zoeken: 'Zoeken',
cancel_appointment: 'Afspraak annuleren',
// ...
};
Done criteria:
- Status icon correct per status
- Confidence badge met juiste kleur
- Confirmation buttons bij
confirmingstatus - Retry button bij
failedstatus
E3.S3 — ClarificationCard component
Bestand: components/cortex/chat/clarification-card.tsx (NIEUW)
Actie: UI voor verduidelijkingsvragen.
Props interface:
interface ClarificationCardProps {
question: string; // "Bedoel je een notitie of afspraak?"
options: string[]; // ["Notitie maken", "Afspraak inplannen"]
onSelect: (option: string) => void;
}
Voorbeeld weergave:
┌─────────────────────────────────────────┐
│ 💡 Bedoel je een notitie of afspraak? │
│ │
│ [Notitie maken] [Afspraak inplannen] │
└─────────────────────────────────────────┘
Done criteria:
- Vraag duidelijk zichtbaar
- Knoppen voor elke optie
- Click triggert
onSelectcallback
E3.S4 — Processing indicator
Bestand: Integreer in components/cortex/chat/ of bestaande chat components
Actie: Toon "denk" indicator wanneer AI werkt.
Wanneer tonen:
- Na user input, tijdens classificatie
- Alleen bij AI calls (niet bij Reflex)
Varianten:
// Optie 1: Simpele spinner met tekst
<div className="flex items-center gap-2 text-slate-500">
<Loader2 className="w-4 h-4 animate-spin" />
<span>Even nadenken...</span>
</div>
// Optie 2: Skeleton loader (chat message style)
<div className="animate-pulse bg-slate-100 rounded-lg h-20" />
Done criteria:
- Indicator zichtbaar tijdens AI call
- Verdwijnt zodra response binnen is
- Geen "frozen" UI gevoel
Epic 4 — Nudge MVP (Layer 3)
Epic Doel: Proactieve suggesties na succesvolle acties (proof of concept).
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP |
|---|---|---|---|---|---|
| E4.S1 | Protocol rules definiëren | Wondzorg-controle regel (hardcoded) | ⏳ | E0.S1 | 1 |
| E4.S2 | evaluateNudge functie | Check protocol rules na actie completion | ⏳ | E4.S1 | 2 |
| E4.S3 | NudgeToast component | Toast met countdown timer, accept/dismiss | ⏳ | E0.S4 | 2 |
Demo Case:
Actie: Notitie "wond verzorgd" → Suggestie: "Wondcontrole inplannen over 3 dagen?"
Deliverable: Proactieve suggestie getoond na wondzorg notitie
🔧 Dev Context & Instructies — Epic 4
Concept uitleg: Nudge is een "post-action" systeem. Nadat een actie succesvol is uitgevoerd, checkt het systeem of er een protocol-regel matcht. Zo ja → toon suggestie.
Bestaande code:
- Geen bestaande nudge code
stores/cortex-store.tsmoet uitgebreid worden met suggestions state (E0.S4)
E4.S1 — Protocol rules
Bestand: lib/cortex/nudge.ts (NIEUW)
Actie: Definieer ProtocolRule interface en hardcoded regels.
MVP regels (1-2 voor demo):
export const PROTOCOL_RULES: ProtocolRule[] = [
{
id: 'wondzorg-controle',
name: 'Wondcontrole na verzorging',
trigger: {
intent: 'dagnotitie',
conditions: [
{ field: 'content', operator: 'contains', value: 'wond' },
],
},
suggestion: {
intent: 'create_appointment',
message: 'Wondcontrole inplannen over 3 dagen?',
prefillFrom: (source) => ({
patientName: source.patientName,
appointmentType: 'follow-up',
}),
},
priority: 'medium',
enabled: true,
},
];
Condition operators:
equals- exact matchcontains- substring (case insensitive)matches- regex matchexists- field is not null/empty
Done criteria:
- ProtocolRule type gedefinieerd
- Minimaal 1 werkende regel (wondzorg)
E4.S2 — evaluateNudge functie
Bestand: lib/cortex/nudge.ts
Actie: Check alle regels tegen een voltooide actie.
Functie signature:
export function evaluateNudge(completedAction: IntentAction): NudgeSuggestion[] {
// 1. Filter enabled rules
// 2. Check trigger.intent match
// 3. Check all conditions
// 4. Build suggestion if match
// 5. Sort by priority (high first)
}
Condition checker:
function checkCondition(condition: ProtocolCondition, entities: ExtractedEntities): boolean {
const value = entities[condition.field];
switch (condition.operator) {
case 'contains':
return typeof value === 'string' && value.toLowerCase().includes(condition.value.toLowerCase());
// ...
}
}
Done criteria:
- Notitie met "wond" → NudgeSuggestion returned
- Notitie zonder "wond" → empty array
- Suggesties gesorteerd op priority
E4.S3 — NudgeToast component
Bestand: components/cortex/command-center/nudge-toast.tsx (NIEUW)
Actie: Toast UI met countdown en actions.
Props interface:
interface NudgeToastProps {
suggestion: NudgeSuggestion;
onAccept: (suggestionId: string) => void;
onDismiss: (suggestionId: string) => void;
}
Features:
- Progress bar countdown (5 minuten default)
- Priority-based styling:
high→border-red-200 bg-red-50medium→border-amber-200 bg-amber-50low→border-blue-200 bg-blue-50
- Accept/Dismiss buttons
- Auto-dismiss na expiry
Countdown implementatie:
useEffect(() => {
const interval = setInterval(() => {
const remaining = expiresAt - Date.now();
if (remaining <= 0) onDismiss(suggestion.id);
setTimeLeft(remaining / total * 100);
}, 1000);
return () => clearInterval(interval);
}, []);
Done criteria:
- Toast verschijnt na matching actie
- Progress bar animeert
- Accept triggert nieuwe actie
- Dismiss verwijdert toast
Epic 5 — Integration & Polish
Epic Doel: End-to-end flow werkend, getest en demo-ready.
| Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | SP |
|---|---|---|---|---|---|
| E5.S1 | CommandCenter V3 integratie | ActionChainCard, NudgeToast in chat panel | ⏳ | E3, E4 | 3 |
| E5.S2 | Chain execution flow | Sequential action execution met confirmations | ⏳ | E5.S1 | 2 |
| E5.S3 | Integration tests | E2E tests voor hele flow | ⏳ | E5.S1-S2 | 2 |
| E5.S4 | Demo scenario voorbereiden | Happy path + edge cases gedocumenteerd | ⏳ | E5.S3 | 1 |
Demo Flow (5 minuten):
- Simpel commando → Reflex (direct)
- Multi-intent commando → Orchestrator (ActionChainCard)
- Context-dependent → Pronoun resolution
- Wondzorg notitie → Nudge suggestie
Deliverable: Demo-ready prototype
🔧 Dev Context & Instructies — Epic 5
Bestaande code (aan te passen):
components/cortex/
├── command-center/
│ ├── command-center.tsx # Hoofd container
│ ├── chat-panel.tsx # Chat interface (indien aanwezig)
│ └── index.ts # Exports
└── chat/
└── chat-messages.tsx # Message rendering
Feature flag check: Alle V2 features achter feature flags voor backward compatibility.
E5.S1 — CommandCenter V3 integratie
Bestanden:
components/cortex/command-center/command-center.tsx(AANPASSEN)- Eventueel chat panel component
Actie: Integreer nieuwe components in bestaande UI.
Integratie punten:
// In chat messages rendering
{result.chain.actions.length > 1 ? (
<ActionChainCard
chain={result.chain}
onConfirm={handleConfirm}
onSkip={handleSkip}
onRetry={handleRetry}
/>
) : (
// Bestaande single-action UI
)}
// Nudge toast (fixed position)
{suggestions.length > 0 && (
<NudgeToast
suggestion={suggestions[0]}
onAccept={handleAcceptSuggestion}
onDismiss={handleDismissSuggestion}
/>
)}
Feature flag wrapper:
import { FEATURE_FLAGS } from '@/lib/config/feature-flags';
{FEATURE_FLAGS.CORTEX_MULTI_INTENT && chain.actions.length > 1 && (
<ActionChainCard ... />
)}
Done criteria:
- ActionChainCard toont bij multi-intent
- NudgeToast verschijnt na matching actie
- V1 UI werkt nog als flags uit staan
E5.S2 — Chain execution flow
Bestand: lib/cortex/chain-executor.ts (NIEUW) of in store actions
Actie: Sequentiële uitvoering van acties in een chain.
Flow:
async function executeChain(chain: IntentChain): Promise<void> {
for (const action of chain.actions) {
// 1. Update status → 'confirming' (als requiresConfirmation)
// 2. Wacht op user confirmation OF skip
// 3. Update status → 'executing'
// 4. Execute action
// 5. Update status → 'success' of 'failed'
// 6. Trigger Nudge evaluation
}
}
Confirmation handling:
// Als action.requiresConfirmation === true
// Wacht tot user op "Bevestig" of "Overslaan" klikt
// Store: pendingConfirmation state
Done criteria:
- Acties worden sequentieel uitgevoerd
- Confirmation dialog werkt
- Failed action stopt niet hele chain
- Nudge triggered na success
E5.S3 — Integration tests
Bestand: __tests__/integration/cortex-v2.test.ts (NIEUW)
Actie: E2E tests voor complete flows.
Test scenarios:
describe('Cortex V2 Integration', () => {
describe('Classification Flow', () => {
test('simple input → Reflex handles', async () => {
const response = await fetch('/api/cortex/classify', {
method: 'POST',
body: JSON.stringify({ input: 'agenda vandaag', context: mockContext }),
});
expect(response.handledBy).toBe('reflex');
});
test('multi-intent → Orchestrator handles', async () => {
const response = await fetch('/api/cortex/classify', {
method: 'POST',
body: JSON.stringify({ input: 'zeg jan af en maak notitie', context: mockContext }),
});
expect(response.handledBy).toBe('orchestrator');
expect(response.chain.actions).toHaveLength(2);
});
});
describe('Nudge Flow', () => {
test('wondzorg notitie → suggestion', ...);
});
});
Done criteria:
pnpm test __tests__/integration/slaagt- Coverage voor happy paths
- AI calls gemockt voor deterministische tests
E5.S4 — Demo scenario
Bestand: docs/intent/demo-script-cortex-v2.md (NIEUW)
Actie: Documenteer demo flow met exacte zinnen en verwachte resultaten.
Demo script (5 minuten):
## Demo: Cortex V2 - Van Reactief naar Proactief
### Setup
- Open EPD met patiënt "Jan de Vries"
- Shift: Ochtenddienst
### Scene 1: Snelheid (30 sec)
**Zeg:** "Agenda vandaag"
**Verwacht:** Direct resultaat (<20ms), geen spinner
**Highlight:** "Dit is Layer 1 - de Reflex Arc"
### Scene 2: Multi-Intent (1.5 min)
**Zeg:** "Zeg Jan af en maak notitie dat hij griep heeft"
**Verwacht:** ActionChainCard met 2 acties
**Highlight:** "Het systeem begrijpt dat dit twee dingen zijn"
### Scene 3: Context (1 min)
**Zeg:** "Maak notitie voor haar" (met Marie open)
**Verwacht:** Notitie voor Marie
**Highlight:** "Het snapt wie 'haar' is"
### Scene 4: Proactiviteit (1.5 min)
**Zeg:** "Notitie: wond verzorgd, ziet er goed uit"
**Verwacht:** NudgeToast - "Wondcontrole inplannen?"
**Highlight:** "Het denkt mee over vervolgacties"
### Backup
- Als AI down: toon Reflex-only (graceful degradation)
- Pre-seeded data beschikbaar
Done criteria:
- Demo script geschreven
- Test data geseeded
- Backup scenario getest
6. Kwaliteit & Testplan
Test Types
| Test Type | Scope | Tools | Verantwoordelijke |
|---|---|---|---|
| Unit Tests | Reflex classifier, entity extractor, nudge | Vitest | Developer |
| Integration Tests | API endpoints, AI responses | Vitest + MSW | Developer |
| E2E Tests | Complete flows | Playwright | Developer |
| Manual Tests | Demo scenarios | Checklist | Developer |
Test Coverage Targets
- Unit tests: Reflex classifier, entity extractor
- Integration tests:
/api/cortex/classify,/api/cortex/context - E2E tests: Multi-intent flow, Nudge flow
Manual Test Checklist (MVP)
| # | Scenario | Expected Result |
|---|---|---|
| 1 | "Agenda vandaag" | Direct resultaat (<20ms), geen spinner |
| 2 | "Zoek Jan" | Direct zoekresultaat |
| 3 | "Zeg Jan af en maak notitie: ziek" | 2 acties in ActionChainCard |
| 4 | "Maak notitie voor hem" (met actieve patiënt) | Patiëntnaam correct resolved |
| 5 | "Plan afspraak morgen 14:00" | Datum correct berekend |
| 6 | "Notitie: wond verzorgd" | NudgeToast verschijnt |
| 7 | Ambigue input | ClarificationCard met opties |
| 8 | AI API down | Graceful fallback naar Reflex |
Test Zinnen Dataset
{
"single_intent": [
{ "input": "notitie jan medicatie", "expected": ["dagnotitie"] },
{ "input": "zoek marie", "expected": ["zoeken"] },
{ "input": "agenda vandaag", "expected": ["agenda_query"] }
],
"multi_intent": [
{
"input": "Zeg Jan af en maak notitie dat hij griep heeft",
"expected": ["cancel_appointment", "dagnotitie"]
}
],
"context_dependent": [
{
"input": "Maak notitie voor hem",
"context": { "activePatient": { "name": "Piet" } },
"expected": ["dagnotitie"],
"entities": [{ "patientResolution": "pronoun" }]
}
]
}
7. Demo & Presentatieplan
Demo Scenario
Duur: 5 minuten Doelgroep: Developers, stakeholders, social media (Build in Public) Locatie: Localhost of Vercel preview
Demo Flow
| Stap | Actie | Doel | Tijd |
|---|---|---|---|
| 1 | "Agenda vandaag" | Toon Reflex snelheid | 30s |
| 2 | "Zeg Jan af en maak notitie: hij heeft griep" | Toon Multi-intent + ActionChainCard | 1.5m |
| 3 | "Maak notitie voor haar" (met Marie open) | Toon Pronoun resolution | 1m |
| 4 | "Plan afspraak morgen 14:00" | Toon Relatieve tijd | 30s |
| 5 | "Wond verzorgd, ziet er goed uit" | Toon Nudge suggestie | 1.5m |
Backup Plan
- Lokale versie bij internet issues
- Pre-seeded data als AI API niet reageert
- Screenshots als complete fallback
8. Risico's & Mitigatie
| Risico | Kans | Impact | Mitigatie | Owner |
|---|---|---|---|---|
| AI output inconsistent | Middel | Hoog | Temperature=0, strikte JSON schema, fallback to unknown | Dev |
| Multi-intent parsing faalt | Laag | Hoog | Uitgebreide test dataset, fallback naar single intent | Dev |
| Anthropic API down | Laag | Middel | Graceful degradation naar Reflex-only | Dev |
| Scope creep | Hoog | Middel | Strikte MVP scope, 6 user stories max | PM |
| Privacy breach (logs) | Laag | Hoog | Input sanitization, geen PII in production logs | Dev |
| False positive Nudge | Middel | Laag | Specifieke regex met word boundaries | Dev |
| Performance degradatie | Laag | Middel | Caching, monitoring, threshold tuning | Dev |
9. MVP User Stories Mapping
De MVP User Stories uit mvp-userstories-intent-system.md zijn als volgt verdeeld:
Thema 1: De Slimme Assistent (Core Intelligence)
| User Story | Epic | Stories |
|---|---|---|
| US-MVP-01: Twee acties in één zin | E2 | E2.S1-S5 |
| US-MVP-02: Verwijzen naar "deze patiënt" of "hij" | E2 | E2.S2-S4 |
| US-MVP-03: Impliciete tijd ("morgen") | E1, E2 | E1.S2, E2.S2 |
Thema 2: Hybride Snelheid (Architecture)
| User Story | Epic | Stories |
|---|---|---|
| US-MVP-04: Simpele commando's direct (<20ms) | E1 | E1.S1-S4 |
| US-MVP-05: Zien dat systeem nadenkt | E3 | E3.S4 |
Thema 3: De Partner (Proactivity)
| User Story | Epic | Stories |
|---|---|---|
| US-MVP-06: Proactieve suggestie (wondzorg) | E4 | E4.S1-S3 |
10. Succescriteria
Functionele Criteria
| Criterium | Target | Meetmethode |
|---|---|---|
| Multi-intent herkenning | "X en Y" zinnen correct gesplitst | Test dataset |
| Pronoun resolution | "hij/zij" correct resolved | Test met actieve patiënt |
| Reflex hit rate | >70% lokaal afgehandeld | Logging metrics |
| AI latency p95 | <800ms | Performance monitoring |
| Nudge trigger | Wondzorg → suggestie getoond | Manual test |
UX Criteria
| Criterium | Target |
|---|---|
| Geen "Ik begrijp het niet" | Altijd poging tot begrip of clarification |
| Processing feedback | Spinner bij AI-acties |
| Demo duur | Volledige flow in ≤5 minuten |
Technische Criteria
| Criterium | Target |
|---|---|
| Reflex latency | <20ms |
| AI fallback werkt | Bij Anthropic 503 → graceful degradation |
| Geen PII in logs | Input gesanitized |
11. Referenties
Mission Control Documents
| Document | Locatie |
|---|---|
| PRD | docs/intent/prd-cortex-v2.md |
| FO | docs/intent/fo-cortex-intent-system-v2.md |
| TO | docs/intent/to-cortex-v2.md |
| Architectuur | docs/intent/architecture-cortex-v2.md |
| MVP User Stories | docs/intent/mvp-userstories-intent-system.md |
| Haalbaarheidsanalyse | docs/intent/haalbaarheidsanalyse-cortex-v2.md |
Tech Documentatie
- Next.js: https://nextjs.org/docs
- Supabase: https://supabase.com/docs
- Anthropic: https://docs.anthropic.com
- Zustand: https://docs.pmnd.rs/zustand
12. Glossary
| Term | Betekenis |
|---|---|
| Cortex | Het intent classificatie systeem (voorheen "Swift") |
| Reflex Arc | Layer 1 - snelle lokale pattern matching (<20ms) |
| Orchestrator | Layer 2 - AI-gedreven classificatie (~400ms) |
| Nudge | Layer 3 - proactieve suggesties na acties |
| IntentChain | Lijst van intents uit één gebruikersinput |
| Entity | Geëxtraheerde data (patiëntnaam, datum, etc.) |
| Artifact | UI component voor een specifieke taak |
| Escalatie | Doorsturen van Reflex naar Orchestrator |
Versiehistorie
| Versie | Datum | Auteur | Wijziging |
|---|---|---|---|
| v1.0 | 30-12-2025 | Colin Lit | Initiële versie op basis van PRD, FO, TO, Architecture docs |
| v1.1 | 31-12-2025 | Colin Lit | Dev-instructies per epic toegevoegd, file mappings, done criteria |
| v1.2 | 01-01-2026 | Colin Lit | Epic 2 (Intent Orchestrator) compleet - alle 6 stories afgerond |