3e31ebcc1e
Explicit per-stage grants (least-privilege, invariant #5) for the mounted codebase-memory MCP side-car: - role_pipeline: discovery bootstraps the graph (index_repository) + grounds; analyst/architect/decomposer/implementer/reviewer get read-only query tools (search_code/search_graph/get_architecture/trace_path/get_code_snippet) scoped to each role. NOTE: architect flips from pure-reasoning to tool-calling. - review_loop implement+review and task_planning planner get read-only queries. Tools are approval-gated at T2 (server default in config.toml [[mcp]]). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
204 lines
7.6 KiB
TOML
204 lines
7.6 KiB
TOML
# Role pipeline: analyst → architect → decomposer → ⟲(claim → implement → review)⟲ → done
|
|
#
|
|
# The architect's design is broken into a task graph by the decomposer, then a single loop
|
|
# drains those tasks one at a time. Each implementer entry deterministically CLAIMS the next
|
|
# ready task (claim_task = true) — the kernel picks it, not the LLM — and injects that task's
|
|
# acceptance criteria + blockers as L0 context. While a task is claimed, file writes are
|
|
# auto-scoped to its affected_paths; the implementer widens scope only via propose_scope
|
|
# (operator-approved). The loop is gated by the tasks_ready predicate, so it exits when the
|
|
# board is drained.
|
|
#
|
|
# Loop control depends on transition ORDERING. The resolver evaluates a stage's outgoing
|
|
# transitions sorted by transition id and takes the first whose condition holds, so the
|
|
# reviewer edges are named review-1/2/3 to force this precedence:
|
|
# 1. review-1-changes (verdict ≠ approved) → implementer (fix the current task)
|
|
# 2. review-2-more (tasks_ready) → implementer (approved: claim next)
|
|
# 3. review-3-done (always_true, fallthrough) → done (approved + board empty)
|
|
# This avoids needing composite (all_of/not) conditions in the flat TOML schema.
|
|
#
|
|
# Requires these artifact kinds in ~/.config/correx/config.toml (schemas under docs/schemas/):
|
|
# [[artifacts]]
|
|
# id = "discovery"; schema_path = "schemas/discovery.json"; llm_emitted = true
|
|
# [[artifacts]]
|
|
# id = "analysis"; schema_path = "schemas/analysis.json"; llm_emitted = true
|
|
# [[artifacts]]
|
|
# id = "design"; schema_path = "schemas/design.json"; llm_emitted = true
|
|
# [[artifacts]]
|
|
# id = "review_report"; schema_path = "schemas/review_report.json"; llm_emitted = true
|
|
#
|
|
# Prompt files (prompts/*.md, relative to this workflow) must exist for a real run.
|
|
|
|
id = "role_pipeline"
|
|
start = "discovery"
|
|
|
|
# 0. Vet the request against the real repo before any analysis: park on a batched list of
|
|
# operator questions (underspecification or a claim the code contradicts) or clear it to
|
|
# proceed. Read-only; owns clarification exclusively so the analyst always emits an analysis.
|
|
[[stages]]
|
|
id = "discovery"
|
|
prompt = "prompts/discovery.md"
|
|
produces = [{ name = "discovery", kind = "discovery" }]
|
|
# Bootstraps the code-intelligence graph (index_repository) so downstream stages can query it,
|
|
# plus grounds vetting against the real architecture. mcp__codebase-memory__* come from the
|
|
# [[mcp]] side-car in config.toml.
|
|
allowed_tools = [
|
|
"file_read", "list_dir", "shell",
|
|
"mcp__codebase-memory__index_repository",
|
|
"mcp__codebase-memory__search_code",
|
|
"mcp__codebase-memory__get_architecture",
|
|
]
|
|
token_budget = 16384
|
|
max_retries = 2
|
|
|
|
# 1. Understand the request and the relevant code. Read-only.
|
|
# ground_references: every workspace-relative file path the analysis names is checked
|
|
# for existence; a hallucinated path fails the stage and retries with the misses fed back.
|
|
[[stages]]
|
|
id = "analyst"
|
|
prompt = "prompts/analyst.md"
|
|
produces = [{ name = "analysis", kind = "analysis" }]
|
|
allowed_tools = [
|
|
"file_read", "shell", "task_search", "task_context", "task_create",
|
|
"mcp__codebase-memory__search_code",
|
|
"mcp__codebase-memory__search_graph",
|
|
"mcp__codebase-memory__get_architecture",
|
|
"mcp__codebase-memory__trace_path",
|
|
"mcp__codebase-memory__get_code_snippet",
|
|
]
|
|
ground_references = true
|
|
token_budget = 16384
|
|
max_retries = 2
|
|
|
|
# 2. Decide the approach and component boundaries.
|
|
[[stages]]
|
|
id = "architect"
|
|
prompt = "prompts/architect.md"
|
|
needs = ["analysis"]
|
|
produces = [{ name = "design", kind = "design" }]
|
|
# Read-only code intelligence to decide component boundaries against the real call graph.
|
|
# NOTE: this flips the architect from a pure-reasoning stage into a tool-calling one.
|
|
allowed_tools = [
|
|
"mcp__codebase-memory__get_architecture",
|
|
"mcp__codebase-memory__search_graph",
|
|
"mcp__codebase-memory__trace_path",
|
|
]
|
|
token_budget = 16384
|
|
max_retries = 2
|
|
|
|
# 3. Break the design into a task graph via task_decompose. require_task_decompose is a
|
|
# deterministic kernel gate: stage_complete is blocked until task_decompose (or task_create
|
|
# for a trivial single-task request) returns. Each child carries acceptance_criteria and
|
|
# affected_paths — those drive the per-task L0 context and the per-task write scope below.
|
|
[[stages]]
|
|
id = "decomposer"
|
|
prompt = "prompts/task_planner.md"
|
|
needs = ["design"]
|
|
allowed_tools = [
|
|
"file_read", "shell", "task_search", "task_context", "task_decompose", "task_create",
|
|
"mcp__codebase-memory__search_code",
|
|
"mcp__codebase-memory__search_graph",
|
|
]
|
|
require_task_decompose = true
|
|
token_budget = 16384
|
|
max_retries = 2
|
|
|
|
# 4. Implement one task. claim_task = true: on entry the kernel claims the next ready task
|
|
# (or re-surfaces the one already claimed on a review bounce) and injects its acceptance
|
|
# criteria as L0. Writes are auto-jailed to the claimed task's affected_paths; propose_scope
|
|
# is the operator-approved valve to widen that scope mid-task.
|
|
[[stages]]
|
|
id = "implementer"
|
|
prompt = "prompts/implementer.md"
|
|
produces = [{ name = "patch", kind = "file_written" }]
|
|
claim_task = true
|
|
allowed_tools = [
|
|
"file_read", "file_write", "file_edit", "shell", "propose_scope",
|
|
"task_create", "task_update", "task_context", "task_search",
|
|
"mcp__codebase-memory__search_code",
|
|
"mcp__codebase-memory__get_code_snippet",
|
|
"mcp__codebase-memory__trace_path",
|
|
]
|
|
token_budget = 32768
|
|
max_retries = 3
|
|
|
|
# 5. Review the patch against the claimed task's acceptance criteria and the analyst's brief.
|
|
# On approval the reviewer marks the task done via task_update — that drops it from the
|
|
# active claim so the next implementer entry claims a fresh task and the loop advances.
|
|
[[stages]]
|
|
id = "reviewer"
|
|
prompt = "prompts/reviewer.md"
|
|
needs = ["patch", "analysis"]
|
|
produces = [{ name = "review_report", kind = "review_report" }]
|
|
allowed_tools = [
|
|
"task_context", "task_update",
|
|
"mcp__codebase-memory__search_code",
|
|
"mcp__codebase-memory__trace_path",
|
|
"mcp__codebase-memory__get_code_snippet",
|
|
]
|
|
token_budget = 32768
|
|
max_retries = 2
|
|
|
|
# --- forward edges ---
|
|
|
|
[[transitions]]
|
|
id = "discovery-to-analyst"
|
|
from = "discovery"
|
|
to = "analyst"
|
|
condition_type = "artifact_validated"
|
|
condition_artifact_id = "discovery"
|
|
|
|
[[transitions]]
|
|
id = "analyst-to-architect"
|
|
from = "analyst"
|
|
to = "architect"
|
|
condition_type = "artifact_validated"
|
|
condition_artifact_id = "analysis"
|
|
|
|
[[transitions]]
|
|
id = "architect-to-decomposer"
|
|
from = "architect"
|
|
to = "decomposer"
|
|
condition_type = "artifact_validated"
|
|
condition_artifact_id = "design"
|
|
|
|
# Only proceed to implementation once the decomposer actually produced ready tasks.
|
|
[[transitions]]
|
|
id = "decomposer-to-implementer"
|
|
from = "decomposer"
|
|
to = "implementer"
|
|
condition_type = "tasks_ready"
|
|
|
|
[[transitions]]
|
|
id = "implementer-to-reviewer"
|
|
from = "implementer"
|
|
to = "reviewer"
|
|
condition_type = "artifact_validated"
|
|
condition_artifact_id = "patch"
|
|
|
|
# --- verdict + task-board gated loop (id order = evaluation precedence, see header) ---
|
|
|
|
# 1. Changes requested → keep refining the current (still-claimed) task.
|
|
[[transitions]]
|
|
id = "review-1-changes"
|
|
from = "reviewer"
|
|
to = "implementer"
|
|
condition_type = "artifact_field_equals"
|
|
condition_artifact_id = "review_report"
|
|
condition_field = "verdict"
|
|
condition_value = "approved"
|
|
condition_operator = "neq"
|
|
|
|
# 2. Approved and tasks remain → claim the next one.
|
|
[[transitions]]
|
|
id = "review-2-more"
|
|
from = "reviewer"
|
|
to = "implementer"
|
|
condition_type = "tasks_ready"
|
|
|
|
# 3. Approved and the board is drained → done (fallthrough).
|
|
[[transitions]]
|
|
id = "review-3-done"
|
|
from = "reviewer"
|
|
to = "done"
|
|
condition_type = "always_true"
|