Files
triqura-ecd/docs/swift/implementation-e5-s1-action-routing.md
colinislit 14a9d34337 feat(swift): voeg chat orchestration toe (E5)
Epic 5 compleet: Chat orchestration voor Swift Agenda Planning.
Alle agenda intents worden nu correct gerouteerd naar AgendaBlock
met user-friendly error handling en fallback opties.

E5.S1 - Action Routing (2 SP)
- routeIntentToArtifact() functie in action-parser
- Maps agenda intents naar juiste AgendaBlock mode
- Confidence threshold enforcement (< 0.7 → fallback)
- Required entity validation (patient voor create, identifier voor reschedule)
- Command-input gebruikt nieuwe routing ipv legacy openBlock
- Migratie naar modern artifact systeem (openArtifact)

E5.S2 - Chat Prompt Update (2 SP)
- 4 agenda intents toegevoegd aan Swift chat system prompt
- agenda_query: afspraken opvragen op datumrange
- create_appointment: nieuwe afspraak maken (required: patient, datetime)
- cancel_appointment: afspraak annuleren (disambiguation support)
- reschedule_appointment: afspraak verzetten (required: identifier)
- Entity extraction rules gedocumenteerd (dateRange, datetime, identifier)
- 4 complete voorbeelden met JSON action format
- Clarification questions voor incomplete data
- Prompt size: ~325 → ~525 regels (+60%)

E5.S3 - Error States (2 SP)
- AgendaErrorState component voor full-page errors
- AgendaErrorAlert component voor inline form errors
- getUserFriendlyMessage() vertaalt technical → user-friendly Dutch
- Auto-redirect bij auth errors (401 → /login)
- Fallback link naar /epd/agenda in alle error states
- Context-aware messaging (query/create/cancel/reschedule)
- Retry functionaliteit voor recoverable errors
- Dev-only technical details collapsible

Error message mapping:
- 401 → "Je sessie is verlopen. Log opnieuw in." + auto-redirect
- 403 → "Je hebt geen toegang tot deze afspraak."
- 404 → "De gevraagde afspraak kon niet worden gevonden."
- 500 → "Er ging iets mis op de server. Probeer het opnieuw."
- Network → "Geen internetverbinding. Controleer je netwerkverbinding."
- Timeout → "De aanvraag duurde te lang. Probeer het opnieuw."

Components updated:
- command-input: gebruikt routeIntentToArtifact + openArtifact
- agenda-create-form: gebruikt AgendaErrorAlert met fallback link
- chat/route: uitgebreide system prompt met agenda sectie

Nieuwe files:
- lib/swift/action-parser.ts: routeIntentToArtifact() functie
- components/swift/artifacts/blocks/agenda-error-state.tsx (225 regels)
- docs/swift/implementation-e5-s1-action-routing.md
- docs/swift/implementation-e5-s2-chat-prompt.md
- docs/swift/implementation-e5-s3-error-states.md

Documentatie:
- Bouwplan bijgewerkt: Epic 5 → Done
- 3 implementation docs met API specs en testing scenarios
- Error handling best practices gedocumenteerd

Progress: 48 SP / 51 SP (94%) - Epic 6 (QA) remaining

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 22:44:18 +01:00

225 lines
6.5 KiB
Markdown

