rote-cli 0.14.0__tar.gz → 0.15.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rote_cli-0.14.0 → rote_cli-0.15.0}/PKG-INFO +139 -29
- {rote_cli-0.14.0 → rote_cli-0.15.0}/README.md +129 -20
- rote_cli-0.15.0/examples/expense-audit/README.md +156 -0
- rote_cli-0.15.0/examples/expense-audit/eval-input.json +199 -0
- rote_cli-0.15.0/examples/expense-audit/expected/eval.yaml +60 -0
- rote_cli-0.15.0/examples/expense-audit/expected/extracted/audit.py +171 -0
- rote_cli-0.15.0/examples/expense-audit/expected/extracted/checks.py +71 -0
- rote_cli-0.15.0/examples/expense-audit/expected/extracted/fx.py +62 -0
- rote_cli-0.15.0/examples/expense-audit/expected/extracted/report.py +186 -0
- rote_cli-0.15.0/examples/expense-audit/expected/pipeline-draft.yaml +242 -0
- rote_cli-0.15.0/examples/expense-audit/expected/pipeline.yaml +269 -0
- rote_cli-0.15.0/examples/expense-audit/input.json +21 -0
- rote_cli-0.15.0/examples/expense-audit/runs/2026-10-18-first-agent-run/compile-metrics.json +17 -0
- rote_cli-0.15.0/examples/expense-audit/runs/2026-10-18-first-agent-run/compile-report.md +220 -0
- rote_cli-0.15.0/examples/expense-audit/runs/2026-10-18-first-agent-run/eval.yaml +84 -0
- rote_cli-0.15.0/examples/expense-audit/runs/2026-10-18-first-agent-run/extracted/checks.py +94 -0
- rote_cli-0.15.0/examples/expense-audit/runs/2026-10-18-first-agent-run/extracted/fx.py +100 -0
- rote_cli-0.15.0/examples/expense-audit/runs/2026-10-18-first-agent-run/extracted/policy.py +92 -0
- rote_cli-0.15.0/examples/expense-audit/runs/2026-10-18-first-agent-run/extracted/report.py +145 -0
- rote_cli-0.15.0/examples/expense-audit/runs/2026-10-18-first-agent-run/extracted/save.py +51 -0
- rote_cli-0.15.0/examples/expense-audit/runs/2026-10-18-first-agent-run/pipeline.yaml +382 -0
- rote_cli-0.15.0/examples/expense-audit/runs/2026-10-18-first-agent-run/provenance.json +49 -0
- rote_cli-0.15.0/examples/expense-audit/scorecard.md +82 -0
- rote_cli-0.15.0/examples/expense-audit/skill/SKILL.md +94 -0
- rote_cli-0.15.0/examples/expense-audit/skill/references/expense-policy.md +48 -0
- rote_cli-0.15.0/examples/expense-audit/skill/references/fx-rates.md +30 -0
- rote_cli-0.15.0/examples/expense-audit/skill/references/report-format.md +43 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/pyproject.toml +31 -8
- {rote_cli-0.14.0 → rote_cli-0.15.0}/skills/rote-compile/SKILL.md +42 -13
- {rote_cli-0.14.0 → rote_cli-0.15.0}/skills/rote-compile/references/crystallization-heuristics.md +19 -2
- rote_cli-0.15.0/skills/rote-compile/references/implementation.md +347 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/skills/rote-compile/references/ir-schema.md +95 -7
- {rote_cli-0.14.0 → rote_cli-0.15.0}/skills/rote-compile/references/llm-judge-extraction.md +49 -3
- {rote_cli-0.14.0 → rote_cli-0.15.0}/skills/rote-compile/references/node-kinds.md +11 -1
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/__init__.py +1 -1
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/_dbos.py +16 -15
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/adapters/__init__.py +2 -2
- rote_cli-0.15.0/src/rote/adapters/_common.py +1029 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/adapters/_py_common.py +486 -25
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/adapters/_ts_common.py +975 -182
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/adapters/cloudflare.py +75 -47
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/adapters/dbos.py +135 -77
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/adapters/dbos_ts.py +115 -71
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/adapters/inngest.py +99 -46
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/adapters/python.py +61 -80
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/adapters/temporal.py +78 -18
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/cli.py +861 -143
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/compiler/__init__.py +163 -40
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/compiler/drivers/__init__.py +12 -2
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/compiler/drivers/anthropic_api.py +7 -7
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/compiler/drivers/claude.py +276 -51
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/compiler/drivers/openai_api.py +7 -7
- rote_cli-0.15.0/src/rote/compiler/provenance.py +679 -0
- rote_cli-0.15.0/src/rote/compiler/sandbox.py +316 -0
- rote_cli-0.15.0/src/rote/compiler/verify.py +636 -0
- rote_cli-0.15.0/src/rote/contracts.py +647 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/eval/empirical.py +143 -18
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/eval/estimate.py +59 -3
- rote_cli-0.15.0/src/rote/eval/infra_pricing.py +298 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/eval/pricing.py +17 -3
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/eval/priors.py +23 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/eval/scorecard.py +148 -7
- rote_cli-0.15.0/src/rote/examples.py +184 -0
- rote_cli-0.15.0/src/rote/extras.py +138 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/inference/__init__.py +2 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/inference/_runtime_helper.py +478 -93
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/init_wizard.py +2 -1
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/ir.py +479 -14
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/mcp/__init__.py +1 -3
- rote_cli-0.15.0/src/rote/mcp/_runtime_helper.py +741 -0
- rote_cli-0.15.0/src/rote/mcp/auth.py +864 -0
- rote_cli-0.15.0/src/rote/mcp/client_metadata.json +20 -0
- rote_cli-0.15.0/src/rote/mcp/learned.py +168 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/mcp/live_tools.py +20 -21
- rote_cli-0.15.0/src/rote/mcp/parked.py +626 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/mcp/registry.py +32 -23
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/mcp/release.py +69 -37
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/mcp/tokens.py +59 -13
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/runners/__init__.py +70 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/runners/_node.py +2 -2
- rote_cli-0.15.0/src/rote/runners/cloudflare.py +313 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/runners/dbos_ts.py +58 -10
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/runners/inngest.py +60 -6
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/runners/temporal.py +21 -11
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/serve/backends.py +3 -2
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/serve/server.py +108 -30
- rote_cli-0.14.0/skills/rote-compile/references/implementation.md +0 -109
- rote_cli-0.14.0/src/rote/adapters/_common.py +0 -541
- rote_cli-0.14.0/src/rote/contracts.py +0 -330
- rote_cli-0.14.0/src/rote/mcp/_runtime_helper.py +0 -378
- rote_cli-0.14.0/src/rote/mcp/auth.py +0 -166
- rote_cli-0.14.0/src/rote/runners/cloudflare.py +0 -172
- {rote_cli-0.14.0 → rote_cli-0.15.0}/.gitignore +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/LICENSE +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/skills/rote-compile/references/eval-estimates.md +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/__main__.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/app_registry.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/cloud_auth.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/cloud_compile.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/compiler/drivers/_fs_tools.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/compiler/drivers/_heartbeat.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/compiler/drivers/codex.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/compiler/events.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/compiler/update.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/config.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/deploy.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/deploy_rote_cloud.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/eval/__init__.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/eval/baseline.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/eval/sidecar.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/eval/tokens.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/probe.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/py.typed +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/serve/__init__.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/serve/registry.py +0 -0
- {rote_cli-0.14.0 → rote_cli-0.15.0}/src/rote/skill_source.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: rote-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.15.0
|
|
4
4
|
Summary: Compile AI agent skills into cheap, fast, deterministic pipelines that run without an LLM in the loop
|
|
5
5
|
Project-URL: Homepage, https://roteskills.com
|
|
6
6
|
Project-URL: Repository, https://github.com/trevhud/rote
|
|
@@ -25,30 +25,31 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
25
25
|
Classifier: Typing :: Typed
|
|
26
26
|
Requires-Python: >=3.11
|
|
27
27
|
Requires-Dist: pydantic>=2.7
|
|
28
|
+
Requires-Dist: pytest<10,>=9.0
|
|
28
29
|
Requires-Dist: pyyaml>=6.0
|
|
29
30
|
Provides-Extra: api
|
|
30
|
-
Requires-Dist: anthropic>=
|
|
31
|
+
Requires-Dist: anthropic>=1.15; extra == 'api'
|
|
31
32
|
Provides-Extra: dbos
|
|
32
|
-
Requires-Dist: dbos<3
|
|
33
|
+
Requires-Dist: dbos<4,>=3.3; extra == 'dbos'
|
|
33
34
|
Provides-Extra: dev
|
|
34
|
-
Requires-Dist: anthropic>=
|
|
35
|
-
Requires-Dist: dbos<3
|
|
36
|
-
Requires-Dist: fastmcp<
|
|
35
|
+
Requires-Dist: anthropic>=1.15; extra == 'dev'
|
|
36
|
+
Requires-Dist: dbos<4,>=3.3; extra == 'dev'
|
|
37
|
+
Requires-Dist: fastmcp<5,>=4.1.1; extra == 'dev'
|
|
37
38
|
Requires-Dist: httpx>=0.28; extra == 'dev'
|
|
38
39
|
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
39
40
|
Requires-Dist: openai>=2.45; extra == 'dev'
|
|
40
41
|
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
41
|
-
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
42
42
|
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
43
43
|
Requires-Dist: temporalio>=1.7; extra == 'dev'
|
|
44
|
+
Requires-Dist: trustme>=1.2.1; extra == 'dev'
|
|
44
45
|
Requires-Dist: types-pyyaml; extra == 'dev'
|
|
45
46
|
Provides-Extra: mcp
|
|
46
|
-
Requires-Dist: fastmcp<
|
|
47
|
+
Requires-Dist: fastmcp<5,>=4.1.1; extra == 'mcp'
|
|
47
48
|
Requires-Dist: httpx>=0.28; extra == 'mcp'
|
|
48
49
|
Provides-Extra: openai-api
|
|
49
50
|
Requires-Dist: openai>=2.45; extra == 'openai-api'
|
|
50
51
|
Provides-Extra: serve
|
|
51
|
-
Requires-Dist: fastmcp<
|
|
52
|
+
Requires-Dist: fastmcp<5,>=4.1.1; extra == 'serve'
|
|
52
53
|
Requires-Dist: httpx>=0.28; extra == 'serve'
|
|
53
54
|
Provides-Extra: temporal
|
|
54
55
|
Requires-Dist: temporalio>=1.7; extra == 'temporal'
|
|
@@ -75,6 +76,91 @@ genuinely need judgment. A 10 to 20 minute agent loop becomes a
|
|
|
75
76
|
background workflow that costs a fraction of the tokens and can be
|
|
76
77
|
regression tested.
|
|
77
78
|
|
|
79
|
+
### Try it free in 60 seconds
|
|
80
|
+
|
|
81
|
+
No API key, no subscription, no tokens, no clone. The
|
|
82
|
+
[`expense-audit`](examples/expense-audit/) example is a month-end expense
|
|
83
|
+
audit skill whose compiled pipeline ships with working code, and it comes
|
|
84
|
+
inside the package, so you can emit it and run it before you compile
|
|
85
|
+
anything of your own:
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
uv tool install 'rote-cli[dbos]' # or: python3 -m venv .venv && . .venv/bin/activate && pip install 'rote-cli[dbos]'
|
|
89
|
+
rote example expense-audit # copies the skill, IR, code and sample input to ./expense-audit
|
|
90
|
+
# (zero-install: uvx --from rote-cli rote example expense-audit)
|
|
91
|
+
|
|
92
|
+
# IR -> a plain Python script, then run it on the sample batch
|
|
93
|
+
rote emit expense-audit/expected/pipeline-draft.yaml --runtime python --out expense-audit/app-python
|
|
94
|
+
rote run expense-audit/app-python --input expense-audit/input.json
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`rote example` prints these commands (and the dbos and eval ones) for
|
|
98
|
+
the copy it made. `rote example` alone lists what is bundled. Prefer the
|
|
99
|
+
repo? `git clone https://github.com/trevhud/rote.git && cd rote`, then
|
|
100
|
+
use `examples/expense-audit/` in place of `expense-audit/`.
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
rote run (python): succeeded
|
|
104
|
+
0.1s
|
|
105
|
+
{
|
|
106
|
+
"file_report": {
|
|
107
|
+
"report_path": "/home/you/expense-audit/app-python/reports/2026-09-expense-audit.md",
|
|
108
|
+
"status": "draft",
|
|
109
|
+
"reviewer": null,
|
|
110
|
+
"reimbursable_usd": 2395.59,
|
|
111
|
+
"pending_usd": 844.39,
|
|
112
|
+
"returned_usd": 64.17,
|
|
113
|
+
"batch_total_usd": 3304.15,
|
|
114
|
+
"auto_approved": ["Dev Patel", "Emi Tanaka"],
|
|
115
|
+
"review_items": 4,
|
|
116
|
+
"blocked_items": 2
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Sixteen expense lines in five currencies went through a parallel wave of
|
|
122
|
+
three mandatory checks, a per-employee audit fanned out five ways, and a
|
|
123
|
+
report written to disk, with no model anywhere in the loop. The full
|
|
124
|
+
skill also parks for the controller's approval, which needs a durable
|
|
125
|
+
runtime:
|
|
126
|
+
|
|
127
|
+
```sh
|
|
128
|
+
rote emit expense-audit/expected/pipeline.yaml --runtime dbos --out expense-audit/app-dbos
|
|
129
|
+
rote run expense-audit/app-dbos --input expense-audit/input.json \
|
|
130
|
+
--signal controller_review='{"reviewer": "Priya Shah", "approved_ids": ["EXP-2403", "EXP-2413", "EXP-2420"], "rejected_ids": ["EXP-2410"]}'
|
|
131
|
+
# ... "status": "final", "reimbursable_usd": 2990.98, "returned_usd": 313.17
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
What compiling it bought, measured: we ran the same batch twice each way
|
|
135
|
+
with `rote eval --run` (the skill as an agent via `claude -p` on Sonnet,
|
|
136
|
+
against the dbos app above):
|
|
137
|
+
|
|
138
|
+
| | Skill run as an agent | Compiled pipeline |
|
|
139
|
+
| --- | --- | --- |
|
|
140
|
+
| Wall clock per run | 42.9 s | 9.8 s, mostly DBOS startup |
|
|
141
|
+
| Cost per run | $0.22 | $0 |
|
|
142
|
+
| Identical output across runs | 1 of 2 | 2 of 2 |
|
|
143
|
+
|
|
144
|
+
So it's roughly 4x faster in practice, it drops from $0.22 a run to
|
|
145
|
+
nothing, and it gives the same answer every time. The agent's two runs
|
|
146
|
+
disagreed on where they filed the report. Full results are in
|
|
147
|
+
[`examples/expense-audit/scorecard.md`](examples/expense-audit/scorecard.md).
|
|
148
|
+
|
|
149
|
+
To get a scorecard for any pipeline without running anything, use
|
|
150
|
+
`rote eval`. It fetches the live price list once and runs no inference.
|
|
151
|
+
Its numbers are an estimate from turn counts, not a measurement:
|
|
152
|
+
|
|
153
|
+
```sh
|
|
154
|
+
rote eval examples/expense-audit/expected/pipeline.yaml
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`pip` installs don't include `examples/`, hence the clone. The other
|
|
158
|
+
examples' extracted modules are `NotImplementedError` stubs waiting for
|
|
159
|
+
real vendor API clients, so `expense-audit` is the one that runs end to
|
|
160
|
+
end. Details in [its README](examples/expense-audit/README.md).
|
|
161
|
+
|
|
162
|
+
### Compile your own skill
|
|
163
|
+
|
|
78
164
|
```sh
|
|
79
165
|
pip install rote-cli # or zero-install: uvx --from rote-cli rote ...
|
|
80
166
|
|
|
@@ -159,6 +245,19 @@ representation (`pipeline.yaml`), extracted Python modules for the
|
|
|
159
245
|
deterministic parts, typed signature stubs for the LLM-judge parts, and
|
|
160
246
|
runnable code for the durable execution engine of your choice.
|
|
161
247
|
|
|
248
|
+
The agent implements the deterministic `pure_function` steps the skill
|
|
249
|
+
spells out and writes tests for them. Before the compile reports
|
|
250
|
+
success, rote checks what the agent wrote and runs those tests, and any
|
|
251
|
+
failure goes back to the agent to fix. The tests are code an LLM wrote
|
|
252
|
+
from a skill you may not have written, so rote runs them in an OS
|
|
253
|
+
sandbox (`sandbox-exec` on macOS, bubblewrap on Linux): no network, no
|
|
254
|
+
reads outside the compile's work dir and the Python install, no programs
|
|
255
|
+
other than Python, no API keys in the environment, an empty `HOME`. If
|
|
256
|
+
no sandbox works on your machine, the tests are skipped and the compile
|
|
257
|
+
says the deliverable is unverified. `--trust-skill` runs them without a
|
|
258
|
+
sandbox, and `--no-verify-tests` never runs them; either way, review
|
|
259
|
+
`<out>/compiled/tests/` before running them yourself.
|
|
260
|
+
|
|
162
261
|
`rote` is a three-layer system; each layer has one job and contracts on
|
|
163
262
|
a small interface.
|
|
164
263
|
|
|
@@ -180,7 +279,7 @@ a small interface.
|
|
|
180
279
|
```
|
|
181
280
|
|
|
182
281
|
1. **The compiler agent** (`skills/rote-compile/`): a regular
|
|
183
|
-
Anthropic Skill (`SKILL.md` +
|
|
282
|
+
Anthropic Skill (`SKILL.md` + six reference files). This is the
|
|
184
283
|
*brain*; it runs inside any Skills-compatible surface, and you don't
|
|
185
284
|
need `rote` to use it.
|
|
186
285
|
2. **The IR** (`src/rote/ir.py`): Pydantic models for the five node
|
|
@@ -210,7 +309,9 @@ touching Python tooling:
|
|
|
210
309
|
Then say "compile this skill" (or run `/rote:compile`). It confirms the
|
|
211
310
|
source directory, asks which runtime you want, runs the CLI via
|
|
212
311
|
[uv](https://docs.astral.sh/uv/) in the background, and reports the
|
|
213
|
-
emitted pipeline.
|
|
312
|
+
emitted pipeline. `/rote:implement` then fills in the emitted stub
|
|
313
|
+
modules against each node's IR contract and verifies the result with
|
|
314
|
+
`rote run`. A third skill, `/rote:serve`, wires compiled
|
|
214
315
|
pipelines up as MCP tools so Claude can trigger the deployed workflows
|
|
215
316
|
(see [docs/mcp-trigger.md](docs/mcp-trigger.md)).
|
|
216
317
|
|
|
@@ -230,9 +331,10 @@ this README still works logged out.
|
|
|
230
331
|
|
|
231
332
|
> **Naming note:** the `rote` package on PyPI is an unrelated
|
|
232
333
|
> memoization library that also installs `import rote`, so the two can't
|
|
233
|
-
> share an environment. This project's distribution is `rote-cli`
|
|
234
|
-
>
|
|
235
|
-
> `
|
|
334
|
+
> share an environment. This project's distribution is `rote-cli` and the
|
|
335
|
+
> import name stays `rote`. The package installs the same CLI under two
|
|
336
|
+
> commands, `rote` and `rote-cli`, so both `uvx rote-cli ...` and
|
|
337
|
+
> `uvx --from rote-cli rote ...` work. See [docs/releasing.md](docs/releasing.md).
|
|
236
338
|
|
|
237
339
|
### Run on the bundled example
|
|
238
340
|
|
|
@@ -246,7 +348,7 @@ rote compile examples/bdr-outreach/skill --out /tmp/bdr-compiled
|
|
|
246
348
|
|
|
247
349
|
On that skill the compiler produces a 22-node IR that's **78.9%
|
|
248
350
|
codifiable** (15 of 19 non-gate nodes), extracts 5 Python modules and 2
|
|
249
|
-
typed judge signatures, and flags
|
|
351
|
+
typed judge signatures, and flags 3 mandatory nodes and 3 HITL gates,
|
|
250
352
|
all in ~13 minutes for ~$0.70 (Sonnet via Claude Code). Along the way it
|
|
251
353
|
independently lifts the three MANDATORY exclusion checks out of prose,
|
|
252
354
|
pulls four batch-size constants out of prompt text, and models a
|
|
@@ -425,6 +527,8 @@ subscription path.
|
|
|
425
527
|
|
|
426
528
|
## How it differs from other tools
|
|
427
529
|
|
|
530
|
+
For a side-by-side with calcifier, skill-to-workflow, Claude Code dynamic workflows, and hand-written workflows, see [docs/comparison.md](docs/comparison.md).
|
|
531
|
+
|
|
428
532
|
- **vs. raw durable engines (Temporal / Cloudflare / Inngest / Restate):**
|
|
429
533
|
they give you the workflow *runtime*; they don't help you decide *what
|
|
430
534
|
should be a workflow*. `rote` is the missing step that turns a working
|
|
@@ -443,16 +547,17 @@ subscription path.
|
|
|
443
547
|
## Status
|
|
444
548
|
|
|
445
549
|
`rote` is **pre-1.0**. The end-to-end flow works on the BDR example. The
|
|
446
|
-
fast suite (`pytest tests/`) makes no real API calls
|
|
447
|
-
on every push
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
Known gaps:
|
|
455
|
-
|
|
550
|
+
fast suite (`pytest tests/`) makes no real API calls, spends no tokens,
|
|
551
|
+
and runs on every push. CI also runs every `slow`-marked e2e suite on
|
|
552
|
+
every push: DBOS over SQLite, the MCP server over real stdio, the
|
|
553
|
+
TypeScript targets via `tsc --noEmit` and live dev servers (Cloudflare
|
|
554
|
+
`wrangler dev`, the Inngest dev server), and DBOS TypeScript against a
|
|
555
|
+
Docker Postgres. Locally, `pytest tests/ -m slow` runs them; they need a
|
|
556
|
+
Node toolchain and Docker.
|
|
557
|
+
|
|
558
|
+
Known gaps: extracted modules that need a vendor API client (HubSpot,
|
|
559
|
+
ZoomInfo, and so on) come out as `NotImplementedError` stubs you fill in,
|
|
560
|
+
and a Restate adapter is planned.
|
|
456
561
|
Published on
|
|
457
562
|
PyPI as [`rote-cli`](https://pypi.org/project/rote-cli/) via tag-driven
|
|
458
563
|
Trusted Publishing ([docs/releasing.md](docs/releasing.md)).
|
|
@@ -472,7 +577,7 @@ measure your own workflow instead of reading someone else's, use
|
|
|
472
577
|
```
|
|
473
578
|
rote/
|
|
474
579
|
├── docs/ agent-runtime · mcp-client · mcp-trigger · releasing
|
|
475
|
-
├── skills/rote-compile/ the compiler agent (SKILL.md +
|
|
580
|
+
├── skills/rote-compile/ the compiler agent (SKILL.md + 6 reference files)
|
|
476
581
|
├── src/rote/
|
|
477
582
|
│ ├── cli.py rote compile / emit / eval / serve
|
|
478
583
|
│ ├── ir.py Pydantic IR models + load_pipeline
|
|
@@ -480,6 +585,7 @@ rote/
|
|
|
480
585
|
│ └── adapters/ dbos · temporal · python · cloudflare · dbos_ts · inngest
|
|
481
586
|
│ (+ _common / _py_common / _ts_common emit helpers)
|
|
482
587
|
├── examples/
|
|
588
|
+
│ ├── expense-audit/ free first run: working extracted code · fan-out · HITL gate
|
|
483
589
|
│ ├── bdr-outreach/ canonical: all 5 node kinds · IR baseline · run snapshots
|
|
484
590
|
│ ├── ops-report/ 100% roteness: zero LLM nodes + a HITL gate
|
|
485
591
|
│ ├── deal-monitor/ data-heavy: parallel waves · fan-out judges · template render
|
|
@@ -501,12 +607,16 @@ rote/
|
|
|
501
607
|
emitted code uses under `--backend mcp`: endpoint/credential
|
|
502
608
|
resolution and durable park-on-auth across every MCP-capable runtime
|
|
503
609
|
- [`docs/mcp-trigger.md`](docs/mcp-trigger.md): `rote register` +
|
|
504
|
-
`rote serve`: compiled pipelines as MCP tools (FastMCP
|
|
610
|
+
`rote serve`: compiled pipelines as MCP tools (FastMCP 4.x)
|
|
505
611
|
- [`docs/releasing.md`](docs/releasing.md): tag-driven PyPI Trusted
|
|
506
612
|
Publishing
|
|
507
613
|
- [`skills/rote-compile/`](skills/rote-compile/): the compiler's
|
|
508
|
-
`SKILL.md` and its
|
|
509
|
-
heuristics, IR schema, LLM-judge extraction
|
|
614
|
+
`SKILL.md` and its six rubric files (node kinds, crystallization
|
|
615
|
+
heuristics, IR schema, LLM-judge extraction, implementation, eval
|
|
616
|
+
estimates)
|
|
617
|
+
- [`examples/expense-audit/`](examples/expense-audit/): the free first
|
|
618
|
+
run: a hand-compiled pipeline with working extracted code that
|
|
619
|
+
`rote run` completes on python, dbos, and temporal with no credentials
|
|
510
620
|
- [`examples/bdr-outreach/`](examples/bdr-outreach/): the canonical
|
|
511
621
|
skill, its ground-truth IR, and snapshotted real compiler runs
|
|
512
622
|
- [`examples/ops-report/`](examples/ops-report/): the 100%-roteness
|
|
@@ -19,6 +19,91 @@ genuinely need judgment. A 10 to 20 minute agent loop becomes a
|
|
|
19
19
|
background workflow that costs a fraction of the tokens and can be
|
|
20
20
|
regression tested.
|
|
21
21
|
|
|
22
|
+
### Try it free in 60 seconds
|
|
23
|
+
|
|
24
|
+
No API key, no subscription, no tokens, no clone. The
|
|
25
|
+
[`expense-audit`](examples/expense-audit/) example is a month-end expense
|
|
26
|
+
audit skill whose compiled pipeline ships with working code, and it comes
|
|
27
|
+
inside the package, so you can emit it and run it before you compile
|
|
28
|
+
anything of your own:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
uv tool install 'rote-cli[dbos]' # or: python3 -m venv .venv && . .venv/bin/activate && pip install 'rote-cli[dbos]'
|
|
32
|
+
rote example expense-audit # copies the skill, IR, code and sample input to ./expense-audit
|
|
33
|
+
# (zero-install: uvx --from rote-cli rote example expense-audit)
|
|
34
|
+
|
|
35
|
+
# IR -> a plain Python script, then run it on the sample batch
|
|
36
|
+
rote emit expense-audit/expected/pipeline-draft.yaml --runtime python --out expense-audit/app-python
|
|
37
|
+
rote run expense-audit/app-python --input expense-audit/input.json
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`rote example` prints these commands (and the dbos and eval ones) for
|
|
41
|
+
the copy it made. `rote example` alone lists what is bundled. Prefer the
|
|
42
|
+
repo? `git clone https://github.com/trevhud/rote.git && cd rote`, then
|
|
43
|
+
use `examples/expense-audit/` in place of `expense-audit/`.
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
rote run (python): succeeded
|
|
47
|
+
0.1s
|
|
48
|
+
{
|
|
49
|
+
"file_report": {
|
|
50
|
+
"report_path": "/home/you/expense-audit/app-python/reports/2026-09-expense-audit.md",
|
|
51
|
+
"status": "draft",
|
|
52
|
+
"reviewer": null,
|
|
53
|
+
"reimbursable_usd": 2395.59,
|
|
54
|
+
"pending_usd": 844.39,
|
|
55
|
+
"returned_usd": 64.17,
|
|
56
|
+
"batch_total_usd": 3304.15,
|
|
57
|
+
"auto_approved": ["Dev Patel", "Emi Tanaka"],
|
|
58
|
+
"review_items": 4,
|
|
59
|
+
"blocked_items": 2
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Sixteen expense lines in five currencies went through a parallel wave of
|
|
65
|
+
three mandatory checks, a per-employee audit fanned out five ways, and a
|
|
66
|
+
report written to disk, with no model anywhere in the loop. The full
|
|
67
|
+
skill also parks for the controller's approval, which needs a durable
|
|
68
|
+
runtime:
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
rote emit expense-audit/expected/pipeline.yaml --runtime dbos --out expense-audit/app-dbos
|
|
72
|
+
rote run expense-audit/app-dbos --input expense-audit/input.json \
|
|
73
|
+
--signal controller_review='{"reviewer": "Priya Shah", "approved_ids": ["EXP-2403", "EXP-2413", "EXP-2420"], "rejected_ids": ["EXP-2410"]}'
|
|
74
|
+
# ... "status": "final", "reimbursable_usd": 2990.98, "returned_usd": 313.17
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
What compiling it bought, measured: we ran the same batch twice each way
|
|
78
|
+
with `rote eval --run` (the skill as an agent via `claude -p` on Sonnet,
|
|
79
|
+
against the dbos app above):
|
|
80
|
+
|
|
81
|
+
| | Skill run as an agent | Compiled pipeline |
|
|
82
|
+
| --- | --- | --- |
|
|
83
|
+
| Wall clock per run | 42.9 s | 9.8 s, mostly DBOS startup |
|
|
84
|
+
| Cost per run | $0.22 | $0 |
|
|
85
|
+
| Identical output across runs | 1 of 2 | 2 of 2 |
|
|
86
|
+
|
|
87
|
+
So it's roughly 4x faster in practice, it drops from $0.22 a run to
|
|
88
|
+
nothing, and it gives the same answer every time. The agent's two runs
|
|
89
|
+
disagreed on where they filed the report. Full results are in
|
|
90
|
+
[`examples/expense-audit/scorecard.md`](examples/expense-audit/scorecard.md).
|
|
91
|
+
|
|
92
|
+
To get a scorecard for any pipeline without running anything, use
|
|
93
|
+
`rote eval`. It fetches the live price list once and runs no inference.
|
|
94
|
+
Its numbers are an estimate from turn counts, not a measurement:
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
rote eval examples/expense-audit/expected/pipeline.yaml
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`pip` installs don't include `examples/`, hence the clone. The other
|
|
101
|
+
examples' extracted modules are `NotImplementedError` stubs waiting for
|
|
102
|
+
real vendor API clients, so `expense-audit` is the one that runs end to
|
|
103
|
+
end. Details in [its README](examples/expense-audit/README.md).
|
|
104
|
+
|
|
105
|
+
### Compile your own skill
|
|
106
|
+
|
|
22
107
|
```sh
|
|
23
108
|
pip install rote-cli # or zero-install: uvx --from rote-cli rote ...
|
|
24
109
|
|
|
@@ -103,6 +188,19 @@ representation (`pipeline.yaml`), extracted Python modules for the
|
|
|
103
188
|
deterministic parts, typed signature stubs for the LLM-judge parts, and
|
|
104
189
|
runnable code for the durable execution engine of your choice.
|
|
105
190
|
|
|
191
|
+
The agent implements the deterministic `pure_function` steps the skill
|
|
192
|
+
spells out and writes tests for them. Before the compile reports
|
|
193
|
+
success, rote checks what the agent wrote and runs those tests, and any
|
|
194
|
+
failure goes back to the agent to fix. The tests are code an LLM wrote
|
|
195
|
+
from a skill you may not have written, so rote runs them in an OS
|
|
196
|
+
sandbox (`sandbox-exec` on macOS, bubblewrap on Linux): no network, no
|
|
197
|
+
reads outside the compile's work dir and the Python install, no programs
|
|
198
|
+
other than Python, no API keys in the environment, an empty `HOME`. If
|
|
199
|
+
no sandbox works on your machine, the tests are skipped and the compile
|
|
200
|
+
says the deliverable is unverified. `--trust-skill` runs them without a
|
|
201
|
+
sandbox, and `--no-verify-tests` never runs them; either way, review
|
|
202
|
+
`<out>/compiled/tests/` before running them yourself.
|
|
203
|
+
|
|
106
204
|
`rote` is a three-layer system; each layer has one job and contracts on
|
|
107
205
|
a small interface.
|
|
108
206
|
|
|
@@ -124,7 +222,7 @@ a small interface.
|
|
|
124
222
|
```
|
|
125
223
|
|
|
126
224
|
1. **The compiler agent** (`skills/rote-compile/`): a regular
|
|
127
|
-
Anthropic Skill (`SKILL.md` +
|
|
225
|
+
Anthropic Skill (`SKILL.md` + six reference files). This is the
|
|
128
226
|
*brain*; it runs inside any Skills-compatible surface, and you don't
|
|
129
227
|
need `rote` to use it.
|
|
130
228
|
2. **The IR** (`src/rote/ir.py`): Pydantic models for the five node
|
|
@@ -154,7 +252,9 @@ touching Python tooling:
|
|
|
154
252
|
Then say "compile this skill" (or run `/rote:compile`). It confirms the
|
|
155
253
|
source directory, asks which runtime you want, runs the CLI via
|
|
156
254
|
[uv](https://docs.astral.sh/uv/) in the background, and reports the
|
|
157
|
-
emitted pipeline.
|
|
255
|
+
emitted pipeline. `/rote:implement` then fills in the emitted stub
|
|
256
|
+
modules against each node's IR contract and verifies the result with
|
|
257
|
+
`rote run`. A third skill, `/rote:serve`, wires compiled
|
|
158
258
|
pipelines up as MCP tools so Claude can trigger the deployed workflows
|
|
159
259
|
(see [docs/mcp-trigger.md](docs/mcp-trigger.md)).
|
|
160
260
|
|
|
@@ -174,9 +274,10 @@ this README still works logged out.
|
|
|
174
274
|
|
|
175
275
|
> **Naming note:** the `rote` package on PyPI is an unrelated
|
|
176
276
|
> memoization library that also installs `import rote`, so the two can't
|
|
177
|
-
> share an environment. This project's distribution is `rote-cli`
|
|
178
|
-
>
|
|
179
|
-
> `
|
|
277
|
+
> share an environment. This project's distribution is `rote-cli` and the
|
|
278
|
+
> import name stays `rote`. The package installs the same CLI under two
|
|
279
|
+
> commands, `rote` and `rote-cli`, so both `uvx rote-cli ...` and
|
|
280
|
+
> `uvx --from rote-cli rote ...` work. See [docs/releasing.md](docs/releasing.md).
|
|
180
281
|
|
|
181
282
|
### Run on the bundled example
|
|
182
283
|
|
|
@@ -190,7 +291,7 @@ rote compile examples/bdr-outreach/skill --out /tmp/bdr-compiled
|
|
|
190
291
|
|
|
191
292
|
On that skill the compiler produces a 22-node IR that's **78.9%
|
|
192
293
|
codifiable** (15 of 19 non-gate nodes), extracts 5 Python modules and 2
|
|
193
|
-
typed judge signatures, and flags
|
|
294
|
+
typed judge signatures, and flags 3 mandatory nodes and 3 HITL gates,
|
|
194
295
|
all in ~13 minutes for ~$0.70 (Sonnet via Claude Code). Along the way it
|
|
195
296
|
independently lifts the three MANDATORY exclusion checks out of prose,
|
|
196
297
|
pulls four batch-size constants out of prompt text, and models a
|
|
@@ -369,6 +470,8 @@ subscription path.
|
|
|
369
470
|
|
|
370
471
|
## How it differs from other tools
|
|
371
472
|
|
|
473
|
+
For a side-by-side with calcifier, skill-to-workflow, Claude Code dynamic workflows, and hand-written workflows, see [docs/comparison.md](docs/comparison.md).
|
|
474
|
+
|
|
372
475
|
- **vs. raw durable engines (Temporal / Cloudflare / Inngest / Restate):**
|
|
373
476
|
they give you the workflow *runtime*; they don't help you decide *what
|
|
374
477
|
should be a workflow*. `rote` is the missing step that turns a working
|
|
@@ -387,16 +490,17 @@ subscription path.
|
|
|
387
490
|
## Status
|
|
388
491
|
|
|
389
492
|
`rote` is **pre-1.0**. The end-to-end flow works on the BDR example. The
|
|
390
|
-
fast suite (`pytest tests/`) makes no real API calls
|
|
391
|
-
on every push
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
Known gaps:
|
|
399
|
-
|
|
493
|
+
fast suite (`pytest tests/`) makes no real API calls, spends no tokens,
|
|
494
|
+
and runs on every push. CI also runs every `slow`-marked e2e suite on
|
|
495
|
+
every push: DBOS over SQLite, the MCP server over real stdio, the
|
|
496
|
+
TypeScript targets via `tsc --noEmit` and live dev servers (Cloudflare
|
|
497
|
+
`wrangler dev`, the Inngest dev server), and DBOS TypeScript against a
|
|
498
|
+
Docker Postgres. Locally, `pytest tests/ -m slow` runs them; they need a
|
|
499
|
+
Node toolchain and Docker.
|
|
500
|
+
|
|
501
|
+
Known gaps: extracted modules that need a vendor API client (HubSpot,
|
|
502
|
+
ZoomInfo, and so on) come out as `NotImplementedError` stubs you fill in,
|
|
503
|
+
and a Restate adapter is planned.
|
|
400
504
|
Published on
|
|
401
505
|
PyPI as [`rote-cli`](https://pypi.org/project/rote-cli/) via tag-driven
|
|
402
506
|
Trusted Publishing ([docs/releasing.md](docs/releasing.md)).
|
|
@@ -416,7 +520,7 @@ measure your own workflow instead of reading someone else's, use
|
|
|
416
520
|
```
|
|
417
521
|
rote/
|
|
418
522
|
├── docs/ agent-runtime · mcp-client · mcp-trigger · releasing
|
|
419
|
-
├── skills/rote-compile/ the compiler agent (SKILL.md +
|
|
523
|
+
├── skills/rote-compile/ the compiler agent (SKILL.md + 6 reference files)
|
|
420
524
|
├── src/rote/
|
|
421
525
|
│ ├── cli.py rote compile / emit / eval / serve
|
|
422
526
|
│ ├── ir.py Pydantic IR models + load_pipeline
|
|
@@ -424,6 +528,7 @@ rote/
|
|
|
424
528
|
│ └── adapters/ dbos · temporal · python · cloudflare · dbos_ts · inngest
|
|
425
529
|
│ (+ _common / _py_common / _ts_common emit helpers)
|
|
426
530
|
├── examples/
|
|
531
|
+
│ ├── expense-audit/ free first run: working extracted code · fan-out · HITL gate
|
|
427
532
|
│ ├── bdr-outreach/ canonical: all 5 node kinds · IR baseline · run snapshots
|
|
428
533
|
│ ├── ops-report/ 100% roteness: zero LLM nodes + a HITL gate
|
|
429
534
|
│ ├── deal-monitor/ data-heavy: parallel waves · fan-out judges · template render
|
|
@@ -445,12 +550,16 @@ rote/
|
|
|
445
550
|
emitted code uses under `--backend mcp`: endpoint/credential
|
|
446
551
|
resolution and durable park-on-auth across every MCP-capable runtime
|
|
447
552
|
- [`docs/mcp-trigger.md`](docs/mcp-trigger.md): `rote register` +
|
|
448
|
-
`rote serve`: compiled pipelines as MCP tools (FastMCP
|
|
553
|
+
`rote serve`: compiled pipelines as MCP tools (FastMCP 4.x)
|
|
449
554
|
- [`docs/releasing.md`](docs/releasing.md): tag-driven PyPI Trusted
|
|
450
555
|
Publishing
|
|
451
556
|
- [`skills/rote-compile/`](skills/rote-compile/): the compiler's
|
|
452
|
-
`SKILL.md` and its
|
|
453
|
-
heuristics, IR schema, LLM-judge extraction
|
|
557
|
+
`SKILL.md` and its six rubric files (node kinds, crystallization
|
|
558
|
+
heuristics, IR schema, LLM-judge extraction, implementation, eval
|
|
559
|
+
estimates)
|
|
560
|
+
- [`examples/expense-audit/`](examples/expense-audit/): the free first
|
|
561
|
+
run: a hand-compiled pipeline with working extracted code that
|
|
562
|
+
`rote run` completes on python, dbos, and temporal with no credentials
|
|
454
563
|
- [`examples/bdr-outreach/`](examples/bdr-outreach/): the canonical
|
|
455
564
|
skill, its ground-truth IR, and snapshotted real compiler runs
|
|
456
565
|
- [`examples/ops-report/`](examples/ops-report/): the 100%-roteness
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Example: Expense Audit
|
|
2
|
+
|
|
3
|
+
The **free first run**. A month-end expense audit skill whose compiled
|
|
4
|
+
pipeline ships with *working* extracted code, so `rote emit` + `rote run`
|
|
5
|
+
complete end to end with no API key, no subscription, and no tokens. The
|
|
6
|
+
other examples show what compilation produces against real vendor APIs.
|
|
7
|
+
Their extracted modules are `NotImplementedError` stubs until someone
|
|
8
|
+
writes the HubSpot or Gmail client. This one shows the result actually
|
|
9
|
+
running.
|
|
10
|
+
|
|
11
|
+
The work is all arithmetic and rules: convert sixteen expense lines from
|
|
12
|
+
five currencies at the finance month-end rates, find duplicates and late
|
|
13
|
+
submissions, check every line against the policy's caps and receipt
|
|
14
|
+
rules, auto-approve clean reports, and file the audit for payroll. Run
|
|
15
|
+
as an agent, every month re-reads the policy and re-derives the same
|
|
16
|
+
rules from prose. Compiled, it is a few milliseconds of Python and one
|
|
17
|
+
human decision.
|
|
18
|
+
|
|
19
|
+
## What it demonstrates
|
|
20
|
+
|
|
21
|
+
- **A parallel entry wave.** `convert_to_usd`, `find_duplicates`, and
|
|
22
|
+
`find_late_submissions` all read the raw export and nothing else, so
|
|
23
|
+
they run concurrently (a thread pool on python, `asyncio.gather` on
|
|
24
|
+
temporal, steps enqueued on a DBOS queue on dbos).
|
|
25
|
+
- **`fan_out`.** `audit_report` runs once per employee report, five
|
|
26
|
+
times on the sample batch, and the results come back as a list in
|
|
27
|
+
input order.
|
|
28
|
+
- **`mandatory: true`.** The duplicate and late checks, which SKILL.md
|
|
29
|
+
marks MANDATORY, are unconditional code. They can't be skipped on a
|
|
30
|
+
month that "looks small enough to eyeball".
|
|
31
|
+
- **Rules lifted out of prose.** Category caps, the $25.00 receipt
|
|
32
|
+
threshold, the $2,500.00 report limit, the 60-day window, and the FX
|
|
33
|
+
table live as constants in `expected/extracted/`, not prompt text.
|
|
34
|
+
- **`hitl_gate`.** `controller_review` parks the durable workflow until
|
|
35
|
+
the controller approves or rejects each review item, then the report
|
|
36
|
+
is filed as final.
|
|
37
|
+
- **No `llm_judge`.** Nothing in the skill needs judgment once the
|
|
38
|
+
policy is written down, so the compiled pipeline makes zero model
|
|
39
|
+
calls. Roteness 1.0.
|
|
40
|
+
|
|
41
|
+
## Two IRs, one pipeline
|
|
42
|
+
|
|
43
|
+
| File | Gate | Runs on |
|
|
44
|
+
| --- | --- | --- |
|
|
45
|
+
| `expected/pipeline.yaml` | `controller_review` | `dbos`, `temporal` (and the TS runtimes for emission) |
|
|
46
|
+
| `expected/pipeline-draft.yaml` | none | `python` too |
|
|
47
|
+
|
|
48
|
+
The `python` runtime refuses pipelines with a `hitl_gate`: a plain script
|
|
49
|
+
can't durably park for days waiting on a human. `pipeline-draft.yaml` is
|
|
50
|
+
the same pipeline with the gate removed, so `file_report` files a
|
|
51
|
+
**draft** with every review item pending. A test
|
|
52
|
+
(`tests/test_expense_audit_example.py`) pins that the two IRs differ by
|
|
53
|
+
exactly the gate and its edges.
|
|
54
|
+
|
|
55
|
+
## Run it
|
|
56
|
+
|
|
57
|
+
From the repo root, after `uv tool install 'rote-cli[dbos]'` (or `pip install` into a venv):
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
# Plain Python, no gate: a draft report.
|
|
61
|
+
rote emit examples/expense-audit/expected/pipeline-draft.yaml --runtime python --out /tmp/expense-audit
|
|
62
|
+
rote run /tmp/expense-audit --input examples/expense-audit/input.json
|
|
63
|
+
|
|
64
|
+
# DBOS, with the controller's decision delivered to the parked gate.
|
|
65
|
+
rote emit examples/expense-audit/expected/pipeline.yaml --out /tmp/expense-audit-dbos
|
|
66
|
+
rote run /tmp/expense-audit-dbos --input examples/expense-audit/input.json \
|
|
67
|
+
--signal controller_review='{"reviewer": "Priya Shah", "approved_ids": ["EXP-2403", "EXP-2413", "EXP-2420"], "rejected_ids": ["EXP-2410"]}'
|
|
68
|
+
|
|
69
|
+
# Temporal: needs the temporal extra and a release newer than 0.14.0. The first run downloads the dev server.
|
|
70
|
+
rote emit examples/expense-audit/expected/pipeline.yaml --runtime temporal --out /tmp/expense-audit-temporal
|
|
71
|
+
rote run /tmp/expense-audit-temporal --input examples/expense-audit/input.json \
|
|
72
|
+
--signal controller_review='{"reviewer": "Priya Shah", "approved_ids": ["EXP-2403", "EXP-2413", "EXP-2420"], "rejected_ids": ["EXP-2410"]}'
|
|
73
|
+
|
|
74
|
+
# The before/after scorecard (fetches live model prices, no inference).
|
|
75
|
+
rote eval examples/expense-audit/expected/pipeline.yaml
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The run prints the totals as JSON and writes the markdown report to
|
|
79
|
+
`reports/2026-09-expense-audit.md` under the app dir on every runtime.
|
|
80
|
+
Set `EXPENSE_AUDIT_REPORT_DIR` to put it somewhere else.
|
|
81
|
+
|
|
82
|
+
Expected results on `input.json`:
|
|
83
|
+
|
|
84
|
+
| | Draft (python) | Final (dbos, temporal) |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| Reimbursable | $2,395.59 | $2,990.98 |
|
|
87
|
+
| Pending review | $844.39 | $0.00 |
|
|
88
|
+
| Returned to employee | $64.17 | $313.17 |
|
|
89
|
+
| Batch total | $3,304.15 | $3,304.15 |
|
|
90
|
+
|
|
91
|
+
The review queue holds four review items (a meal over the cap, software
|
|
92
|
+
over the cap, a late line, entertainment without pre-approval) and two
|
|
93
|
+
blocks (a $25.97 ride with no receipt, a duplicate Lyft charge filed as
|
|
94
|
+
"lyft"). Blocks go back to the employee whatever the controller says. A
|
|
95
|
+
decision that names a block item fails the step with the ids to fix.
|
|
96
|
+
|
|
97
|
+
## Layout
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
examples/expense-audit/
|
|
101
|
+
├── README.md
|
|
102
|
+
├── input.json # the sample month export (2026-09)
|
|
103
|
+
├── eval-input.json # input.json plus the gate payload, for `rote eval --run`
|
|
104
|
+
├── scorecard.md # measured before/after (rote eval --run)
|
|
105
|
+
├── skill/
|
|
106
|
+
│ ├── SKILL.md # the source skill, as an agent would run it
|
|
107
|
+
│ └── references/
|
|
108
|
+
│ ├── expense-policy.md # caps, receipts, severities
|
|
109
|
+
│ ├── fx-rates.md # finance month-end rates
|
|
110
|
+
│ └── report-format.md # the layout payroll reads
|
|
111
|
+
└── expected/
|
|
112
|
+
├── pipeline.yaml # hand-compiled IR, with the controller gate
|
|
113
|
+
├── pipeline-draft.yaml # the same IR without the gate
|
|
114
|
+
├── eval.yaml # per-step agent-turn estimates for `rote eval`
|
|
115
|
+
└── extracted/ # working implementations, no stubs
|
|
116
|
+
├── fx.py
|
|
117
|
+
├── checks.py
|
|
118
|
+
├── audit.py
|
|
119
|
+
└── report.py
|
|
120
|
+
└── runs/
|
|
121
|
+
└── 2026-10-18-first-agent-run/ # a real `rote compile` of skill/, for comparison
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`expected/` is hand-authored, like the other examples' baselines.
|
|
125
|
+
|
|
126
|
+
## Measured, not estimated
|
|
127
|
+
|
|
128
|
+
`scorecard.md` is `rote eval --run --trials 2` on this example, with
|
|
129
|
+
`eval-input.json` (the batch plus the controller's decision). The agent
|
|
130
|
+
runs took 42.9 s and $0.22 each, and they disagreed on `report_path`.
|
|
131
|
+
The pipeline runs took 9.8 s, most of it DBOS startup, cost $0, and
|
|
132
|
+
produced identical output. The static estimate at the top of that file
|
|
133
|
+
(3.2 to 6.5 minutes before) is only a model of agent turns. Trust the
|
|
134
|
+
Measured section over it. To reproduce, emit the dbos app as above and
|
|
135
|
+
run this (it spends subscription tokens on the agent side):
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
rote eval examples/expense-audit/expected/pipeline.yaml --run --trials 2 \
|
|
139
|
+
--input examples/expense-audit/eval-input.json --runtime-dir /tmp/expense-audit-dbos
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## What the real compiler produced
|
|
143
|
+
|
|
144
|
+
`runs/2026-10-18-first-agent-run/` is a real `rote compile` of this
|
|
145
|
+
skill (claude driver, Sonnet, 46 turns, 12 minutes, $2.56). It agrees on
|
|
146
|
+
node kinds and on the three-way parallel entry wave, and it puts the
|
|
147
|
+
controller gate in the same place. It differs in three ways:
|
|
148
|
+
|
|
149
|
+
- It folded grouping and auditing into one `apply_policy_rules` node
|
|
150
|
+
instead of fanning `audit_report` out per employee.
|
|
151
|
+
- It marked 7 nodes mandatory where the hand-written IR marks 5.
|
|
152
|
+
- It left every extracted function as a `NotImplementedError` stub, even
|
|
153
|
+
though this skill's logic is plain Python.
|
|
154
|
+
|
|
155
|
+
The hand-written IR is the intended shape. The compiler gaps are being
|
|
156
|
+
fixed in a separate PR.
|