feat(events,server): ExecutionPlanRejectedEvent — freestyle compile failure visible in log
Both silent-failure branches in FreestyleDriver.lockAndRun now emit ExecutionPlanRejectedEvent (source=compile or missing_content) before returning, satisfying invariant #1. Event registered in eventModule for correct polymorphic serialization.
This commit is contained in:
@@ -2,6 +2,7 @@ package com.correx.apps.server.freestyle
|
||||
|
||||
import com.correx.core.events.events.EventMetadata
|
||||
import com.correx.core.events.events.ExecutionPlanLockedEvent
|
||||
import com.correx.core.events.events.ExecutionPlanRejectedEvent
|
||||
import com.correx.core.events.events.NewEvent
|
||||
import com.correx.core.events.stores.EventStore
|
||||
import com.correx.core.events.types.ArtifactId
|
||||
@@ -32,11 +33,13 @@ class FreestyleDriver(
|
||||
suspend fun lockAndRun(sessionId: SessionId) {
|
||||
val json = planContent(sessionId) ?: run {
|
||||
log.warn("freestyle: no execution_plan content for session={}", sessionId.value)
|
||||
emitRejected(sessionId, "no execution_plan content produced by planning phase", "missing_content")
|
||||
return
|
||||
}
|
||||
val graph = runCatching { compiler.compile(json, "freestyle-${sessionId.value}") }
|
||||
.getOrElse {
|
||||
log.error("freestyle: plan failed to compile: {}", it.message)
|
||||
emitRejected(sessionId, "plan failed to compile: ${it.message}", "compile")
|
||||
return
|
||||
}
|
||||
eventStore.append(
|
||||
@@ -61,6 +64,26 @@ class FreestyleDriver(
|
||||
runPhase2(sessionId, graph, config)
|
||||
}
|
||||
|
||||
private suspend fun emitRejected(sessionId: SessionId, reason: String, source: String) {
|
||||
eventStore.append(
|
||||
NewEvent(
|
||||
metadata = EventMetadata(
|
||||
eventId = EventId(UUID.randomUUID().toString()),
|
||||
sessionId = sessionId,
|
||||
timestamp = Clock.System.now(),
|
||||
schemaVersion = 1,
|
||||
causationId = null,
|
||||
correlationId = null,
|
||||
),
|
||||
payload = ExecutionPlanRejectedEvent(
|
||||
sessionId = sessionId,
|
||||
reason = reason,
|
||||
source = source,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val log = LoggerFactory.getLogger(FreestyleDriver::class.java)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.correx.core.artifacts.kind.ConfigArtifactKind
|
||||
import com.correx.core.artifacts.kind.DefaultArtifactKindRegistry
|
||||
import com.correx.core.artifacts.kind.JsonSchema
|
||||
import com.correx.core.events.events.ExecutionPlanLockedEvent
|
||||
import com.correx.core.events.events.ExecutionPlanRejectedEvent
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.kernel.execution.WorkflowResult
|
||||
import com.correx.core.kernel.orchestration.OrchestrationConfig
|
||||
@@ -123,6 +124,12 @@ class FreestyleDriverTest {
|
||||
|
||||
assertTrue(lockedEvents.isEmpty(), "No lock event expected for malformed plan")
|
||||
assertEquals(0, runPhase2Invocations, "runPhase2 must not be called for malformed plan")
|
||||
|
||||
val rejectedEvents = eventStore.read(sessionId)
|
||||
.map { it.payload }
|
||||
.filterIsInstance<ExecutionPlanRejectedEvent>()
|
||||
assertEquals(1, rejectedEvents.size, "Expected exactly one ExecutionPlanRejectedEvent")
|
||||
assertEquals("compile", rejectedEvents.single().source)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -153,5 +160,11 @@ class FreestyleDriverTest {
|
||||
|
||||
assertTrue(lockedEvents.isEmpty(), "No lock event when plan content is absent")
|
||||
assertEquals(0, runPhase2Invocations, "runPhase2 must not be called when plan is absent")
|
||||
|
||||
val rejectedEvents = eventStore.read(sessionId)
|
||||
.map { it.payload }
|
||||
.filterIsInstance<ExecutionPlanRejectedEvent>()
|
||||
assertEquals(1, rejectedEvents.size, "Expected exactly one ExecutionPlanRejectedEvent")
|
||||
assertEquals("missing_content", rejectedEvents.single().source)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user