fix(validation): resolve F-007 artifact validator CAS-hash conflation

ArtifactPayloadValidator passed the logical slot name into the
content-hash-keyed CAS store, crashing every workflow with a typed
produces slot. Validator now reads payloads from a content map threaded
through ValidationContext (populated from the orchestrator's per-session
artifact cache across all stages) and no longer depends on ArtifactStore.

Also records the slot->CAS-hash mapping as a new ArtifactContentStoredEvent
(registered for serialization), emitted at both CAS write sites, so
artifact content is recoverable from CAS by hash after a cold start.
This commit is contained in:
2026-06-08 17:26:34 +04:00
parent 3f59faaa08
commit d518400b5f
7 changed files with 206 additions and 13 deletions
@@ -3,7 +3,6 @@ package com.correx.core.validation.artifact
import com.correx.core.artifacts.kind.FileWrittenArtifact
import com.correx.core.artifacts.kind.ProcessResultArtifact
import com.correx.core.artifacts.kind.TypedArtifactSlot
import com.correx.core.artifactstore.ArtifactStore
import com.correx.core.events.types.ArtifactId
import com.correx.core.validation.model.ValidationContext
import com.correx.core.validation.model.ValidationIssue
@@ -13,16 +12,14 @@ import com.correx.core.validation.pipeline.Validator
import kotlinx.serialization.json.Json
import java.nio.file.Path
class ArtifactPayloadValidator(
private val artifactStore: ArtifactStore,
) : Validator {
class ArtifactPayloadValidator : Validator {
override suspend fun validate(context: ValidationContext): ValidationSection {
val issues = mutableListOf<ValidationIssue>()
for ((_, stageConfig) in context.graph.stages) {
for (slot in stageConfig.produces) {
validateSlot(slot, issues)
validateSlot(slot, context.producedArtifactContent, issues)
}
}
@@ -31,11 +28,10 @@ class ArtifactPayloadValidator(
private suspend fun validateSlot(
slot: TypedArtifactSlot,
artifactContent: Map<String, String>,
issues: MutableList<ValidationIssue>,
) {
val bytes = artifactStore.get(slot.name) ?: return
val payloadStr = bytes.toString(Charsets.UTF_8)
val payloadStr = artifactContent[slot.name.value] ?: return
when (slot.kind.id) {
"file_written" ->
@@ -11,4 +11,5 @@ data class ValidationContext(
val cyclePolicies: Set<CyclePolicyBinding> = emptySet(),
val sessionState: SessionState? = null,
val availableTools: Set<String>? = null,
val producedArtifactContent: Map<String, String> = emptyMap(),
)