Files
coyote/assets/agents/review-gauntlet/graph.yaml
T

360 lines
11 KiB
YAML

name: review-gauntlet
description: |
Graph-based post-implementation review gate — "the done gate". Given a diff
spec and the work's context (plan/acceptance criteria, rigor, security
posture), it deterministically computes diff signals, selects the applicable
independent review lanes — code-reviewer (always), adversary (when a
spec/plan exists), security-reviewer (attack-surface signals), probe
(consumer-facing surface with a local-run recipe) — runs the selected lanes
IN PARALLEL as isolated sub-agents, and machine-parses their verdict
sentinels into one deterministic verdict: GAUNTLET: PASS or
GAUNTLET: BLOCKED. An orchestrator that must not claim "done" without green
reviews spawns this once instead of judging trigger rules and parsing
verdicts itself — the graph structure makes skipping a lane or misreading a
verdict impossible. Invariants: lane selection can only be WIDENED by LLM
judgment, never narrowed (deterministic rules are the floor); a missing
verdict sentinel is a lane failure, never a pass; probe INCONCLUSIVE blocks
with an environment note (it is never PASS or FAIL); reviewer lanes run
verdict-independent — no cross-lane messaging.
version: '1.0'
max_agent_depth: 4
max_concurrent_agents: 12
variables:
- name: project_dir
description: Repository the change lives in; signals and every review lane run against it.
default: '.'
settings:
max_loop_iterations: 10
log_state_snapshots: true
timeout: 10800
initial_state:
diff_spec: worktree
plan_context: ''
rigor: production
security_posture: standard
probe_context: ''
extra_context: ''
forced_lanes: []
changed_files: []
file_count: 0
signals_summary: ''
signals_error: ''
touches_auth: false
touches_deps: false
touches_exec: false
touches_proto: false
touches_migrations: false
consumer_surface: false
docs_only: false
add_code_review: false
add_adversary: false
add_security: false
add_probe: false
lane_reasons: ''
code_review_items: []
adversary_items: []
security_items: []
probe_items: []
code_review_results: []
adversary_results: []
security_results: []
probe_results: []
lanes_summary: ''
gauntlet_verdict: ''
gauntlet_report: ''
start: parse
nodes:
parse:
id: parse
type: llm
description: Extract the review request's facts from the spawn prompt. Extraction only — no judging, no lane selection.
instructions: |
You extract the facts of a review request into structured fields.
EXTRACTION ONLY — do not decide which review lanes should run.
- `diff_spec`: the git diff specification the caller named — a ref
range ("main...HEAD", "HEAD~3..HEAD"), a single ref, or the literal
"worktree" for uncommitted changes against HEAD. When the caller
did not name one, use "worktree".
- `project_dir_in`: the repository directory the caller named as holding
the change (absolute path preferred); empty string when the caller
named none.
- `plan_context`: the plan / spec / acceptance-criteria text the
implementation must satisfy, verbatim (or a file path the caller
pointed at). Empty string when the caller supplied none.
- `rigor`: poc | prototype | production. Default "production" when
not stated.
- `security_posture`: prototype | standard | hardened. Default
"standard" when not stated.
- `probe_context`: the local-run recipe and/or existing usage-suite
pointers for black-box probing (how to boot the service, where the
suites live). Empty string when the caller supplied none.
- `forced_lanes`: lanes the caller explicitly demanded (verbatim
names). Empty array = automatic selection.
- `extra_context`: any remaining caller context reviewers should see,
condensed.
prompt: |
Extract the review-request facts from this request:
{{initial_prompt}}
tools: []
output_schema:
type: object
properties:
diff_spec:
type: string
plan_context:
type: string
rigor:
type: string
security_posture:
type: string
probe_context:
type: string
forced_lanes:
type: array
items:
type: string
extra_context:
type: string
project_dir_in:
type: string
required: [diff_spec, plan_context, rigor, security_posture, probe_context, forced_lanes, extra_context, project_dir_in]
max_iterations: 3
max_attempts: 2
next: signals
signals:
id: signals
type: script
description: Deterministic diff signals — file list, attack-surface and consumer-surface indicators. Pure git; no LLM judgment.
script: scripts/signals.py
timeout: 120
next: select_lanes
select_lanes:
id: select_lanes
type: llm
description: Additive-only lane judgment on top of the deterministic rules. It can WIDEN the gauntlet, never narrow it.
instructions: |
You look at a change's deterministic diff signals and decide whether any
review lane the hard rules might have missed should ALSO run. You can
only ADD lanes — the deterministic floor (enforced downstream, not by
you) is: code-review always runs; adversary runs when a plan/spec is
present; security runs on attack-surface signals or hardened posture;
probe runs when consumer-facing surface changed and a local-run recipe
exists. Setting a flag to false NEVER removes a lane the rules selected.
Reasons to add:
- `add_security`: the diff handles external/untrusted input, touches
crypto/secrets handling, or changes trust boundaries in ways the
path-based signals missed.
- `add_adversary`: the request references behavior it must preserve or
criteria scattered in prose that amount to a spec, even without a
formal plan.
- `add_probe`: the change alters observable API/CLI behavior even
though the path signals missed it (only useful when a local-run
recipe exists — the builder skips it otherwise, noting why).
- `add_code_review`: always selected by the rules; flag exists only
for forced-lane configurations.
Put one line per added lane into `lane_reasons`. When nothing needs
adding, all flags false and empty reasons is the correct answer.
prompt: |
Change signals (deterministic):
- diff spec: {{diff_spec}}
- files changed: {{file_count}}
- changed files (first 200): {{changed_files}}
- touches auth paths/content: {{touches_auth}}
- touches dependency manifests: {{touches_deps}}
- adds exec/shell/subprocess calls: {{touches_exec}}
- touches protobuf/contracts: {{touches_proto}}
- touches migrations: {{touches_migrations}}
- consumer-facing surface paths: {{consumer_surface}}
- docs-only diff: {{docs_only}}
- {{signals_summary}}
{{signals_error}}
Caller context:
- rigor: {{rigor}}; security posture: {{security_posture}}
- plan/spec present: {{plan_context}}
- probe recipe present: {{probe_context}}
- extra: {{extra_context}}
tools: []
output_schema:
type: object
properties:
add_code_review:
type: boolean
add_adversary:
type: boolean
add_security:
type: boolean
add_probe:
type: boolean
lane_reasons:
type: string
required: [add_code_review, add_adversary, add_security, add_probe, lane_reasons]
max_iterations: 3
max_attempts: 2
next: build_items
build_items:
id: build_items
type: script
description: Deterministic lane builder — hard rules are the floor, LLM flags may only add, forced lanes override exactly. Emits one 0-or-1-item list per lane; an empty list skips that lane's map.
script: scripts/build_items.py
timeout: 30
next: [map_code_review, map_adversary, map_security, map_probe]
map_code_review:
id: map_code_review
type: map
over: '{{code_review_items}}'
as: lane_ctx
branch: run_code_review
output_key: lane_out
collect_into: code_review_results
max_concurrency: 1
next: verdict_gate
map_adversary:
id: map_adversary
type: map
over: '{{adversary_items}}'
as: lane_ctx
branch: run_adversary
output_key: lane_out
collect_into: adversary_results
max_concurrency: 1
next: verdict_gate
map_security:
id: map_security
type: map
over: '{{security_items}}'
as: lane_ctx
branch: run_security
output_key: lane_out
collect_into: security_results
max_concurrency: 1
next: verdict_gate
map_probe:
id: map_probe
type: map
over: '{{probe_items}}'
as: lane_ctx
branch: run_probe
output_key: lane_out
collect_into: probe_results
max_concurrency: 1
next: verdict_gate
run_code_review:
id: run_code_review
type: agent
description: Independent code-quality review lane.
agent: code-reviewer
timeout: 7200
prompt: |
Review the code changes in {{project_dir}} (diff: {{diff_spec}}) as an
independent post-implementation review.
Quality bar: rigor={{rigor}}.
Plan / acceptance-criteria context (may be empty):
{{plan_context}}
{{extra_context}}
Close your report with your Review Verdict line.
max_attempts: 2
state_updates:
lane_out: '{{output}}'
run_adversary:
id: run_adversary
type: agent
description: Plan-conformance review lane.
agent: adversary
timeout: 7200
prompt: |
Adversarial plan-conformance review in {{project_dir}}.
The implementation to check: diff {{diff_spec}}.
The spec / plan / acceptance criteria it must satisfy:
{{plan_context}}
{{extra_context}}
Return your ADVERSARIAL_REVIEW verdict.
max_attempts: 2
state_updates:
lane_out: '{{output}}'
run_security:
id: run_security
type: agent
description: Security review lane.
agent: security-reviewer
timeout: 7200
prompt: |
Security review of the code changes in {{project_dir}} (diff: {{diff_spec}}).
Security posture: {{security_posture}}.
{{extra_context}}
Return your SECURITY_REVIEW verdict.
max_attempts: 2
state_updates:
lane_out: '{{output}}'
run_probe:
id: run_probe
type: agent
description: Black-box usage-pattern verification lane.
agent: probe
timeout: 7200
prompt: |
Usage-pattern verification (black-box probe) of the changes in
{{project_dir}} (diff: {{diff_spec}}).
Local-run recipe / existing usage suites:
{{probe_context}}
Plan / acceptance-criteria context:
{{plan_context}}
Derive expected behaviors from the plan/contract BEFORE reading
implementation code, run existing suites first, then author spec-first
tests for the uncovered delta. Return your USAGE_PROBE verdict.
max_attempts: 2
state_updates:
lane_out: '{{output}}'
verdict_gate:
id: verdict_gate
type: script
description: Deterministic sentinel parser and gate. A missing sentinel is a lane failure, never a pass; probe INCONCLUSIVE blocks with an environment note; 🔴 findings block regardless of the lane's own verdict line.
script: scripts/verdict_gate.py
timeout: 60
next: done
done:
id: done
type: end
output: |
GAUNTLET: {{gauntlet_verdict}}
{{gauntlet_report}}