feat(workflow): sample review_report schema + implementer↔reviewer loop
This commit is contained in:
@@ -117,3 +117,7 @@ backend = "in_memory" # or "turbovec"
|
|||||||
# id = "review_report"
|
# id = "review_report"
|
||||||
# schema_path = "schemas/review_report.json"
|
# schema_path = "schemas/review_report.json"
|
||||||
# llm_emitted = true
|
# llm_emitted = true
|
||||||
|
#
|
||||||
|
# The bundled examples/workflows/review_loop.toml uses this kind: the reviewer emits a
|
||||||
|
# review_report whose `verdict` field gates the implementer↔reviewer loop via
|
||||||
|
# artifact_field_equals transitions. See docs/schemas/review_report.json for the schema.
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"verdict": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "approved | changes_requested"
|
||||||
|
},
|
||||||
|
"notes": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "reviewer rationale and requested changes"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["verdict"],
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
# Implementer ↔ reviewer refinement loop.
|
||||||
|
#
|
||||||
|
# The reviewer emits a validated `review_report` artifact carrying a `verdict` field.
|
||||||
|
# The loop exits to `done` when verdict == "approved" and loops back to `implement`
|
||||||
|
# otherwise (verdict != "approved"). The runtime refinement guard caps the
|
||||||
|
# review→implement cycle at `implement.max_retries` iterations and then escalates.
|
||||||
|
#
|
||||||
|
# Requires a `review_report` artifact kind declared in config:
|
||||||
|
# [[artifacts]]
|
||||||
|
# id = "review_report"
|
||||||
|
# schema_path = "schemas/review_report.json"
|
||||||
|
# llm_emitted = true
|
||||||
|
|
||||||
|
id = "review_loop"
|
||||||
|
start = "implement"
|
||||||
|
|
||||||
|
[[stages]]
|
||||||
|
id = "implement"
|
||||||
|
prompt = "prompts/implement.md"
|
||||||
|
produces = [{ name = "patch", kind = "file_written" }]
|
||||||
|
allowed_tools = ["file_write"]
|
||||||
|
token_budget = 8192
|
||||||
|
max_retries = 3
|
||||||
|
|
||||||
|
[[stages]]
|
||||||
|
id = "review"
|
||||||
|
prompt = "prompts/review.md"
|
||||||
|
needs = ["patch"]
|
||||||
|
produces = [{ name = "review_report", kind = "review_report" }]
|
||||||
|
token_budget = 8192
|
||||||
|
max_retries = 3
|
||||||
|
|
||||||
|
[[transitions]]
|
||||||
|
id = "implement-to-review"
|
||||||
|
from = "implement"
|
||||||
|
to = "review"
|
||||||
|
condition_type = "artifact_validated"
|
||||||
|
condition_artifact_id = "patch"
|
||||||
|
|
||||||
|
[[transitions]]
|
||||||
|
id = "review-approved"
|
||||||
|
from = "review"
|
||||||
|
to = "done"
|
||||||
|
condition_type = "artifact_field_equals"
|
||||||
|
condition_artifact_id = "review_report"
|
||||||
|
condition_field = "verdict"
|
||||||
|
condition_value = "approved"
|
||||||
|
condition_operator = "eq"
|
||||||
|
|
||||||
|
[[transitions]]
|
||||||
|
id = "review-changes-requested"
|
||||||
|
from = "review"
|
||||||
|
to = "implement"
|
||||||
|
condition_type = "artifact_field_equals"
|
||||||
|
condition_artifact_id = "review_report"
|
||||||
|
condition_field = "verdict"
|
||||||
|
condition_value = "approved"
|
||||||
|
condition_operator = "neq"
|
||||||
+96
@@ -0,0 +1,96 @@
|
|||||||
|
package com.correx.infrastructure.workflow
|
||||||
|
|
||||||
|
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.artifacts.kind.JsonSchemaProperty
|
||||||
|
import com.correx.core.events.types.StageId
|
||||||
|
import com.correx.core.transitions.conditions.ArtifactFieldEquals
|
||||||
|
import com.correx.core.transitions.conditions.FieldOperator
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import java.nio.file.Files
|
||||||
|
import kotlin.io.path.writeText
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class ReviewLoopWorkflowTest {
|
||||||
|
|
||||||
|
private val registry = DefaultArtifactKindRegistry().apply {
|
||||||
|
register(
|
||||||
|
ConfigArtifactKind(
|
||||||
|
id = "review_report",
|
||||||
|
schema = JsonSchema(
|
||||||
|
type = "object",
|
||||||
|
properties = mapOf("verdict" to JsonSchemaProperty(type = "string")),
|
||||||
|
required = listOf("verdict"),
|
||||||
|
additionalProperties = true,
|
||||||
|
),
|
||||||
|
llmEmitted = true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
private val loader = TomlWorkflowLoader(registry)
|
||||||
|
|
||||||
|
private val toml = """
|
||||||
|
id = "review_loop"
|
||||||
|
start = "implement"
|
||||||
|
|
||||||
|
[[stages]]
|
||||||
|
id = "implement"
|
||||||
|
prompt = "prompts/implement.md"
|
||||||
|
produces = [{ name = "patch", kind = "file_written" }]
|
||||||
|
allowed_tools = ["file_write"]
|
||||||
|
max_retries = 3
|
||||||
|
|
||||||
|
[[stages]]
|
||||||
|
id = "review"
|
||||||
|
prompt = "prompts/review.md"
|
||||||
|
needs = ["patch"]
|
||||||
|
produces = [{ name = "review_report", kind = "review_report" }]
|
||||||
|
max_retries = 3
|
||||||
|
|
||||||
|
[[transitions]]
|
||||||
|
id = "implement-to-review"
|
||||||
|
from = "implement"
|
||||||
|
to = "review"
|
||||||
|
condition_type = "artifact_validated"
|
||||||
|
condition_artifact_id = "patch"
|
||||||
|
|
||||||
|
[[transitions]]
|
||||||
|
id = "review-approved"
|
||||||
|
from = "review"
|
||||||
|
to = "done"
|
||||||
|
condition_type = "artifact_field_equals"
|
||||||
|
condition_artifact_id = "review_report"
|
||||||
|
condition_field = "verdict"
|
||||||
|
condition_value = "approved"
|
||||||
|
condition_operator = "eq"
|
||||||
|
|
||||||
|
[[transitions]]
|
||||||
|
id = "review-changes-requested"
|
||||||
|
from = "review"
|
||||||
|
to = "implement"
|
||||||
|
condition_type = "artifact_field_equals"
|
||||||
|
condition_artifact_id = "review_report"
|
||||||
|
condition_field = "verdict"
|
||||||
|
condition_value = "approved"
|
||||||
|
condition_operator = "neq"
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `review loop parses with verdict-gated edges`() {
|
||||||
|
val path = Files.createTempFile("review_loop", ".toml").also { it.writeText(toml) }
|
||||||
|
val graph = loader.load(path)
|
||||||
|
|
||||||
|
val review = graph.stages[StageId("review")]!!
|
||||||
|
assertTrue(review.produces.any { it.name.value == "review_report" && it.kind.llmEmitted })
|
||||||
|
|
||||||
|
val approved = graph.transitions.first { it.to == StageId("done") }.condition as ArtifactFieldEquals
|
||||||
|
assertEquals("verdict", approved.field)
|
||||||
|
assertEquals("approved", approved.value)
|
||||||
|
assertEquals(FieldOperator.EQ, approved.operator)
|
||||||
|
|
||||||
|
val changes = graph.transitions.first { it.to == StageId("implement") }.condition as ArtifactFieldEquals
|
||||||
|
assertEquals(FieldOperator.NEQ, changes.operator)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user