Files
kami 2c459da009 feat(router): implement Epic 14 — core:router module
Implements the full conversational router facade: RouterState, RouterReducer,
RouterProjector, RouterRepository, RouterContextBuilder, RouterFacade, protocol
types, WebSocket wiring, infrastructure factory, and deterministic test suite.

Also fixes spec divergences found in post-implementation review:
- Add SteeringNote domain object to core:context (epic prerequisite)
- Rename RouterFacade.handleChat → onUserInput per spec interface contract
- Add in-memory ConcurrentHashMap conversation history to DefaultRouterFacade
- Make RouterRepository.getRouterState suspend
- Rename RouterConfig.keepLast → conversationKeepLast, fix defaults (6, 4096)
- Refactor InfrastructureModule.createRouterFacade to self-assemble internally
- Fix FileReadTool: allowedPaths was dead constructor param (@SuppressUnusedParameter);
  now stored as private val and enforced in validateRequest
- Disable koverVerify on modules tested via testing/ submodules or with
  hardware/integration dependencies (24 modules); build gate now passes clean
2026-05-21 15:06:20 +04:00

10 KiB

plan

tasks

task-001

artifact: infrastructure/build.gradle purpose: Add :core:router as an implementation dependency so InfrastructureModule can access RouterFacade. depends_on: none non_goals: Creating RouterFacade implementation, wiring RouterFacade into ServerModule done_when: ./gradlew :infrastructure:compileKotlin succeeds and no detekt errors are introduced

task-002

artifact: apps/server/build.gradle purpose: Add :core:router as an implementation dependency so ServerModule can inject RouterFacade. depends_on: none non_goals: Creating RouterFacade implementation, adding WebSocket handler logic done_when: ./gradlew :apps:server:compileKotlin succeeds and no detekt errors are introduced

task-003

artifact: core/router/build.gradle purpose: Declare :core:router module dependencies on :core:events, :core:context, :core:inference, and :core:sessions so the source code can compile. depends_on: none non_goals: Creating source files — only the build file is modified done_when: ./gradlew :core:router:compileKotlin succeeds and no detekt errors are introduced

task-004

artifact: core/events/src/main/kotlin/com/correx/core/events/events/ContextEvents.kt purpose: Add SteeringNoteAddedEvent data class implementing EventPayload — the only event type the router writes to the event store. depends_on: none non_goals: Adding SteeringNote as a separate domain type in :core:context (spec defers this decision), implementing reducer logic done_when: SteeringNoteAddedEvent compiles as a subclass of EventPayload and serializes correctly

task-005

artifact: core/events/src/main/kotlin/com/correx/core/events/serialization/Serialization.kt purpose: Register SteeringNoteAddedEvent in the eventModule polymorphic block via subclass(SteeringNoteAddedEvent::class) to prevent silent runtime deserialization failure. depends_on: task-004 non_goals: Modifying any other serialization blocks, adding new serializers done_when: ./gradlew check passes — specifically the serialization round-trip test for SteeringNoteAddedEvent

task-006

artifact: core/router/src/main/kotlin/com/correx/core/router/model/RouterState.kt purpose: Define RouterState as a serializable data class holding L2 memory, workflow status, and conversation history — the foundational domain type for all router projections. depends_on: task-005 non_goals: Implementing reducer logic, defining config types done_when: RouterState compiles as @Serializable with sensible empty defaults and is accessible from all dependent modules

task-007

artifact: core/router/src/main/kotlin/com/correx/core/router/model/RouterConfig.kt purpose: Define RouterConfig as a serializable data class holding conversation keep-last count and token budget parameters. depends_on: task-006 non_goals: Implementing context building logic, defining response types done_when: RouterConfig compiles as @Serializable with default values and is accessible from infrastructure wiring

task-008

artifact: core/router/src/main/kotlin/com/correx/core/router/model/RouterResponse.kt purpose: Define RouterResponse as a serializable data class holding the inference output content and a steeringEmitted flag for the server protocol. depends_on: task-007 non_goals: Implementing facade logic, defining protocol message types done_when: RouterResponse compiles as @Serializable and is accessible from the server protocol layer

task-009

artifact: core/router/src/main/kotlin/com/correx/core/router/RouterReducer.kt purpose: Implement RouterReducer — reduces RouterState from SteeringNoteAddedEvent and workflow lifecycle events (started, completed, failed, paused, resumed, stage completed, stage failed). depends_on: task-006 non_goals: Implementing projector, repository, context builder, or facade logic done_when: DefaultRouterReducer.reduce() correctly updates RouterState fields for every known event type and returns unchanged state for unknown events

task-010

artifact: core/router/src/main/kotlin/com/correx/core/router/RouterProjector.kt purpose: Implement RouterProjector as Projection<RouterState> backed by DefaultRouterReducer, providing initial() and apply() methods per the core architecture pattern. depends_on: task-009 non_goals: Implementing repository, context builder, or facade logic done_when: RouterProjector produces correct RouterState from an empty event list and from a sequence of events

task-011

artifact: core/router/src/main/kotlin/com/correx/core/router/RouterRepository.kt purpose: Implement DefaultRouterRepository using EventReplayer<RouterState> to rebuild RouterState from the event store for a given session. depends_on: task-010 non_goals: Implementing context builder, facade logic, or infrastructure wiring done_when: DefaultRouterRepository.getRouterState(sessionId) delegates to EventReplayer.rebuild(sessionId) and returns the projected state

