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"
|
||||
# schema_path = "schemas/review_report.json"
|
||||
# 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