Skip to content

Commit 4fa76f7

Browse files
committed
[#169] Add YAML workflow language and execution
Defines a validated YAML workflow language as a thin layer over Orangu commands, natural-language prompts, skills, and tools. - Versioned workflows with global and per-job variables, job workspaces, roles, reusable functions, ordered main steps, explicit external-path approvals, standard control-flow constructs (if/else, bounded for/while, break, return, label/goto), and bounded code-and-review loops as a workflow step. - Validates the complete workflow before starting any potentially long-running job. - Adds --dry-run for offline preflight and --workflow for sequential execution with one shared prompt conversation per job. - Preflights explicit slash commands in every job, including required arguments, headless compatibility, and skills discovered separately in each workspace; all command errors are reported together. - Routes workflow roles through configured servers or the coordinator and keeps every job inside its workspace policy. - Documents workflows in the manual and cheat sheet and updates bash, zsh, and fish completions.
1 parent 488fb2a commit 4fa76f7

9 files changed

Lines changed: 4535 additions & 131 deletions

File tree

doc/cheatsheet/en/03-tools.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,16 @@ beside the built-in ones. orangu connects; it does not launch them.
1919

2020
| Command | What it does |
2121
| --- | --- |
22-
| `[mcp.<name>]` | A service in `orangu.conf`: its URL (usually ending in `/mcp`), `timeout`, `enabled`, `required`, `enabled_tools` / `disabled_tools`. |
23-
| `approval_mode = auto` | Or `prompt` (ask per call), `writes` (ask unless the tool is read-only), `deny`. Unattended and review runs deny what needs asking. |
24-
| `/mcp` `/mcp refresh` | What is connected, disabled or failed, and how many tools each gave; reconnect and rediscover. |
25-
| `/mcp add <name> <command>` | Write a profile and connect it; `/mcp remove <name>` drops it. |
26-
| `mcp__<server>__<tool>` | How they are named, so nothing collides; `/tools` lists them. Tools only. |
22+
| `[mcp.<name>]` | A configured Streamable HTTP service; URL, filters and approval are in `orangu.conf`. |
23+
| `/mcp` `/mcp refresh` | Show and reconnect; `/mcp add` / `remove` manage one. `/tools` lists `mcp__<server>__<tool>`. |
24+
25+
## Workflow files
26+
27+
| Command | What it does |
28+
| --- | --- |
29+
| `orangu --workflow run.yaml --dry-run` | Validate every job without executing. |
30+
| `orangu --workflow run.yaml` | Run the jobs in order. |
31+
| `orangu --workflow run.yaml` `status` `pause` `resume` `clear` | Inspect/pause/resume/clear the saved job state. |
2732

2833
## The rest of the stack
2934

doc/manual/en/40-terminal.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ The sync runs in the background so it never delays startup. Its progress and res
2222
| `-a` | `--all` | Reopen the workspace tabs that were open at the end of the last run. |
2323
| `-t` | `--theme` | Apply a theme to the terminal interface for this run. Built-in names and paths to `.theme` files are accepted. |
2424
| `-p` | `--prompt` | Run a single prompt or command and exit — no terminal UI and no session on disk. See One-shot mode below. |
25-
| `-q` | `--quiet` | Print nothing on success — the exit code is the result. Applies to the modes that print and exit (`-p`, `-l`, `-s`). |
25+
| | `--workflow FILE` | Validate and execute every job in a YAML workflow. |
26+
| | `--dry-run FILE` | Validate a YAML workflow completely without executing it or loading configuration. |
27+
| `-q` | `--quiet` | Print nothing on success — the exit code is the result. Applies to the modes that print and exit, including workflows. |
2628
| `-l` | `--list` | List all stored sessions as a `SESSION WORKSPACE BRANCH DATE` table and exit. |
2729
| `-i` | `--init` | Interactively create `~/.orangu/orangu.conf` and exit (see the Configuration chapter). |
2830
| `-s` | `--shell-completions` | Print the shell completion script for the detected shell (`$SHELL`; bash, zsh, or fish) and exit. |
@@ -56,7 +58,7 @@ orangu -q -p "/export pr" # writes the PDF, says nothing
5658
echo $? # 0, or 1 with the reason on stderr
5759
```
5860

59-
That is what makes it suit a crontab or a `~/.orangu/schedule` job: silent until the day it fails. It applies to the modes that print and exit — `-p`, `-l`, and `-s`. Combining it with `-i` or with the terminal interface is an error rather than a no-op, since neither has diagnostics to separate from its output.
61+
That is what makes it suit a crontab or a `~/.orangu/schedule` job: silent until the day it fails. It applies to the modes that print and exit — `-p`, `-l`, `-s`, and `--workflow` (including `--dry-run`). Combining it with `-i` or with the terminal interface is an error rather than a no-op, since neither has diagnostics to separate from its output.
6062

6163
`-t`/`--theme` is the other way round: a theme paints the interface — including the terminal's own background and foreground — so it takes effect only when there is an interface. A one-shot ignores it rather than repainting the terminal it printed into.
6264

doc/manual/en/45-workflows.md

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
\newpage
2+
3+
# Workflow files
4+
5+
A workflow file describes a set of jobs and the ordered Orangu operations to
6+
run in each workspace. It is a thin YAML layer over the same slash commands,
7+
natural-language prompts, skills, and tools available through `orangu -p`.
8+
9+
Validate the complete file before starting it:
10+
11+
```text
12+
orangu --workflow export-prs.yaml --dry-run
13+
orangu --workflow export-prs.yaml
14+
```
15+
16+
`--workflow --dry-run` loads no model configuration and executes nothing. It checks
17+
every job, workspace, variable, function call, approval, loop definition,
18+
control-flow construct, and explicit slash command. `--workflow` performs the
19+
same preflight before it loads configuration or starts the first job.
20+
21+
## Complete file
22+
23+
The following workflow runs `/export pr` in two projects and collects both
24+
PDFs in one explicitly approved directory:
25+
26+
```yaml
27+
orangu:
28+
version: 1
29+
role: all
30+
variables:
31+
upstream: /home/me/Upstream
32+
jobs:
33+
- job: pgagroal
34+
workspace: /home/me/PostgreSQL/pgagroal
35+
- job: orangu
36+
workspace: /home/me/Company/orangu
37+
functions:
38+
export_pr:
39+
- command: /export pr
40+
move_pdf:
41+
- command: /shell mv ${job}-pr.pdf ${upstream}/
42+
main:
43+
- approved: ${upstream}
44+
- call: export_pr
45+
- call: move_pdf
46+
```
47+
48+
The root key is `orangu`. `version` is required and is currently `1`. `jobs`
49+
and `main` must both be non-empty. Unknown keys are errors rather than ignored
50+
configuration.
51+
52+
## Jobs, roles, and conversations
53+
54+
Each `jobs` entry requires a unique `job` name and an existing `workspace`
55+
directory. Jobs run sequentially in declaration order and stop at the first
56+
failure. The complete `main` sequence runs once for every job, with `${job}`
57+
set to that job's name.
58+
59+
`role` may be declared once under `orangu` or overridden by a job. It defaults
60+
to `all`; the other v1 values are `code`, `review`, `explorer`, and
61+
`embeddings`. When the configured endpoint is an Orangu coordinator, the role
62+
is sent to it for routing. Otherwise Orangu selects a configured server with
63+
that role, falling back to the default server.
64+
65+
Command steps that reach the model share one conversation within their job.
66+
The next job starts a separate conversation in its own workspace.
67+
68+
## Variables
69+
70+
Top-level `variables` apply to every job. A job may add variables or override a
71+
top-level value:
72+
73+
```yaml
74+
variables:
75+
profile: release
76+
jobs:
77+
- job: server
78+
workspace: server
79+
variables:
80+
profile: debug
81+
```
82+
83+
Values may be strings, integers, floating-point numbers, or booleans and are
84+
inserted with `${name}`. Variables may refer to other variables. `${job}` is
85+
reserved for the current job name and cannot be redefined. Undefined,
86+
recursive, malformed, or unterminated references fail validation.
87+
88+
## Functions and execution order
89+
90+
`functions` contains named, reusable lists of steps. A `call` expands a
91+
function at that exact position, so `main` is the single unambiguous execution
92+
order:
93+
94+
```yaml
95+
functions:
96+
build:
97+
- command: /build release
98+
explain:
99+
- command: Explain any build warnings and fix actionable ones.
100+
main:
101+
- call: build
102+
- call: explain
103+
```
104+
105+
Functions may call other functions. Unknown calls and direct or indirect
106+
recursion are validation errors. Function and variable names start with a
107+
letter or underscore and contain only letters, digits, and underscores.
108+
109+
## Step types
110+
111+
Version 1 has eleven explicit step types. A step contains exactly one of them.
112+
`command`, `call`, `approved`, and `loop` are the basic operations;
113+
`if`, `for`, `while`, `break`, `return`, `label`, and `goto` are the
114+
workflow's programming constructs. `loop` is one of those constructs: a
115+
bounded code-and-review cycle, not a separate command.
116+
117+
### `command`
118+
119+
The value is offered to Orangu's existing dispatcher:
120+
121+
```yaml
122+
main:
123+
- command: /status
124+
- command: /code-review authentication
125+
- command: Investigate the failing authentication test and fix it.
126+
```
127+
128+
A built-in slash command runs locally, a workspace skill expands into a model
129+
prompt, and other text is a natural-language model prompt. Preflight rejects
130+
unknown slash commands, missing required arguments, and commands that require
131+
the interactive terminal or persistent terminal session.
132+
133+
### `call`
134+
135+
`call` inserts a named function's steps. Expansion happens separately for each
136+
job, after its variables and `${job}` have been resolved.
137+
138+
### `approved`
139+
140+
An external path supplied through a variable requires an earlier `approved`
141+
step. One path or a list of paths may be approved:
142+
143+
```yaml
144+
main:
145+
- approved:
146+
- ${reports}
147+
- ${archives}
148+
- command: /shell mv report.pdf ${reports}/
149+
```
150+
151+
Approved paths must already exist. Approval is ordered: placing it after the
152+
command does not authorize that command. An approval authorizes later uses
153+
wherever the run reaches them, including inside a called function or after
154+
it returns. It is an explicit workflow-language
155+
opt-in for passing a variable-derived external path; it does not widen the
156+
roots of Orangu's file tools.
157+
158+
File and directory tools reject absolute, `..`, and symlinked paths that leave
159+
the job workspace, and a tool's `cwd` must remain inside it. `/shell` explicitly
160+
invokes the user's platform shell and is not an operating-system sandbox.
161+
Literal shell syntax therefore retains the existing `/shell` semantics.
162+
163+
### `loop`
164+
165+
A loop repeats a tool-enabled work phase followed by an independent,
166+
tool-free review phase:
167+
168+
```yaml
169+
functions:
170+
improve_parser:
171+
- loop:
172+
objective: Fix ${job}'s parser without changing its public API.
173+
stop:
174+
type: goal
175+
condition: All parser tests pass and review finds no regression.
176+
review:
177+
checks:
178+
- cargo test
179+
rubric:
180+
- correctness
181+
- backward compatibility
182+
main:
183+
- call: improve_parser
184+
```
185+
186+
Every iteration gives the worker the objective and the previous review. After
187+
the work phase, Orangu runs the configured checks in the job workspace. The
188+
reviewer receives the worker report, check results, and branch diff, but no
189+
tools. Its response becomes feedback for the next iteration.
190+
191+
Each loop has exactly one stopping policy:
192+
193+
| `stop.type` | Required field | Meaning |
194+
| --- | --- | --- |
195+
| `turns` | `count` | Stop after a positive number of complete iterations. |
196+
| `time` | `duration` | Stop at a safe boundary after active time such as `30m` or `2h`. |
197+
| `goal` | `condition` | Continue until the reviewer verifies the stated condition. |
198+
199+
Durations use `s`, `m`, `h`, or `d`. Orangu never interrupts a tool call or
200+
review response merely because the duration has elapsed. For a goal loop, the
201+
reviewer must end with the exact standalone line `LOOP_COMPLETE: yes`; an
202+
inline mention or optimistic wording does not complete it.
203+
204+
`review.checks` and `review.rubric` are optional lists. Variables are expanded
205+
in the objective, stop value, checks, and rubric. The default rubric covers
206+
correctness, regressions, tests, and maintainability; listed entries add
207+
workflow-specific criteria.
208+
209+
### `if`, `for`, `while`, `break`, `return`, `label`, `goto`
210+
211+
Workflows are a small programming language over the steps above. Conditions
212+
are ordinary workflow commands run in the job session: success means true
213+
and failure selects the other branch instead of failing the job.
214+
215+
```yaml
216+
main:
217+
- if:
218+
condition: /shell test -f report.pdf
219+
then:
220+
- command: /shell mv report.pdf ${reports}/
221+
else:
222+
- command: /export pr
223+
- for:
224+
var: target
225+
items: [alpha, beta]
226+
do:
227+
- command: /build ${target}
228+
- while:
229+
condition: /shell test ! -f done.txt
230+
max_turns: 10
231+
do:
232+
- command: /test
233+
- break: true
234+
- label: finished
235+
- goto: finished
236+
```
237+
238+
`for` repeats its `do` steps once per item, with `${var}` set to that
239+
value. Instead of `items`, a finite `range` such as `1..3` may be given.
240+
At most 100 items (a range span of at most 1000) are allowed, and a loop
241+
variable cannot shadow an outer one or be used in an `approved` path.
242+
`while` re-evaluates its condition before every turn and stops after
243+
`max_turns` turns at the latest (between 1 and 1000, required).
244+
`break` stops the innermost enclosing `for` or `while` and is rejected
245+
outside one. `return` stops the current function after its `call` and is
246+
rejected in `main`. Labels address the step list that declares them: a
247+
`goto` must name a label from the same function or `main` list, so a jump
248+
never crosses a function boundary or implicitly enters a nested block.
249+
250+
Every branch, condition, and loop body is validated before execution,
251+
including the slash-command preflight, so an error in a rarely taken
252+
branch still fails `--dry-run` rather than an hours-long run.
253+
254+
## Managing a workflow
255+
256+
Loop progress is stored in Orangu's workspace cache rather than in the
257+
repository. The workflow file supplies the jobs and their workspaces; append a
258+
lifecycle action after the file to inspect or control the saved loop for every
259+
job:
260+
261+
```text
262+
orangu --workflow /path/to/workflow.yml status
263+
orangu --workflow /path/to/workflow.yml pause
264+
orangu --workflow /path/to/workflow.yml resume
265+
orangu --workflow /path/to/workflow.yml clear
266+
```
267+
268+
`status` reports the objective, policy, iteration count, active time, and most
269+
recent review for each job. `pause` stops an active loop at its next safe phase
270+
boundary. `resume` continues a paused or failed loop with its saved review
271+
feedback. `clear` cancels saved state so another loop may start. A
272+
per-workspace lock prevents two loops from running against the same checkout
273+
simultaneously.
274+
275+
## Operational behavior
276+
277+
- `-q` suppresses successful workflow output. Failures still write to stderr
278+
and return a non-zero exit status.
279+
- A loop can modify its workspace but does not commit, push, or open a pull
280+
request unless an explicit workflow command does so.
281+
- Review evidence is bounded before it is sent to the model. Truncation is
282+
marked rather than silently treated as complete evidence.
283+
- Saved loop state is control metadata, not a persisted model conversation.

0 commit comments

Comments
 (0)