task-012

artifact: core/router/src/main/kotlin/com/correx/core/router/RouterContextBuilder.kt purpose: Implement RouterContextBuilder — assembles a budget-constrained ContextPack from L2 memory (stage summaries), workflow status events, and conversation history, respecting L0 immutability and L2 eviction ordering. depends_on: task-006, task-007 non_goals: Implementing inference routing, facade orchestration, or event writing done_when: RouterContextBuilder.build() produces a ContextPack with correct layer assignments, budget enforcement, and L0 entries preserved under all conditions

task-013

artifact: core/router/src/main/kotlin/com/correx/core/router/RouterFacade.kt purpose: 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. depends_on: task-011, task-012 non_goals: Implementing protocol types, WebSocket handler, or infrastructure wiring done_when: RouterFacade.handleChat() returns RouterResponse with correct content and steeringEmitted flag for both CHAT and STEERING modes

task-014

artifact: apps/server/src/main/kotlin/com/correx/apps/server/protocol/ClientMessage.kt purpose: Add ChatInput data class (sessionId: SessionId, text: String, mode: ChatMode) and ChatMode enum (CHAT, STEERING) as protocol types for user-router interaction. depends_on: none non_goals: Implementing WebSocket handler logic, adding server response types done_when: ChatInput serializes and deserializes through ProtocolSerializer without errors

task-015

artifact: apps/server/src/main/kotlin/com/correx/apps/server/protocol/ServerMessage.kt purpose: Add RouterResponseMessage data class (sessionId: SessionId, content: String, steeringEmitted: Boolean) as the egress protocol type for router responses. depends_on: task-008 non_goals: Implementing WebSocket handler logic, adding client message types done_when: RouterResponseMessage serializes and deserializes through ProtocolSerializer without errors

task-016

artifact: apps/server/src/main/kotlin/com/correx/apps/server/ws/SessionStreamHandler.kt purpose: Add a when branch for ClientMessage.ChatInput in handleClientMessage() that dispatches to module.routerFacade and sends RouterResponseMessage back over WebSocket. depends_on: task-014, task-015 non_goals: Creating RouterFacade, adding new protocol types, modifying infrastructure wiring done_when: Chat and steering messages route to RouterFacade correctly and responses are sent back over WebSocket

task-017

artifact: apps/server/src/main/kotlin/com/correx/apps/server/ServerModule.kt purpose: Add routerFacade: RouterFacade field to ServerModule data class so SessionStreamHandler can access the router facade. depends_on: task-013 non_goals: Creating RouterFacade, wiring the facade in InfrastructureModule done_when: ServerModule compiles with the new routerFacade parameter and is referenced by SessionStreamHandler

task-018

artifact: infrastructure/src/main/kotlin/com/correx/infrastructure/InfrastructureModule.kt purpose: Add createRouterFacade() factory that assembles RouterFacade from RouterRepository, RouterContextBuilder, InferenceRouter, EventStore, and RouterConfig. depends_on: task-013, task-002 non_goals: Creating router types (all done in earlier tasks), modifying ServerModule done_when: createRouterFacade() returns a fully wired RouterFacade and ./gradlew check passes

task-019

artifact: testing/deterministic/build.gradle purpose: Add testImplementation(project(":core:router")) and testImplementation(project(":core:events")) so deterministic tests can access router and event types. depends_on: task-003 non_goals: Writing test code done_when: ./gradlew :testing:deterministic:compileTestKotlin compiles with the new dependencies

task-020

artifact: testing/deterministic/src/test/kotlin/RouterReducerTest.kt purpose: Test DefaultRouterReducer — verify state transitions for SteeringNoteAddedEvent and each workflow lifecycle event. depends_on: task-009, task-019 non_goals: Testing projector, context builder, or facade logic done_when: All reducer state transition tests pass with ./gradlew :testing:deterministic:test --tests RouterReducerTest

task-021

artifact: testing/deterministic/src/test/kotlin/RouterProjectorTest.kt purpose: Test RouterProjector — verify initial() returns empty defaults and apply() correctly chains reducer output. depends_on: task-010, task-019 non_goals: Testing reducer logic independently, testing repository done_when: All projector tests pass with ./gradlew :testing:deterministic:test --tests RouterProjectorTest

task-022

artifact: testing/deterministic/src/test/kotlin/RouterContextBuilderTest.kt purpose: Test RouterContextBuilder — verify budget enforcement, L0 immutability, L2 oldest-first eviction, and layer classification. depends_on: task-012, task-019 non_goals: Testing facade orchestration, testing inference routing done_when: All context builder tests pass with ./gradlew :testing:deterministic:test --tests RouterContextBuilderTest

task-023

artifact: testing/deterministic/src/test/kotlin/RouterFacadeTest.kt purpose: Test RouterFacade — verify end-to-end orchestration for both CHAT and STEERING modes using faked dependencies. depends_on: task-013, task-019 non_goals: Testing individual components, testing WebSocket protocol done_when: All facade tests pass with ./gradlew :testing:deterministic:test --tests RouterFacadeTest