# task-013

status: done

## goal
Implement RouterFacade — the orchestrating entry point that loads state via RouterRepository, builds context via RouterContextBuilder, routes inference via InferenceRouter, and conditionally emits SteeringNoteAddedEvent via EventStore.

## target artifact
core/router/src/main/kotlin/com/correx/core/router/RouterFacade.kt

## execution steps
1. **Create `RouterFacade` interface** in `RouterFacade.kt` at `core/router/src/main/kotlin/com/correx/core/router/RouterFacade.kt`
   - Interface signature: `suspend fun handleChat(sessionId: SessionId, text: String, mode: ChatMode = ChatMode.CHAT, stageId: StageId? = null, requiredCapabilities: Set<ModelCapability> = setOf(ModelCapability.General)): RouterResponse`
   - Package: `com.correx.core.router`
   - Imports: `SessionId`, `StageId` from `com.correx.core.events.types`; `ChatMode` enum (CHAT, STEERING) declared locally in this file; `ModelCapability` from `com.correx.core.inference`; `RouterResponse` from `com.correx.core.router.model`

2. **Create `DefaultRouterFacade` class** implementing `RouterFacade`
   - Constructor injects: `RouterRepository`, `RouterContextBuilder`, `InferenceRouter`, `EventStore`, `RouterConfig`
   - All constructor params are interfaces or data classes — never instantiate concrete dependencies inside the class
   - `handleChat()` implementation:
     a. Load `RouterState` from `routerRepository.getRouterState(sessionId)`
     b. Determine `effectiveStageId`: use provided `stageId` parameter, or fall back to `state.currentStageId`, or create `StageId("none")` as final default
     c. Build `ContextPack` via `contextBuilder.build(state, config.tokenBudget)`
     d. Route provider via `inferenceRouter.route(effectiveStageId, requiredCapabilities)`
     e. Call `provider.infer(InferenceRequest(...))` with:
        - `requestId` = new `InferenceRequestId(java.util.UUID.randomUUID().toString())`
        - `sessionId` = from parameter
        - `stageId` = effectiveStageId
        - `contextPack` = built ContextPack
        - `generationConfig` = `GenerationConfig(temperature = 0.7, topP = 0.9, maxTokens = 512)`
        - `responseFormat` = `ResponseFormat.Text`
     f. Extract `text` from `InferenceResponse`
     g. If `mode == ChatMode.STEERING`: create `NewEvent` with `SteeringNoteAddedEvent(sessionId = sessionId, content = text, stageId = stageId)` and `EventMetadata` (eventId, sessionId, timestamp = Clock.System.now(), schemaVersion = 1, causationId = null, correlationId = null); call `eventStore.append(newEvent)`
     h. Return `RouterResponse(content = text, steeringEmitted = (mode == ChatMode.STEERING))`

3. **Add `ChatMode` enum** in the same file
   - `enum class ChatMode { CHAT, STEERING }`

4. **Verify no raw event payloads, artifacts, or tool outputs** leak into the context pack — the RouterFacade only delegates to RouterContextBuilder for context assembly

5. **Verify compilation** with `./gradlew :core:router:compileKotlin`

## changed artifacts
- `core/router/src/main/kotlin/com/correx/core/router/RouterFacade.kt` — new file (interface + implementation + ChatMode enum)

## blockers
- None — task-011 (RouterRepository) and task-012 (RouterContextBuilder) are complete and the artifact files exist

## notes
- The RouterFacade does NOT implement protocol types, WebSocket handlers, or infrastructure wiring — those are separate tasks (014-018)
- `ChatMode` is declared locally in this file because it is a router-specific concept (not shared with the server protocol layer, which will define its own types in task-014)
- The `GenerationConfig` defaults (temperature=0.7, topP=0.9, maxTokens=512) are reasonable router defaults — infrastructure wiring can override if needed
- The `requiredCapabilities` defaults to `General` — this is a reasonable default for a routing assistant
- The `SteeringNoteAddedEvent` content is the inference response text, not the original user input — this allows the steering note to carry the router's synthesized guidance
- Exception handling: `InferenceRouter.route()` and `InferenceProvider.infer()` may throw; these propagate up to the caller (WebSocket handler in task-016)
- The `EventDispatcher` class from `:core:events` is available but RouterFacade writes events directly to `EventStore.append()` to maintain clear ownership boundaries
