fix(kernel): llm-emitted artifacts require stored content — close phantom path

emitLlmArtifacts previously emitted ArtifactCreated/Validating/Validated for every
llm-emitted slot unconditionally, fabricating "validated" artifacts when the model
produced no content. Gate now mirrors the F-015 file_written check: slots with null
or blank cached content are skipped with a warn log. Regression test added in
NeedsArtifactInjectionTest verifies no artifact events appear for a blank-response stage.
This commit is contained in:
2026-06-10 21:20:28 +04:00
parent 113f403f5a
commit fba2bbb17e
2 changed files with 73 additions and 0 deletions
@@ -1158,6 +1158,17 @@ abstract class SessionOrchestrator(
stageConfig.produces
.filter { it.kind.llmEmitted }
.forEach { slot ->
// Same rule as the file_written gate (F-015): no stored content means the
// model never produced this artifact — do not fabricate a validated one.
val cached = artifactContentCache["${sessionId.value}:${slot.name.value}"]
if (cached.isNullOrBlank()) {
log.warn(
"[Orchestrator] stage={} slot={}: no content stored — skipping artifact emission",
stageId.value,
slot.name.value,
)
return@forEach
}
emit(sessionId, ArtifactCreatedEvent(slot.name, sessionId, stageId, schemaVersion = 1))
emit(sessionId, ArtifactValidatingEvent(slot.name, sessionId, stageId))
emit(sessionId, ArtifactValidatedEvent(slot.name, sessionId, stageId))