Skip to content

Commit 9849e07

Browse files
committed
docs(orchestrator): add Handlers design & usage guide; link from README
1 parent 10bf27d commit 9849e07

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

orchestrator/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ Notes
7272
- Guardrails: diff budgets enforced in `adapters/git.py`.
7373
- Optional streaming logs: add `stream_logs: true` to an Odoo step to collect a follow
7474
log tail in artifacts.
75+
- Handlers: the engine runs steps via pluggable handlers (no hardcoded nodes). See
76+
`orchestrator/docs/HANDLERS.md` for details.
7577

7678
Configuration Reference
7779

orchestrator/docs/HANDLERS.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# Orchestrator Handlers (Design & Usage)
2+
3+
This document explains how the orchestrator executes recipe steps via pluggable handlers
4+
(no hardcoded step logic), how legacy `uses:` steps are mapped to handlers, what a
5+
handler looks like, and how to validate a recipe.
6+
7+
## Why handlers?
8+
9+
- Decouple the engine (graph, checkpoints, interrupts, guardrails) from side‑effects
10+
(git, GitHub, Odoo, Task Registry, MCP tools).
11+
- Make it easy to add new step types without touching the engine.
12+
- Validate step arguments per handler before execution.
13+
14+
## Built‑in handlers
15+
16+
- `git.apply` — checkout/commit with diff guardrails
17+
- `github.open_pr` — open a PR using `gh` (REST fallback TBD)
18+
- `odoo.call` — call a tool on the Odoo MCP server (adapter safety still applies)
19+
- `tm.log_decision` — add a decision note to the current task
20+
- `tm.set_status` — update the current task status
21+
- `agent.wait` — write GOAL.md and interrupt to wait for human
22+
- `mcp.call` — generic MCP caller (server/tool/arguments)
23+
24+
## Mapping legacy `uses:` to handlers
25+
26+
You can keep your existing recipes. The engine maps legacy steps to handlers:
27+
28+
- `uses: git``git.apply` (fields: `branch`, `commit`)
29+
- `uses: github` + `pr:``github.open_pr` (fields in `pr`)
30+
- `uses: odoo``odoo.call` (fields: `tool`, `args`)
31+
- `uses: tm` (`decision:` | `status:`) → `tm.log_decision` | `tm.set_status`
32+
- `uses: agent``agent.wait`
33+
- `uses: mcp``mcp.call` (fields: `server`, `tool`, `arguments`)
34+
35+
You can also call a handler explicitly later (future), but legacy mapping is enough for
36+
all current recipes.
37+
38+
## Handler interface (for reference)
39+
40+
```
41+
# orchestrator/engine/handlers/base.py
42+
class NodeHandler:
43+
name: str
44+
def schema(self) -> dict: ... # JSON Schema for arguments (optional)
45+
def execute(self, *, args: dict, ctx: dict, **kw) -> ExecResult: ...
46+
47+
class ExecResult:
48+
ok: bool
49+
output: dict
50+
idempotency_key: Optional[str]
51+
52+
class NodeInterrupt(Exception):
53+
reason: str
54+
payload: dict
55+
```
56+
57+
- The engine provides adapters/clients via `**kw` when calling `execute` (e.g. `git`,
58+
`github`, `odoo`, `tm`, `mcp_clients`, and the `logger`).
59+
- A handler can raise `NodeInterrupt` to yield control (the engine records a checkpoint
60+
and returns `status=interrupted`).
61+
62+
## Validation
63+
64+
Handlers can expose a lightweight JSON Schema for their arguments. The doctor command
65+
uses it to validate recipes:
66+
67+
```
68+
python -m orchestrator.main doctor validate-recipe --recipe <name>
69+
```
70+
71+
This resolves each legacy step to a handler and checks the arguments against the
72+
handler’s schema so you catch mistakes early.
73+
74+
## Return to agent on failure (policy)
75+
76+
Any step can bounce control back to an agent when it fails:
77+
78+
```
79+
steps:
80+
- id: agent
81+
uses: agent
82+
goal: "Please implement X"
83+
- id: tests
84+
uses: odoo
85+
tool: test_addons
86+
args: { modules: ["{{ env.module }}"], dbname: "{{ env.db }}" }
87+
on_error:
88+
return_to_agent: agent # required
89+
tm_status: review # optional convenience
90+
```
91+
92+
On failure, the engine marks the failing node failed, resets the target agent node in
93+
checkpoints, and returns `status=interrupted` at that agent. Fix issues, then resume:
94+
95+
```
96+
python -m orchestrator.main run_graph --task-id T1 --recipe R --resume <run_id>
97+
```
98+
99+
## MCP: generic tool calling
100+
101+
For ad‑hoc tools or new MCP servers, use `mcp.call`:
102+
103+
```
104+
steps:
105+
- uses: mcp
106+
server: tm
107+
tool: add_evidence
108+
arguments: { id: T1, type: note, ref: ok }
109+
```
110+
111+
This reuses the orchestrator’s persistent MCP clients for `odoo` and `tm`.
112+
113+
## Parallel steps (sequential for now)
114+
115+
`parallel:` steps execute their `children` sequentially today for safety. The internal
116+
`GraphExecutor` will fan out in true parallel once repo locking is in place.
117+
118+
## Admin tools
119+
120+
- List handlers: `python -m orchestrator.main doctor list-handlers`
121+
- Validate handler mappings + args:
122+
`python -m orchestrator.main doctor validate-recipe --recipe <name>`
123+
- Reset a node in a run:
124+
`python -m orchestrator.main doctor reset-node --run-id <id> --node-id <id>`
125+
126+
## Roadmap
127+
128+
- Discover additional handlers via entry points (packaged plugins) if needed.
129+
- Optional lockfile to pin handler name/version for reproducibility.
130+
- Full REST fallback for `github.open_pr` when `gh` is unavailable.

0 commit comments

Comments
 (0)