feat(workflow): execution_plan artifact kind + schema + architect prompt
Add docs/schemas/execution_plan.json (goal/stages/edges), the architect_freestyle prompt that instructs emitting the execution pipeline as JSON, and register the execution_plan llm-emitted kind in both artifacts.config.toml and sample-config.toml. Validated via ExecutionPlanSchemaValidationTest (minimal plan passes; missing stages rejected).
This commit is contained in:
+42
@@ -0,0 +1,42 @@
|
|||||||
|
package com.correx.core.validation.artifact
|
||||||
|
|
||||||
|
import com.correx.core.artifacts.kind.JsonSchema
|
||||||
|
import com.correx.core.artifacts.kind.JsonSchemaProperty
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
class ExecutionPlanSchemaValidationTest {
|
||||||
|
|
||||||
|
private val schema = JsonSchema(
|
||||||
|
type = "object",
|
||||||
|
properties = mapOf(
|
||||||
|
"goal" to JsonSchemaProperty(type = "string"),
|
||||||
|
"stages" to JsonSchemaProperty(type = "array"),
|
||||||
|
"edges" to JsonSchemaProperty(type = "array"),
|
||||||
|
),
|
||||||
|
required = listOf("goal", "stages", "edges"),
|
||||||
|
additionalProperties = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun errors(json: String) =
|
||||||
|
JsonSchemaValidator.validate(schema, Json.parseToJsonElement(json))
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `minimal valid execution plan passes`() {
|
||||||
|
val plan = """
|
||||||
|
{
|
||||||
|
"goal": "g",
|
||||||
|
"stages": [{"id": "s", "prompt": "p", "produces": "x"}],
|
||||||
|
"edges": []
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
assertTrue(errors(plan).isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `plan missing stages is rejected`() {
|
||||||
|
val plan = """{"goal": "g", "edges": []}"""
|
||||||
|
assertTrue(errors(plan).any { it.contains("stages") })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -134,3 +134,8 @@ backend = "in_memory" # or "turbovec"
|
|||||||
# The bundled examples/workflows/review_loop.toml uses this kind: the reviewer emits a
|
# 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
|
# review_report whose `verdict` field gates the implementer↔reviewer loop via
|
||||||
# artifact_field_equals transitions. See docs/schemas/review_report.json for the schema.
|
# artifact_field_equals transitions. See docs/schemas/review_report.json for the schema.
|
||||||
|
|
||||||
|
[[artifacts]]
|
||||||
|
id = "execution_plan"
|
||||||
|
schema_path = "schemas/execution_plan.json"
|
||||||
|
llm_emitted = true
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"goal": { "type": "string" },
|
||||||
|
"stages": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "string" },
|
||||||
|
"role": { "type": "string" },
|
||||||
|
"prompt": { "type": "string" },
|
||||||
|
"needs": { "type": "array", "items": { "type": "string" } },
|
||||||
|
"produces": { "type": "string" },
|
||||||
|
"tools": { "type": "array", "items": { "type": "string" } }
|
||||||
|
},
|
||||||
|
"required": ["id", "prompt", "produces"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"edges": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"from": { "type": "string" },
|
||||||
|
"to": { "type": "string" },
|
||||||
|
"condition": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": { "type": "string" },
|
||||||
|
"artifact_id": { "type": "string" },
|
||||||
|
"field": { "type": "string" },
|
||||||
|
"value": { "type": "string" },
|
||||||
|
"operator": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["type"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["from", "to", "condition"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["goal", "stages", "edges"],
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
||||||
@@ -28,3 +28,8 @@ llm_emitted = true
|
|||||||
id = "review_report"
|
id = "review_report"
|
||||||
schema_path = "schemas/review_report.json"
|
schema_path = "schemas/review_report.json"
|
||||||
llm_emitted = true
|
llm_emitted = true
|
||||||
|
|
||||||
|
[[artifacts]]
|
||||||
|
id = "execution_plan"
|
||||||
|
schema_path = "schemas/execution_plan.json"
|
||||||
|
llm_emitted = true
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# Architect — Freestyle Execution Plan
|
||||||
|
|
||||||
|
You are the architect stage in a freestyle workflow. Your only output is a single JSON
|
||||||
|
`execution_plan` artifact. Do not write prose design documents; emit the plan and nothing
|
||||||
|
else.
|
||||||
|
|
||||||
|
## Inputs available to you
|
||||||
|
|
||||||
|
- `analysis` artifact — structured findings from the analyst stage (goal, constraints,
|
||||||
|
risks, open questions).
|
||||||
|
- Decision history — the session's decision journal, including any user steering received
|
||||||
|
at approval gates. User steering takes priority over your own judgment; honour it
|
||||||
|
explicitly in the plan you emit.
|
||||||
|
|
||||||
|
## What to emit
|
||||||
|
|
||||||
|
Emit a JSON object that validates against the `execution_plan` schema:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"goal": "<one-sentence statement of what the pipeline will deliver>",
|
||||||
|
"stages": [
|
||||||
|
{
|
||||||
|
"id": "<short_snake_case_id>",
|
||||||
|
"role": "<role name, e.g. implementer>",
|
||||||
|
"prompt": "<the full prompt that stage will execute>",
|
||||||
|
"produces": "<artifact_id this stage emits>",
|
||||||
|
"needs": ["<artifact_id consumed from an earlier stage>"],
|
||||||
|
"tools": ["file_read", "file_write", "file_edit", "ShellTool"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"from": "<stage_id or 'done'>",
|
||||||
|
"to": "<stage_id or 'done'>",
|
||||||
|
"condition": {
|
||||||
|
"type": "artifact_validated",
|
||||||
|
"artifact_id": "<produces id of the from-stage>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
**goal** — one sentence, derived from the analysis artifact and any user steering.
|
||||||
|
|
||||||
|
**stages** — ordered list; each stage must:
|
||||||
|
- Have a unique `id` in `snake_case`.
|
||||||
|
- Declare `produces`: the artifact id this stage emits. Artifact ids must be declared in
|
||||||
|
your config's `[[artifacts]]` table.
|
||||||
|
- Declare `needs`: every upstream artifact id the stage's prompt references. Every id in
|
||||||
|
`needs` must be `produces`d by a strictly earlier stage.
|
||||||
|
- Include `tools` only for stages that write or edit files:
|
||||||
|
`["file_read", "file_write", "file_edit", "ShellTool"]`. Do not invent tool names
|
||||||
|
beyond this set.
|
||||||
|
- Keep stages small and single-responsibility. Prefer more stages over large monolithic
|
||||||
|
prompts.
|
||||||
|
|
||||||
|
**edges** — describe every transition between stages. Rules:
|
||||||
|
- `from` and `to` must each be a declared stage `id` or the literal string `"done"`.
|
||||||
|
- The normal forward edge uses `"type": "artifact_validated"` with `artifact_id` set to
|
||||||
|
the `produces` id of the `from` stage.
|
||||||
|
- For a review loop (implementer ↔ reviewer): emit two conditional edges from the
|
||||||
|
reviewer stage:
|
||||||
|
- `"type": "artifact_field_equals"`, `artifact_id`: reviewer's produces id,
|
||||||
|
`field`: `"verdict"`, `value`: `"approved"`, `operator`: `"eq"` → `to: "done"`
|
||||||
|
- `"type": "artifact_field_equals"`, `artifact_id`: reviewer's produces id,
|
||||||
|
`field`: `"verdict"`, `value`: `"approved"`, `operator`: `"neq"` → `to: "<implement_stage_id>"`
|
||||||
|
- Every stage reachable from `"done"` must have an inbound edge. Every non-terminal stage
|
||||||
|
must have an outbound edge.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- Do not add stages, roles, or tools not justified by the analysis artifact.
|
||||||
|
- Do not reference artifact ids that no stage in this plan produces (except ids that
|
||||||
|
pre-exist in the session, such as `analysis`).
|
||||||
|
- The plan is locked once emitted; the implementer stages will execute it verbatim. Be
|
||||||
|
precise in each stage's `prompt`.
|
||||||
Reference in New Issue
Block a user