# Epic 5.S1 Implementation: Action Routing
**Story**: E5.S1 - Action routing
**Date**: 2025-12-27
**Status**: ✅ Complete
**Story Points**: 2 SP
---
## 🎯 Objective
Implement intent routing logic that maps agenda intents to the AgendaBlock artifact with the appropriate mode and prefill data.
---
## 📝 Implementation Summary
### 1. Updated Action Parser Schemas
**File**: `lib/swift/action-parser.ts`
Added agenda intents to validation schemas:
- `agenda_query`
- `create_appointment`
- `cancel_appointment`
- `reschedule_appointment`
### 2. Created Intent Routing Function
**Function**: `routeIntentToArtifact(intent, entities, confidence)`
**Purpose**: Maps intents to artifact configurations with:
- Artifact type
- Title
- Prefill data
**Logic**:
```typescript
if (confidence < 0.7) return null; // Trigger fallback
switch (intent) {
case 'agenda_query':
return { type: 'agenda_query', title: 'Agenda', prefill: { dateRange } };
case 'create_appointment':
if (!patientName && !patientId) return null; // Missing required entity
return { type: 'create_appointment', title: 'Nieuwe afspraak', prefill: {...} };
case 'cancel_appointment':
return { type: 'cancel_appointment', title: 'Afspraak annuleren', prefill: {...} };
case 'reschedule_appointment':
if (!identifier) return null; // Need to know which appointment
return { type: 'reschedule_appointment', title: 'Afspraak verzetten', prefill: {...} };
// ... other intents (dagnotitie, zoeken, overdracht)
}
```
**Key Features**:
- ✅ Confidence threshold (0.7) enforcement
- ✅ Required entity validation (patient for create, identifier for reschedule)
- ✅ Returns null to trigger clarification when needed
- ✅ Proper prefill data extraction for each intent
### 3. Updated Command Input
**File**: `components/swift/command-center/command-input.tsx`
**Changes**:
1. Import `routeIntentToArtifact` from action-parser
2. Add `openArtifact` to store hooks
3. Replace legacy `openBlock` logic with routing:
```typescript
// OLD (direct block opening):
if (intent !== 'unknown' && confidence >= 0.5) {
openBlock(intent as BlockType, entities);
}
// NEW (routing with artifact system):
const artifactConfig = routeIntentToArtifact(intent, entities, confidence);
if (artifactConfig) {
openArtifact({
type: artifactConfig.type,
title: artifactConfig.title,
prefill: artifactConfig.prefill,
});
}
```
---
## 🔑 Key Routing Rules
| Intent | Artifact Type | Required Entities | Mode |
|--------|---------------|-------------------|------|
| `agenda_query` | `agenda_query` | None | List view with date filter |
| `create_appointment` | `create_appointment` | `patientName` OR `patientId` | Create form |
| `cancel_appointment` | `cancel_appointment` | None | Cancel view (may need disambiguation) |
| `reschedule_appointment` | `reschedule_appointment` | `identifier` | Reschedule form |
---
## 📊 Confidence Thresholds
| Confidence | Action | Example |
|------------|--------|---------|
| **≥ 0.7** | Open artifact directly | "afspraken vandaag" → AgendaBlock opens |
| **< 0.7** | Show fallback picker | Low confidence → User chooses intent manually |
| **Missing required entity** | Show fallback picker | "maak afspraak" (no patient) → Clarification needed |
---
## 🔄 Data Flow
```
User Input
Intent Classification API (/api/intent/classify)
{ intent, entities, confidence }
routeIntentToArtifact()
Artifact Config { type, title, prefill } OR null
openArtifact() OR openBlock('fallback')
AgendaBlock renders with prefilled data
```
---
## 📁 Files Modified
```
lib/swift/action-parser.ts
├── Updated ActionSchema with agenda intents
├── Updated artifact type enum
├── Added routeIntentToArtifact() function
└── Updated validateArtifactType() for agenda intents
components/swift/command-center/command-input.tsx
├── Import routeIntentToArtifact
├── Use openArtifact from store
└── Replace direct block opening with routing logic
```
---
## ✅ Acceptance Criteria
| Criterion | Status | Notes |
|-----------|--------|-------|
| Agenda intents open AgendaBlock | ✅ | Via routing function |
| Correct mode selected | ✅ | Based on intent type |
| Prefill data passed correctly | ✅ | Mapped from entities |
| Confidence threshold enforced | ✅ | < 0.7 shows fallback |
| Required entities validated | ✅ | Returns null when missing |
| TypeScript type safety | ✅ | No compilation errors |
---
## 🧪 Testing Scenarios
### Scenario 1: High Confidence Agenda Query
**Input**: "afspraken vandaag"
**Expected**:
- Intent: `agenda_query`
- Confidence: ~0.95
- Result: AgendaBlock opens in list mode with today's date range
### Scenario 2: Create Appointment with Patient
**Input**: "maak afspraak jan morgen 14:00"
**Expected**:
- Intent: `create_appointment`
- Entities: `{ patientName: "jan", datetime: {...} }`
- Result: AgendaBlock opens in create mode with prefilled patient and time
### Scenario 3: Create Appointment without Patient (Missing Entity)
**Input**: "maak afspraak morgen 14:00"
**Expected**:
- Intent: `create_appointment`
- Entities: `{ datetime: {...} }` (no patient)
- Result: FallbackPicker opens (routing returns null due to missing required entity)
### Scenario 4: Low Confidence
**Input**: "agenda ding morgen"
**Expected**:
- Intent: `agenda_query` (maybe)
- Confidence: < 0.7
- Result: FallbackPicker opens (routing returns null due to low confidence)
### Scenario 5: Reschedule with Identifier
**Input**: "verzet 14:00 naar 15:00"
**Expected**:
- Intent: `reschedule_appointment`
- Entities: `{ identifier: {...}, newDatetime: {...} }`
- Result: AgendaBlock opens in reschedule mode
---
## 🚀 Next Steps
1. **E5.S2**: Update chat API prompt to include agenda intent examples
2. **E5.S3**: Add error state handling with fallback links
3. **E6**: Manual QA testing of complete agenda flow
---
## 💡 Lessons Learned
1. **Artifact System**: Modern approach using `openArtifact()` is cleaner than legacy `openBlock()`
2. **Centralized Routing**: Single function makes intent mapping maintainable and testable
3. **Validation Early**: Checking required entities in routing prevents incomplete forms
4. **Confidence Thresholds**: 0.7 threshold balances automation with user control
5. **Type Safety**: TypeScript schemas ensure consistency across intent types
---
**Implementation Status**: ✅ Complete
**Ready for**: E5.S2 (Chat prompt update)
**Estimated Testing Time**: 15-20 minutes (manual testing with various inputs)