Repository navigation
Expand file tree
/
Copy pathgraph.yaml
More file actions
128 lines (115 loc) · 4.09 KB
/
Copy pathgraph.yaml
File metadata and controls
128 lines (115 loc) · 4.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# The graph, as data.
#
# Routing lives here; routing *predicates* live in aeg/graph.py. A node reports
# an outcome from a closed vocabulary and the dispatcher looks up where that
# leads. Nodes never name their successor, so a confused node cannot send the
# run somewhere arbitrary -- and the picture of the graph can be generated from
# this file rather than maintained separately.
version: 1
entry: plan
defaults:
agent: claude_code
model: claude-opus-5
timeout_seconds: 900
# Retry ceilings are a property of the graph, not of the node that keeps
# failing. Without this, a test that can never pass loops until the money runs
# out. Enforced by the dispatcher in graph.route().
limits:
fix:
max_attempts: 3
on_exceeded: give_up
nodes:
plan:
impl: aeg.nodes.plan
kind: agentic
# A planner that can edit code will edit code.
tools:
allowed: [Read, Glob, Grep]
disallowed: [Write, Edit, MultiEdit, Bash, WebFetch, WebSearch]
# The human gate. Deterministic, and it does not block: it reads the decision
# file and reports one of waiting / approved / rejected. "waiting" parks the
# run -- the dispatcher exits and the run directory sits there until someone
# comes back to it. No daemon, no held process, no timeout to tune.
await_approval:
impl: aeg.nodes.await_approval
kind: deterministic
code:
impl: aeg.nodes.code
kind: agentic
tools:
allowed: [Read, Write, Edit, MultiEdit, Glob, Grep]
disallowed: [WebFetch, WebSearch]
# Deterministic. No model runs here: the test suite is the oracle, and its
# verdict has to mean the same thing every time.
test:
impl: aeg.nodes.test
kind: deterministic
timeout_seconds: 600
config:
command: ["{python}", "-m", "pytest", "-q"]
coverage_report: coverage.xml
# The second gate, and also deterministic. The test node answers "does it
# work"; this one answers "is it acceptable". Both answers must mean the same
# thing every time they are asked, which is why neither involves a model.
# Swap impl to aeg.nodes.review_sonar_mcp for the agent-backed reviewer that
# queries SonarQube through the MCP server. Nothing else changes: both write
# the same payload, so the edges below and the dispatcher stay untouched.
# That node is `kind: agentic` in spirit, but the verdict still comes from
# Sonar rather than from the model's judgement.
review:
impl: aeg.nodes.review_sonar_cli
kind: deterministic
timeout_seconds: 900
config:
scanner: tools/sonar-scanner-7.3.0.5189-windows-x64/bin/sonar-scanner.bat
host_url: http://localhost:9000
token_file: infra/.sonar-token
# {branch} is a placeholder so Step 7 can give every fanout candidate its
# own project key without candidates overwriting each other's analysis.
project_key: aeg-json-parser
qualitygate_timeout: 300
# Used only by the MCP implementation. localhost inside the container is
# the container, hence the host gateway alias.
mcp_server_name: sonarqube
mcp_image: mcp/sonarqube
sonarqube_url_from_container: http://host.docker.internal:9000
fix:
impl: aeg.nodes.fix
kind: agentic
tools:
allowed: [Read, Write, Edit, MultiEdit, Glob, Grep]
disallowed: [WebFetch, WebSearch]
write:
impl: aeg.nodes.write
kind: deterministic
# A real node rather than a bare marker, so that abandoning a run is recorded
# in the manifest and explains itself in out/.
give_up:
impl: aeg.nodes.give_up
kind: deterministic
END:
kind: terminal
edges:
plan:
done: await_approval
# "waiting" deliberately has no edge. It is not a transition -- it is the run
# stopping. The dispatcher parks before it ever consults the edge table.
await_approval:
approved: code
rejected: give_up
code:
done: test
test:
pass: review
fail: fix
# A failed gate routes to fix exactly like a failed test, and shares the same
# repair budget -- three attempts total, whatever mix of causes.
review:
pass: write
fail: fix
fix:
done: test
write:
done: END
give_up:
done: END