feat(kernel): brief echo-back gate (plan-pipeline-addenda A1)
New brief_echo stage before the planner in role_pipeline: the model restates the analyst brief as a structured artifact, and a deterministic gate (checkBriefEcho, opt-in via brief_echo=true) diffs the restatement against the original brief. On divergence — dropped requirements (Jaccard coverage < 0.34) or hallucinated files — it emits BriefEchoMismatchEvent and fails the stage retryably, so a misread brief never reaches plan generation. The diff (BriefEchoDiff) is a pure function of two recorded artifacts, so it is replay-safe and records no observation; the event fires only on mismatch, for audit. Symbols are recorded but non-blocking in v1. Mirrors the groundBrief- References gate. role_pipeline only; freestyle_planning wiring is a follow-up. Runtime install (like research): copy brief_echo.json + brief_echo.md and the [[artifacts]] block into ~/.config/correx.
This commit is contained in:
+2
@@ -47,6 +47,7 @@ private data class StageSection(
|
||||
@param:JsonProperty("requires_approval") val requiresApproval: Boolean = false,
|
||||
@param:JsonProperty("inject_artifact_kinds") val injectArtifactKinds: Boolean = false,
|
||||
@param:JsonProperty("ground_references") val groundReferences: Boolean = false,
|
||||
@param:JsonProperty("brief_echo") val briefEcho: Boolean = false,
|
||||
)
|
||||
|
||||
// condition fields flattened into the transition row
|
||||
@@ -121,6 +122,7 @@ class TomlWorkflowLoader(
|
||||
if (s.requiresApproval) put("requiresApproval", "true")
|
||||
if (s.injectArtifactKinds) put("injectArtifactKinds", "true")
|
||||
if (s.groundReferences) put("groundReferences", "true")
|
||||
if (s.briefEcho) put("briefEcho", "true")
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
+3
-2
@@ -29,6 +29,7 @@ class RolePipelineWorkflowTest {
|
||||
private val registry = DefaultArtifactKindRegistry().apply {
|
||||
register(llmKind("analysis"))
|
||||
register(llmKind("design"))
|
||||
register(llmKind("brief_echo"))
|
||||
register(llmKind("impl_plan"))
|
||||
register(llmKind("review_report"))
|
||||
}
|
||||
@@ -53,10 +54,10 @@ class RolePipelineWorkflowTest {
|
||||
val graph = TomlWorkflowLoader(registry).load(path!!)
|
||||
|
||||
assertEquals(
|
||||
setOf("analyst", "architect", "planner", "implementer", "reviewer"),
|
||||
setOf("analyst", "architect", "brief_echo", "planner", "implementer", "reviewer"),
|
||||
graph.stages.keys.map { it.value }.toSet(),
|
||||
)
|
||||
assertEquals(6, graph.transitions.size)
|
||||
assertEquals(7, graph.transitions.size)
|
||||
|
||||
// Forward chaining via needs.
|
||||
assertTrue(graph.stages[StageId("reviewer")]!!.needs.map { it.value }.containsAll(listOf("patch", "impl_plan")))
|
||||
|
||||
Reference in New Issue
Block a user