Files
correx/docs/specs/2026-05-20-router/exec/task-009-review.md
T
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

4.9 KiB

review-009

result

  • changes_required

findings

RouterReducer interface and DefaultRouterReducer implementation

  • Interface RouterReducer with reduce(state: RouterState, event: StoredEvent): RouterState matches the core architecture convention.
  • DefaultRouterReducer correctly implements all 8 event handlers specified in the task:
    • SteeringNoteAddedEvent → appends RouterTurn with role = TurnRole.USER to conversationHistory, using payload.content and event.metadata.timestamp.
    • WorkflowStartedEvent → sets workflowStatus = RUNNING and currentStageId = payload.startStageId.
    • WorkflowCompletedEvent → sets workflowStatus = COMPLETED, clears currentStageId (null).
    • WorkflowFailedEvent → sets workflowStatus = FAILED, clears currentStageId (null).
    • OrchestrationPausedEvent → sets workflowStatus = PAUSED.
    • OrchestrationResumedEvent → sets workflowStatus = RUNNING.
    • StageCompletedEvent → appends RouterL2Entry with StageOutcomeKind.SUCCESS to l2Memory.
    • StageFailedEvent → appends RouterL2Entry with StageOutcomeKind.FAILURE to l2Memory, clears currentStageId (null).
  • All handlers use state.copy(...) — state is never mutated in place.
  • Unknown event types return unchanged state via else -> state catch-all branch.

Payload field access verification

  • SteeringNoteAddedEvent.payload.content correct (event has content: String).
  • WorkflowStartedEvent.payload.startStageId correct (event has startStageId: StageId).
  • StageCompletedEvent.payload.stageId correct (event has stageId: StageId).
  • StageFailedEvent.payload.stageId and payload.reason correct (event has stageId: StageId and reason: String).

Architecture pattern compliance

  • Interface + Default* naming convention matches other reducers in the codebase (e.g., DefaultOrchestrationReducer).
  • Timestamps come from event.metadata.timestamp — preserves original event ordering per task notes.
  • Handler methods that don't need payload data take only state parameter — avoids detekt UnusedParameter warnings (documented in task notes).

Build & static analysis

  • :core:router:compileKotlin — succeeds.
  • :core:router:detekt — clean.
  • ./gradlew :core:router:check FAILSkoverVerify reports 0% coverage, expecting 70%. No tests exist for DefaultRouterReducer yet. This is expected since RouterReducerTest (task-020) is a separate downstream task, but it blocks the build gate.

No contradictions detected

  • No contradictions between spec, plan task-009, and the executed task-009.

missing requirements

  • No tests for DefaultRouterReducer: The task done-when criterion states "correctly updates RouterState fields for every known event type and returns unchanged state for unknown events." Verification is currently code-only review — no tests exist. Per the epic plan, RouterReducerTest (task-020) and its dependency testing/deterministic/build.gradle (task-019) are separate tasks, so this is an intentional deferment. However, the :core:router:check build fails until tests are added, which blocks CI.
  • StageStartedEvent not handled: The task only lists the events it should handle. The pre-existing StageStartedEvent (from StageEvents.kt) is correctly omitted from this reducer's scope — it belongs to the orchestration reducer. No issue here, just noted.

edge cases

  • L2 memory summary strings are hardcoded: handleStageCompleted uses "Stage $payload.stageId completed" and handleStageFailed uses "Stage $payload.stageId failed: ${payload.reason}" as summaries. These are plain text summaries suitable for L2 memory context packing (the spec says L2 entries are stage summaries). The summaries are deterministic and derive only from event payloads — no issue.
  • currentStageId cleared on StageFailedEvent but not on StageCompletedEvent: This is intentional per the task spec. StageCompletedEvent advances the workflow to the next stage (set by WorkflowStartedEvent or TransitionExecutedEvent), so clearing currentStageId on success would be incorrect. The reducer correctly only clears on failure.
  • workflowStatus on StageFailedEvent: The task spec does not require changing workflowStatus on StageFailedEvent — only currentStageId is cleared and an L2 entry appended. The workflow may have other stages remaining. This is correct.

suggested follow-up

  1. Implement RouterReducerTest (task-020) promptly — this is the critical blocker. The reducer is straightforward and has clear expected outcomes per event, making it low-effort to test. At minimum, test: state transition from each event type, unknown event pass-through, and StageFailedEvent clearing currentStageId.
  2. Consider adding a test for else -> state (unknown event passthrough) — this ensures future additions to the event system don't accidentally mutate router state.