fix: complete P4 audit findings — steering wiring, generation config

P4-1: Wire steering to actually work (advisory-only):
  - RouterFacade emits SteeringNoteAddedEvent via event store in STEERING mode
  - SessionOrchestrator reads steering notes from event store and includes them
    as ContextEntry objects (sourceType=steeringNote) in stage context packs
  - Delete dead SteeringNote data class; single vocabulary = SteeringNoteAddedEvent
  - Add optional validateSteering callback to DefaultRouterFacade for future
    ValidationPipeline wiring (avoids cross-core dependency)
  - Update RouterFacadeTest to assert event emission

P4-2: Router FACE — move hardcoded generation config:
  - Add GenerationConfig field to RouterConfig with same defaults
  - RouterFacade uses config.generationConfig instead of inline literals
  - (Remaining P4-2 items: conversation persistence and coordination
    semantics are design decisions requiring ADRs, deferred)
This commit is contained in:
2026-05-29 01:19:18 +04:00
parent f7f237e29f
commit 3981d8443d
5 changed files with 69 additions and 37 deletions
@@ -35,6 +35,7 @@ import com.correx.core.events.events.NewEvent
import com.correx.core.events.events.OrchestrationPausedEvent
import com.correx.core.events.events.OrchestrationResumedEvent
import com.correx.core.events.events.RiskAssessedEvent
import com.correx.core.events.events.SteeringNoteAddedEvent
import com.correx.core.events.events.ToolExecutionRejectedEvent
import com.correx.core.events.events.ToolInvocationRequestedEvent
import com.correx.core.events.events.ToolRequest
@@ -237,12 +238,13 @@ abstract class SessionOrchestrator(
?: ResponseFormat.Text
val schemaEntries = buildSchemaEntries(responseFormat, stageId)
val steeringEntries = buildSteeringNoteEntries(sessionId)
val contextPack = contextPackBuilder.build(
id = ContextPackId(UUID.randomUUID().toString()),
sessionId = sessionId,
stageId = stageId,
entries = systemPrompt + schemaEntries + promptEntries,
entries = systemPrompt + schemaEntries + promptEntries + steeringEntries,
budget = TokenBudget(limit = stageConfig.tokenBudget),
)
@@ -544,6 +546,23 @@ abstract class SessionOrchestrator(
)
}
private suspend fun buildSteeringNoteEntries(sessionId: SessionId): List<ContextEntry> {
val events = eventStore.read(sessionId)
return events.mapNotNull { event ->
(event.payload as? SteeringNoteAddedEvent)?.let { steering ->
ContextEntry(
id = ContextEntryId(UUID.randomUUID().toString()),
layer = ContextLayer.L2,
content = steering.content,
sourceType = "steeringNote",
sourceId = steering.stageId?.value ?: sessionId.value,
tokenEstimate = estimateTokens(steering.content),
role = EntryRole.SYSTEM,
)
}
}
}
private suspend fun emitToolArtifacts(
sessionId: SessionId,
stageId: StageId,