epic-12: after epic audit and init commit

This commit is contained in:
2026-05-16 11:42:00 +04:00
commit c77277af0b
461 changed files with 28958 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
plugins {
id 'java-library'
id 'org.jetbrains.kotlin.jvm'
id 'org.jetbrains.kotlin.plugin.serialization'
}
dependencies {
implementation(project(":core:events"))
implementation(project(":core:transitions"))
implementation(project(":core:approvals"))
implementation(project(":core:sessions"))
implementation(project(":core:tools"))
implementation(project(":core:inference"))
implementation(project(":core:context"))
implementation(project(":core:kernel"))
implementation(project(":core:validation"))
}
@@ -0,0 +1,39 @@
package com.correx.testing.fixtures
import com.correx.core.approvals.Tier
import com.correx.core.approvals.model.DomainApprovalRequest
import com.correx.core.events.types.ApprovalRequestId
import com.correx.core.events.types.RiskSummaryId
import com.correx.core.events.types.ValidationReportId
import com.correx.core.validation.model.ValidationContext
import com.correx.core.validation.model.ValidationIssue
import com.correx.core.validation.model.ValidationSection
import com.correx.core.validation.model.ValidationSeverity
import com.correx.core.validation.pipeline.Validator
import kotlinx.datetime.Instant
fun request(
id: String,
tier: Tier,
timestamp: Instant = Instant.parse("2026-01-01T00:00:00Z"),
) = DomainApprovalRequest(
id = ApprovalRequestId(id),
tier = tier,
validationReportId = ValidationReportId("vr"),
riskSummaryId = RiskSummaryId("rs"),
timestamp = timestamp,
causationId = null,
correlationId = null,
userSteering = null,
)
fun cyclePolicyMissingValidator(): Validator = Validator {
ValidationSection(name = "cycle policy", issues = listOf(
ValidationIssue(
severity = ValidationSeverity.WARNING,
code = "CYCLE_POLICY_MISSING",
message = "forced for testing"
)
))
}
@@ -0,0 +1,25 @@
package com.correx.testing.fixtures
import com.correx.core.events.types.StageId
import com.correx.core.events.types.TransitionId
import com.correx.core.transitions.analysis.DetectedCycle
import com.correx.core.transitions.graph.TransitionEdge
object CycleFixtures {
fun simpleCycle(): DetectedCycle {
val a = StageId("A")
val b = StageId("B")
return DetectedCycle(
nodes = listOf(a, b),
edges = listOf(
TransitionEdge(
id = TransitionId("t1"),
from = a,
to = b,
condition = { true },
)
)
)
}
}
@@ -0,0 +1,53 @@
package com.correx.testing.fixtures
import com.correx.core.events.events.EventMetadata
import com.correx.core.events.events.EventPayload
import com.correx.core.events.events.NewEvent
import com.correx.core.events.events.StoredEvent
import com.correx.core.events.events.ToolInvokedEvent
import com.correx.core.events.types.EventId
import com.correx.core.events.types.SessionId
import kotlinx.datetime.Instant
object EventFixtures {
fun newEvent(
eventId: EventId,
sessionId: SessionId = SessionId("s1"),
payload: EventPayload = ToolInvokedEvent("write"),
timestamp: Instant = Instant.parse("2026-01-01T00:00:00Z")
): NewEvent {
return NewEvent(
metadata = EventMetadata(
eventId = eventId,
sessionId = sessionId,
timestamp = timestamp,
schemaVersion = 1,
causationId = null,
correlationId = null
),
payload = payload
)
}
fun stored(
eventId: EventId = EventId("e1"),
payload: EventPayload = ToolInvokedEvent("write"),
sessionId: SessionId = SessionId("s1"),
sequence: Long = 1L,
timestamp: Instant = Instant.parse("2026-01-01T00:00:00Z")
): StoredEvent {
return StoredEvent(
metadata = EventMetadata(
eventId = eventId,
sessionId = sessionId,
timestamp = timestamp,
schemaVersion = 1,
causationId = null,
correlationId = null
),
sequence = sequence,
payload = payload
)
}
}
@@ -0,0 +1,148 @@
package com.correx.testing.fixtures
import com.correx.core.context.model.ContextPack
import com.correx.core.events.events.InferenceCompletedEvent
import com.correx.core.events.events.NewEvent
import com.correx.core.events.types.ContextPackId
import com.correx.core.events.types.InferenceRequestId
import com.correx.core.events.types.ProviderId
import com.correx.core.events.types.SessionId
import com.correx.core.events.types.StageId
import com.correx.core.inference.FinishReason
import com.correx.core.inference.GenerationConfig
import com.correx.core.inference.InferenceProvider
import com.correx.core.inference.InferenceRequest
import com.correx.core.inference.InferenceResponse
import com.correx.core.inference.InferenceRouter
import com.correx.core.inference.ModelCapability
import com.correx.core.inference.TokenUsage
import com.correx.testing.fixtures.inference.MockInferenceProvider
import kotlinx.datetime.Clock
import java.util.*
@Suppress("LongParameterList")
object InferenceFixtures {
fun fixedProvider(): InferenceProvider {
return MockInferenceProvider(
fixedResponse = "Fixed test response",
artificialDelayMs = 0,
)
}
fun fixedRouter(): InferenceRouter {
return object : InferenceRouter {
override suspend fun route(
stageId: StageId,
requiredCapabilities: Set<ModelCapability>,
): InferenceProvider {
return fixedProvider()
}
}
}
fun failingRouter(): InferenceRouter {
return object : InferenceRouter {
override suspend fun route(
stageId: StageId,
requiredCapabilities: Set<ModelCapability>,
): InferenceProvider {
return MockInferenceProvider(forcedFailure = "Simulated failure")
}
}
}
fun contextPack(): ContextPack {
return ContextPack(
id = ContextPackId("cp1"),
sessionId = SessionId("s1"),
stageId = StageId("stage1"),
layers = emptyMap(),
budgetUsed = 0,
budgetLimit = 4096,
)
}
fun generationConfig(): GenerationConfig {
return GenerationConfig(
temperature = 0.7,
topP = 1.0,
maxTokens = 2048,
)
}
fun inferenceCompleted(
requestId: InferenceRequestId,
sessionId: SessionId,
stageId: StageId,
providerId: ProviderId,
tokensUsed: TokenUsage,
latencyMs: Long,
): InferenceCompletedEvent {
return InferenceCompletedEvent(
requestId = requestId,
sessionId = sessionId,
stageId = stageId,
providerId = providerId,
tokensUsed = tokensUsed,
latencyMs = latencyMs,
)
}
fun inferenceRequest(
sessionId: SessionId,
stageId: StageId,
requestId: InferenceRequestId? = null,
): InferenceRequest {
return InferenceRequest(
requestId = requestId ?: InferenceRequestId(UUID.randomUUID().toString()),
sessionId = sessionId,
stageId = stageId,
contextPack = contextPack(),
generationConfig = generationConfig(),
)
}
fun inferenceResponse(
requestId: InferenceRequestId,
text: String = "test response",
tokensUsed: TokenUsage = TokenUsage(promptTokens = 100, completionTokens = 200),
latencyMs: Long = 500,
): InferenceResponse {
return InferenceResponse(
requestId = requestId,
text = text,
finishReason = FinishReason.Stop,
tokensUsed = tokensUsed,
latencyMs = latencyMs,
)
}
fun newInferenceCompletedEvent(
requestId: InferenceRequestId,
sessionId: SessionId,
stageId: StageId,
providerId: ProviderId,
tokensUsed: TokenUsage,
latencyMs: Long,
): NewEvent {
return com.correx.core.events.events.NewEvent(
metadata = com.correx.core.events.events.EventMetadata(
eventId = com.correx.core.events.types.EventId("inf-$requestId"),
sessionId = sessionId,
timestamp = Clock.System.now(),
schemaVersion = 1,
causationId = null,
correlationId = null,
),
payload = InferenceCompletedEvent(
requestId = requestId,
sessionId = sessionId,
stageId = stageId,
providerId = providerId,
tokensUsed = tokensUsed,
latencyMs = latencyMs,
),
)
}
}
@@ -0,0 +1,28 @@
package com.correx.testing.fixtures
import com.correx.core.events.types.StageId
import com.correx.core.events.types.TransitionId
import com.correx.core.transitions.graph.StageConfig
import com.correx.core.transitions.graph.TransitionEdge
import com.correx.core.transitions.graph.WorkflowGraph
object WorkflowFixtures {
fun simpleGraph(): WorkflowGraph {
val a = StageId("A")
val b = StageId("B")
val t1 = TransitionEdge(
id = TransitionId("t1"),
from = a,
to = b,
condition = { true }
)
return WorkflowGraph(
stages = mapOf(a to StageConfig(), b to StageConfig()),
transitions = setOf(t1),
start = a
)
}
}
@@ -0,0 +1,26 @@
package com.correx.testing.fixtures.context
import com.correx.core.context.builder.DefaultContextPackBuilder
import com.correx.core.context.compression.ContextCompressor
import com.correx.core.context.compression.CompressionStrategy
import com.correx.core.context.model.ContextEntry
import com.correx.core.context.model.TokenBudget
object ContextFixtures {
fun simpleCompressor(): ContextCompressor {
return object : ContextCompressor {
override fun compress(
entries: List<ContextEntry>,
budget: TokenBudget,
strategy: CompressionStrategy
): List<ContextEntry> {
return entries
}
}
}
fun simpleBuilder(): DefaultContextPackBuilder {
return DefaultContextPackBuilder(simpleCompressor())
}
}
@@ -0,0 +1,72 @@
package com.correx.testing.fixtures.inference
import com.correx.core.events.types.ProviderId
import com.correx.core.inference.CapabilityScore
import com.correx.core.inference.FinishReason
import com.correx.core.inference.InferenceProvider
import com.correx.core.inference.InferenceRequest
import com.correx.core.inference.InferenceResponse
import com.correx.core.inference.ModelCapability
import com.correx.core.inference.ProviderHealth
import com.correx.core.inference.TokenUsage
import com.correx.core.inference.Tokenizer
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive
/**
* Configurable fake provider for deterministic tests.
*
* Modes (mutually exclusive, checked in order):
* 1. [forcedFailure] — throws [InferenceProviderException] immediately
* 2. [artificialDelayMs] > 0 — suspends before responding (tests timeout/cancel)
* 3. default — returns [fixedResponse] immediately
*/
@Suppress("LongParameterList")
class MockInferenceProvider(
override val id: ProviderId = ProviderId("mock"),
override val name: String = "Mock Provider",
private val fixedResponse: String = "mock response",
private val artificialDelayMs: Long = 0L,
private val forcedFailure: String? = null,
private val declaredCapabilities: Set<CapabilityScore> = setOf(
CapabilityScore(ModelCapability.General, 1.0),
),
private val health: ProviderHealth = ProviderHealth.Healthy,
) : InferenceProvider {
override val tokenizer: Tokenizer = MockTokenizer()
var inferCallCount: Int = 0
private set
override suspend fun infer(request: InferenceRequest): InferenceResponse {
inferCallCount++
if (forcedFailure != null) throw InferenceProviderException(forcedFailure)
if (artificialDelayMs > 0L) {
delay(artificialDelayMs)
currentCoroutineContext().ensureActive() // cooperative cancellation checkpoint
}
val usage = TokenUsage(
promptTokens = tokenizer.countTokens(request.contextPack.toString()),
completionTokens = tokenizer.countTokens(fixedResponse),
)
return InferenceResponse(
requestId = request.requestId,
text = fixedResponse,
finishReason = FinishReason.Stop,
tokensUsed = usage,
latencyMs = artificialDelayMs,
)
}
override suspend fun healthCheck(): ProviderHealth = health
override fun capabilities(): Set<CapabilityScore> = declaredCapabilities
}
class InferenceProviderException(message: String) : Exception(message)
@@ -0,0 +1,17 @@
package com.correx.testing.fixtures.inference
import com.correx.core.inference.Token
import com.correx.core.inference.Tokenizer
/**
* Character-based approximation. Deterministic. Not model-accurate.
* Sufficient for contract and budget tests.
*/
@Suppress("MagicNumber")
class MockTokenizer : Tokenizer {
override suspend fun tokenize(text: String): List<Token> =
List(text.chunked(4).size) { i -> Token(i) } // 1 token ≈ 4 chars
override suspend fun countTokens(text: String): Int =
(text.length + 3) / 4
}
@@ -0,0 +1,24 @@
package com.correx.testing.fixtures.kernel
import com.correx.core.events.types.SessionId
import com.correx.core.sessions.SessionState
import com.correx.core.sessions.SessionStatus
import com.correx.core.sessions.projections.replay.EventReplayer
import kotlinx.datetime.Clock
class MockEventReplayer : EventReplayer<SessionState> {
private val states = mutableMapOf<SessionId, SessionState>()
fun setSessionState(sessionId: SessionId, state: SessionState) {
states[sessionId] = state
}
override fun rebuild(sessionId: SessionId): SessionState {
return states[sessionId] ?: SessionState(
status = SessionStatus.ACTIVE,
createdAt = Clock.System.now(),
updatedAt = Clock.System.now(),
invalidTransitions = 0
)
}
}
@@ -0,0 +1,30 @@
package com.correx.testing.fixtures.kernel
import com.correx.core.events.types.SessionId
import com.correx.core.sessions.Session
import com.correx.core.sessions.SessionState
import com.correx.core.sessions.SessionStatus
import kotlinx.datetime.Clock
class MockSessionRepository {
private val sessions = mutableMapOf<SessionId, Session>()
fun setSession(sessionId: SessionId, session: Session) {
sessions[sessionId] = session
}
fun getSession(sessionId: SessionId): Session {
return sessions[sessionId] ?: Session(
sessionId = sessionId,
state = SessionState(
status = SessionStatus.ACTIVE,
createdAt = Clock.System.now(),
updatedAt = Clock.System.now(),
invalidTransitions = 0,
),
)
}
fun rebuild(sessionId: SessionId): Session {
return getSession(sessionId)
}
}
@@ -0,0 +1,14 @@
package com.correx.testing.fixtures.tools
import com.correx.core.approvals.Tier
import com.correx.core.events.events.ToolRequest
import com.correx.core.tools.contract.Tool
import com.correx.core.tools.contract.ToolCapability
import com.correx.core.tools.contract.ValidationResult
class EchoTool : Tool {
override val name: String = "echo"
override val tier: Tier = Tier.T0
override val requiredCapabilities: Set<ToolCapability> = emptySet()
override fun validateRequest(request: ToolRequest): ValidationResult = ValidationResult.Valid
}
@@ -0,0 +1,15 @@
package com.correx.testing.fixtures.tools
import com.correx.core.approvals.Tier
import com.correx.core.events.events.ToolRequest
import com.correx.core.tools.contract.Tool
import com.correx.core.tools.contract.ToolCapability
import com.correx.core.tools.contract.ValidationResult
class FailingTool : Tool {
override val name: String = "failing"
override val tier: Tier = Tier.T2
override val requiredCapabilities: Set<ToolCapability> = emptySet()
override fun validateRequest(request: ToolRequest): ValidationResult =
ValidationResult.Invalid("simulated validation failure")
}
@@ -0,0 +1,14 @@
package com.correx.testing.fixtures.tools
import com.correx.core.approvals.Tier
import com.correx.core.events.events.ToolRequest
import com.correx.core.tools.contract.Tool
import com.correx.core.tools.contract.ToolCapability
import com.correx.core.tools.contract.ValidationResult
class RejectedTool : Tool {
override val name: String = "rejected"
override val tier: Tier = Tier.T4
override val requiredCapabilities: Set<ToolCapability> = emptySet()
override fun validateRequest(request: ToolRequest): ValidationResult = ValidationResult.Valid
}
@@ -0,0 +1,45 @@
package com.correx.testing.fixtures.transitions
import com.correx.core.events.types.StageId
import com.correx.core.events.types.TransitionId
import com.correx.core.transitions.evaluation.EvaluationContext
import com.correx.core.transitions.evaluation.TransitionConditionEvaluator
import com.correx.core.transitions.graph.StageConfig
import com.correx.core.transitions.graph.TransitionCondition
import com.correx.core.transitions.graph.TransitionEdge
import com.correx.core.transitions.graph.WorkflowGraph
import com.correx.core.transitions.resolution.DefaultTransitionResolver
import com.correx.core.transitions.resolution.TransitionResolver
import com.correx.testing.fixtures.WorkflowFixtures
object TransitionFixtures {
fun alwaysTrueEvaluator(): TransitionConditionEvaluator {
return object : TransitionConditionEvaluator {
override fun evaluate(condition: TransitionCondition, context: EvaluationContext): Boolean {
return true
}
}
}
fun simpleResolver(): TransitionResolver {
return DefaultTransitionResolver(alwaysTrueEvaluator())
}
fun threeStageGraph(): WorkflowGraph {
val a = StageId("A")
val b = StageId("B")
val c = StageId("C")
return WorkflowGraph(
stages = mapOf(a to StageConfig(), b to StageConfig(), c to StageConfig()),
transitions = setOf(
TransitionEdge(id = TransitionId("t1"), from = a, to = b, condition = { true }),
TransitionEdge(id = TransitionId("t2"), from = b, to = c, condition = { true }),
),
start = a
)
}
fun simpleGraph(): WorkflowGraph = WorkflowFixtures.simpleGraph()
}
@@ -0,0 +1,42 @@
package com.correx.testing.fixtures.tools
import com.correx.core.approvals.Tier
import com.correx.core.events.events.ToolRequest
import com.correx.core.events.types.SessionId
import com.correx.core.events.types.StageId
import com.correx.core.events.types.ToolInvocationId
import com.correx.core.tools.contract.ValidationResult
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertInstanceOf
class MockToolsTest {
private val request = ToolRequest(
invocationId = ToolInvocationId("inv-1"),
sessionId = SessionId("s-1"),
stageId = StageId("st-1"),
toolName = "test"
)
@Test
fun `EchoTool is T0 and validates any request as Valid`() {
val tool = EchoTool()
assertEquals(Tier.T0, tool.tier)
assertInstanceOf<ValidationResult.Valid>(tool.validateRequest(request))
}
@Test
fun `FailingTool is T2 and validates any request as Invalid`() {
val tool = FailingTool()
assertEquals(Tier.T2, tool.tier)
assertInstanceOf<ValidationResult.Invalid>(tool.validateRequest(request))
}
@Test
fun `RejectedTool is T4 and validates any request as Valid`() {
val tool = RejectedTool()
assertEquals(Tier.T4, tool.tier)
assertInstanceOf<ValidationResult.Valid>(tool.validateRequest(request))
}
}