feat(router): inject available-workflows and project-profile into router L0 context

This commit is contained in:
2026-06-11 11:11:04 +04:00
parent ac458d83e5
commit a32e9acac4
5 changed files with 114 additions and 25 deletions
@@ -7,6 +7,7 @@ import com.correx.core.events.types.SessionId
import com.correx.core.events.types.StageId
import com.correx.core.router.DefaultRouterContextBuilder
import com.correx.core.router.model.RouterConfig
import com.correx.core.router.model.WorkflowSummary
import com.correx.core.router.model.RouterL2Entry
import com.correx.core.router.model.RouterState
import com.correx.core.router.model.RouterTurn
@@ -19,6 +20,7 @@ import kotlinx.datetime.Instant
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
@@ -805,6 +807,40 @@ class RouterContextBuilderTest {
assertTrue(pack.compressionMetadata.entriesDropped >= 2)
}
// --------------------------------------------------------------------------
// A2: availableWorkflows and projectProfileText L0 injection
// --------------------------------------------------------------------------
@Test
fun `available workflows entry is injected into L0 when non-empty`() {
val workflows = listOf(WorkflowSummary("healthcheck", "Runs the health check pipeline", listOf("collect", "report")))
val pack = runBlocking { builder.build(RouterState(), TokenBudget(limit = 10000), availableWorkflows = workflows) }
val entry = (pack.layers[ContextLayer.L0] ?: emptyList()).find { it.sourceType == "availableWorkflows" }
assertNotNull(entry)
assertTrue(entry!!.content.contains("## Available workflows"))
assertTrue(entry.content.contains("healthcheck: Runs the health check pipeline (stages: collect → report)"))
}
@Test
fun `available workflows entry is omitted when list is empty`() {
val pack = runBlocking { builder.build(RouterState(), TokenBudget(limit = 10000)) }
assertNull((pack.layers[ContextLayer.L0] ?: emptyList()).find { it.sourceType == "availableWorkflows" })
}
@Test
fun `project profile text is injected into L0 when non-null`() {
val text = "## Project profile\nA Kotlin service.\n### Conventions\n- Use runCatching"
val pack = runBlocking { builder.build(RouterState(), TokenBudget(limit = 10000), projectProfileText = text) }
val entry = (pack.layers[ContextLayer.L0] ?: emptyList()).find { it.sourceType == "projectProfile" }
assertEquals(text, entry!!.content)
}
@Test
fun `project profile text is omitted when null or blank`() {
val pack = runBlocking { builder.build(RouterState(), TokenBudget(limit = 10000), projectProfileText = " ") }
assertNull((pack.layers[ContextLayer.L0] ?: emptyList()).find { it.sourceType == "projectProfile" })
}
@Test
fun `truncatedLayers populated with affected layers when drops occur`() {
val longText = "x".repeat(800)