rote-cli 0.6.0__tar.gz → 0.8.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.
Files changed (44) hide show
  1. {rote_cli-0.6.0 → rote_cli-0.8.0}/PKG-INFO +26 -20
  2. {rote_cli-0.6.0 → rote_cli-0.8.0}/README.md +25 -19
  3. {rote_cli-0.6.0 → rote_cli-0.8.0}/skills/rote-graduate/references/eval-estimates.md +41 -20
  4. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/__init__.py +1 -1
  5. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/adapters/_common.py +8 -4
  6. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/cli.py +297 -10
  7. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/eval/__init__.py +4 -1
  8. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/eval/empirical.py +174 -3
  9. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/eval/estimate.py +55 -7
  10. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/eval/priors.py +86 -1
  11. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/eval/scorecard.py +3 -2
  12. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/eval/sidecar.py +30 -3
  13. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/graduator/__init__.py +84 -0
  14. rote_cli-0.8.0/src/rote/graduator/drivers/codex.py +285 -0
  15. rote_cli-0.6.0/src/rote/graduator/drivers/codex.py +0 -84
  16. {rote_cli-0.6.0 → rote_cli-0.8.0}/.gitignore +0 -0
  17. {rote_cli-0.6.0 → rote_cli-0.8.0}/LICENSE +0 -0
  18. {rote_cli-0.6.0 → rote_cli-0.8.0}/pyproject.toml +0 -0
  19. {rote_cli-0.6.0 → rote_cli-0.8.0}/skills/rote-graduate/SKILL.md +0 -0
  20. {rote_cli-0.6.0 → rote_cli-0.8.0}/skills/rote-graduate/references/crystallization-heuristics.md +0 -0
  21. {rote_cli-0.6.0 → rote_cli-0.8.0}/skills/rote-graduate/references/ir-schema.md +0 -0
  22. {rote_cli-0.6.0 → rote_cli-0.8.0}/skills/rote-graduate/references/llm-judge-extraction.md +0 -0
  23. {rote_cli-0.6.0 → rote_cli-0.8.0}/skills/rote-graduate/references/node-kinds.md +0 -0
  24. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/adapters/__init__.py +0 -0
  25. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/adapters/_py_common.py +0 -0
  26. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/adapters/_ts_common.py +0 -0
  27. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/adapters/cloudflare.py +0 -0
  28. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/adapters/dbos.py +0 -0
  29. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/adapters/dbos_ts.py +0 -0
  30. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/adapters/inngest.py +0 -0
  31. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/adapters/python.py +0 -0
  32. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/adapters/temporal.py +0 -0
  33. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/eval/pricing.py +0 -0
  34. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/eval/tokens.py +0 -0
  35. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/graduator/drivers/__init__.py +0 -0
  36. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/graduator/drivers/anthropic_api.py +0 -0
  37. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/graduator/drivers/claude.py +0 -0
  38. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/graduator/update.py +0 -0
  39. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/ir.py +0 -0
  40. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/serve/__init__.py +0 -0
  41. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/serve/backends.py +0 -0
  42. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/serve/registry.py +0 -0
  43. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/serve/server.py +0 -0
  44. {rote_cli-0.6.0 → rote_cli-0.8.0}/src/rote/skill_source.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rote-cli
3
- Version: 0.6.0
3
+ Version: 0.8.0
4
4
  Summary: Graduate fuzzy AI skills into deterministic, reliable workflows
5
5
  Project-URL: Homepage, https://github.com/trevhud/rote
6
6
  Project-URL: Repository, https://github.com/trevhud/rote
@@ -326,17 +326,16 @@ subscription path.
326
326
 
327
327
  ## Status
328
328
 
329
- `rote` is **pre-1.0**. The end-to-end flow works on the BDR example, and
330
- all six adapters are validated by a `slow`-marked e2e suite that runs the
331
- emitted code against real runtimes (DBOS over SQLite, Temporal's
332
- time-skipping server, the TypeScript targets compiled with `tsc --noEmit`
333
- and driven through both HITL gates on live dev servers, the plain-Python
334
- script as a subprocess, and the MCP server over real stdio). The fast
335
- suite (`pytest tests/`) makes no real API calls; the integration suite is
336
- `pytest tests/ -m slow`.
337
-
338
- Known gaps: the `codex` driver is a stub (`is_available` works, `run`
339
- isn't implemented), the extracted modules are `NotImplementedError` stubs
329
+ `rote` is **pre-1.0**. The end-to-end flow works on the BDR example. The
330
+ fast suite (`pytest tests/`) makes no real API calls and is what CI runs
331
+ on every push, alongside a Python e2e (DBOS over SQLite + the MCP server
332
+ over real stdio). Each adapter also has a `slow`-marked e2e that runs its
333
+ emitted code against the real runtime (Temporal's time-skipping server,
334
+ the TypeScript targets via `tsc --noEmit` and live dev servers, the
335
+ plain-Python subprocess); those need a Node toolchain / Docker, so they
336
+ run locally with `pytest tests/ -m slow`, not in CI.
337
+
338
+ Known gaps: the extracted modules are `NotImplementedError` stubs
340
339
  you fill in with real API-client code, a Restate adapter is planned, and
341
340
  `fan_out` nodes currently receive the whole upstream list in one
342
341
  invocation (per-element dispatch is a planned enhancement). Published on
@@ -357,7 +356,10 @@ rote/
357
356
  │ ├── graduator/ orchestrator + drivers/ (claude · codex · anthropic_api)
358
357
  │ └── adapters/ dbos · temporal · python · cloudflare · dbos_ts · inngest
359
358
  │ (+ _common / _py_common / _ts_common emit helpers)
360
- ├── examples/bdr-outreach/ source skill · hand-drafted IR baseline · run snapshots
359
+ ├── examples/
360
+ │ ├── bdr-outreach/ canonical: all 5 node kinds · IR baseline · run snapshots
361
+ │ ├── ops-report/ 100% roteness: zero LLM nodes + a HITL gate
362
+ │ └── deal-monitor/ data-heavy: parallel waves · fan-out judges · template render
361
363
  └── tests/ fast + slow suites (pytest -m slow)
362
364
  ```
363
365
 
@@ -377,6 +379,12 @@ rote/
377
379
  heuristics, IR schema, LLM-judge extraction)
378
380
  - [`examples/bdr-outreach/`](examples/bdr-outreach/) — the canonical
379
381
  skill, its ground-truth IR, and snapshotted real graduator runs
382
+ - [`examples/ops-report/`](examples/ops-report/) — the 100%-roteness
383
+ archetype: every step deterministic, one durable HITL gate, zero LLM
384
+ nodes after graduation
385
+ - [`examples/deal-monitor/`](examples/deal-monitor/) — the data-heavy
386
+ archetype: parallel entry waves, fan-out judges, and a template render
387
+ replacing per-run LLM-generated HTML
380
388
 
381
389
  ---
382
390
 
@@ -384,19 +392,17 @@ rote/
384
392
 
385
393
  In rough priority order:
386
394
 
387
- 1. **`CodexDriver` implementation** same shape as `ClaudeDriver` but
388
- spawning `codex exec`; unlocks ChatGPT subscribers.
389
- 2. **Re-graduate BDR end-to-end with `signature_spec`** — the bundled IR
395
+ 1. **Re-graduate BDR end-to-end with `signature_spec`** — the bundled IR
390
396
  was hand-extended with structured schemas; the rubric now teaches the
391
397
  field, but no real run has produced one yet.
392
- 3. **Pre-filter as a `pure_function` node** — today hard thresholds are
398
+ 2. **Pre-filter as a `pure_function` node** — today hard thresholds are
393
399
  lifted into a judge's `forward()`, which works for Temporal but not
394
400
  Cloudflare; a separate node makes the short-circuit uniform.
395
- 4. **More example skills** — BDR is one shape; research-heavy,
401
+ 3. **More example skills** — BDR is one shape; research-heavy,
396
402
  retrieval-heavy, and code-review skills stress the IR differently.
397
- 5. **`fan_out` per-element dispatch** — currently the whole upstream list
403
+ 4. **`fan_out` per-element dispatch** — currently the whole upstream list
398
404
  arrives in one invocation.
399
- 6. **The graduator graduating itself** — `rote-graduate` is a SKILL.md;
405
+ 5. **The graduator graduating itself** — `rote-graduate` is a SKILL.md;
400
406
  pointing `rote graduate` at it should crystallize its rubric-grade
401
407
  pieces and leave only the genuinely fuzzy judgments in the loop.
402
408
 
@@ -280,17 +280,16 @@ subscription path.
280
280
 
281
281
  ## Status
282
282
 
283
- `rote` is **pre-1.0**. The end-to-end flow works on the BDR example, and
284
- all six adapters are validated by a `slow`-marked e2e suite that runs the
285
- emitted code against real runtimes (DBOS over SQLite, Temporal's
286
- time-skipping server, the TypeScript targets compiled with `tsc --noEmit`
287
- and driven through both HITL gates on live dev servers, the plain-Python
288
- script as a subprocess, and the MCP server over real stdio). The fast
289
- suite (`pytest tests/`) makes no real API calls; the integration suite is
290
- `pytest tests/ -m slow`.
291
-
292
- Known gaps: the `codex` driver is a stub (`is_available` works, `run`
293
- isn't implemented), the extracted modules are `NotImplementedError` stubs
283
+ `rote` is **pre-1.0**. The end-to-end flow works on the BDR example. The
284
+ fast suite (`pytest tests/`) makes no real API calls and is what CI runs
285
+ on every push, alongside a Python e2e (DBOS over SQLite + the MCP server
286
+ over real stdio). Each adapter also has a `slow`-marked e2e that runs its
287
+ emitted code against the real runtime (Temporal's time-skipping server,
288
+ the TypeScript targets via `tsc --noEmit` and live dev servers, the
289
+ plain-Python subprocess); those need a Node toolchain / Docker, so they
290
+ run locally with `pytest tests/ -m slow`, not in CI.
291
+
292
+ Known gaps: the extracted modules are `NotImplementedError` stubs
294
293
  you fill in with real API-client code, a Restate adapter is planned, and
295
294
  `fan_out` nodes currently receive the whole upstream list in one
296
295
  invocation (per-element dispatch is a planned enhancement). Published on
@@ -311,7 +310,10 @@ rote/
311
310
  │ ├── graduator/ orchestrator + drivers/ (claude · codex · anthropic_api)
312
311
  │ └── adapters/ dbos · temporal · python · cloudflare · dbos_ts · inngest
313
312
  │ (+ _common / _py_common / _ts_common emit helpers)
314
- ├── examples/bdr-outreach/ source skill · hand-drafted IR baseline · run snapshots
313
+ ├── examples/
314
+ │ ├── bdr-outreach/ canonical: all 5 node kinds · IR baseline · run snapshots
315
+ │ ├── ops-report/ 100% roteness: zero LLM nodes + a HITL gate
316
+ │ └── deal-monitor/ data-heavy: parallel waves · fan-out judges · template render
315
317
  └── tests/ fast + slow suites (pytest -m slow)
316
318
  ```
317
319
 
@@ -331,6 +333,12 @@ rote/
331
333
  heuristics, IR schema, LLM-judge extraction)
332
334
  - [`examples/bdr-outreach/`](examples/bdr-outreach/) — the canonical
333
335
  skill, its ground-truth IR, and snapshotted real graduator runs
336
+ - [`examples/ops-report/`](examples/ops-report/) — the 100%-roteness
337
+ archetype: every step deterministic, one durable HITL gate, zero LLM
338
+ nodes after graduation
339
+ - [`examples/deal-monitor/`](examples/deal-monitor/) — the data-heavy
340
+ archetype: parallel entry waves, fan-out judges, and a template render
341
+ replacing per-run LLM-generated HTML
334
342
 
335
343
  ---
336
344
 
@@ -338,19 +346,17 @@ rote/
338
346
 
339
347
  In rough priority order:
340
348
 
341
- 1. **`CodexDriver` implementation** same shape as `ClaudeDriver` but
342
- spawning `codex exec`; unlocks ChatGPT subscribers.
343
- 2. **Re-graduate BDR end-to-end with `signature_spec`** — the bundled IR
349
+ 1. **Re-graduate BDR end-to-end with `signature_spec`** — the bundled IR
344
350
  was hand-extended with structured schemas; the rubric now teaches the
345
351
  field, but no real run has produced one yet.
346
- 3. **Pre-filter as a `pure_function` node** — today hard thresholds are
352
+ 2. **Pre-filter as a `pure_function` node** — today hard thresholds are
347
353
  lifted into a judge's `forward()`, which works for Temporal but not
348
354
  Cloudflare; a separate node makes the short-circuit uniform.
349
- 4. **More example skills** — BDR is one shape; research-heavy,
355
+ 3. **More example skills** — BDR is one shape; research-heavy,
350
356
  retrieval-heavy, and code-review skills stress the IR differently.
351
- 5. **`fan_out` per-element dispatch** — currently the whole upstream list
357
+ 4. **`fan_out` per-element dispatch** — currently the whole upstream list
352
358
  arrives in one invocation.
353
- 6. **The graduator graduating itself** — `rote-graduate` is a SKILL.md;
359
+ 5. **The graduator graduating itself** — `rote-graduate` is a SKILL.md;
354
360
  pointing `rote graduate` at it should crystallize its rubric-grade
355
361
  pieces and leave only the genuinely fuzzy judgments in the loop.
356
362
 
@@ -25,6 +25,13 @@ Ground rules:
25
25
  (agents batch parallel calls). If it says "repeat until you have 25
26
26
  qualified contacts", estimate the realistic iteration count from any
27
27
  hints (quota sizes, batch sizes, typical yields), not the worst case.
28
+ - **Steps that repeat per item MUST declare `iterations`.** A "process
29
+ each row" step costs its per-row turns *times the row count* — this
30
+ is almost always the dominant term, and flattening it understates the
31
+ whole run by 5–20×. Put the per-iteration turns in `estimated_turns`
32
+ and the realistic item-count range in `iterations` (page sizes,
33
+ pagination labels, batch quotas, and filter windows in the skill text
34
+ are your evidence). The loader multiplies them; never pre-multiply.
28
35
  - **Include the overhead the prose hides.** Reading reference files,
29
36
  re-checking rubric sections, correcting malformed tool calls — a
30
37
  realistic agent spends turns on these. A step whose happy path is 2
@@ -54,9 +61,14 @@ steps:
54
61
  phase: "1"
55
62
  estimated_turns: {low: 1, high: 2}
56
63
  estimated_tool_calls: {low: 4, high: 4}
57
- totals: # optional omit if the sum is right
58
- low: 28
59
- high: 45
64
+ - description: "Push each eligible invoice and capture its sent date"
65
+ node_id: process_invoices_loop
66
+ phase: "Processing"
67
+ estimated_turns: {low: 3, high: 6} # PER ROW —
68
+ iterations: {low: 20, high: 90} # — × realistic row count
69
+ totals: # optional — omit if the sum is right;
70
+ low: 88 # must be WHOLE-RUN (post-iteration)
71
+ high: 602 # when any step declares iterations
60
72
  notes: >-
61
73
  Lead generation dominates: the loop re-queries ZoomInfo with adjusted
62
74
  filters until the quota is met; typical campaigns converge in 3-5
@@ -72,30 +84,39 @@ Field reference:
72
84
  | `steps[].description` | yes | The step, in the skill's own vocabulary |
73
85
  | `steps[].node_id` | no | IR node id, only when the mapping is clean 1:1 |
74
86
  | `steps[].phase` | no | Source skill phase, same convention as the IR |
75
- | `steps[].estimated_turns` | yes | `{low, high}` agent turns |
87
+ | `steps[].estimated_turns` | yes | `{low, high}` agent turns (per iteration when `iterations` is set) |
76
88
  | `steps[].estimated_tool_calls` | no | `{low, high}` tool calls, when distinct from turns |
77
- | `totals` | no | Whole-run `{low, high}`; omit to mean "sum the steps" |
89
+ | `steps[].iterations` | when the step repeats | `{low, high}` repeats per run (rows, pages, items); whole-run cost = turns × iterations |
90
+ | `totals` | no | Whole-run `{low, high}`; omit to mean "sum the steps (× iterations)" |
78
91
  | `notes` | no | The one paragraph a reader needs to trust the numbers |
79
92
 
80
93
  Validated by `rote.eval.sidecar.EvalEstimates` — if you can run Python,
81
94
  `load_eval_estimates("eval.yaml")` is the dry check, same pattern as
82
95
  `load_pipeline`.
83
96
 
84
- ## Calibration anchors (BDR reference points)
85
-
86
- From real runs of BDR-scale skills (7 phases, ~20 distinct steps,
87
- heavy tool use):
88
-
89
- - Whole-run turn counts landed in the **30–57 turn** range.
90
- - Wall clock was **~13 seconds per turn** including tool execution.
91
- - A single fixed API call step: 1–2 turns. A batch-and-review step:
92
- 2–4. A bounded search loop: 6–15. A genuinely open research step:
93
- 4–10.
94
-
95
- If your step estimates sum to something wildly outside the anchor range
96
- for a comparably sized skill, re-examine before writing — either the
97
- skill really is bigger/smaller (fine, say so in `notes`) or a step
98
- estimate is off.
97
+ ## Calibration anchors (measured production runs)
98
+
99
+ Whole-run turn counts are regime-dependent anchor to the skill's
100
+ *shape*, not to a universal range:
101
+
102
+ - **Sequential tool-heavy skills** (BDR-scale: 7 phases, ~20 distinct
103
+ steps, no per-item loop): **30–57 turns** measured. ~13 seconds per
104
+ turn including tool execution.
105
+ - **Per-item loop skills** (browser automation over a table, per-record
106
+ processing): measured production runs landed at **184 and 730 turns**
107
+ — the loop multiplier dominates everything else in the file. If a
108
+ skill says "for each row…", expect hundreds of turns, not tens, and
109
+ express it via `iterations`, never by inflating per-step turns.
110
+ - Per-step anchors: a single fixed API call step: 1–2 turns. A
111
+ batch-and-review step: 2–4. A bounded search loop: 6–15. A genuinely
112
+ open research step: 4–10. One iteration of a per-row browser cycle
113
+ (locate, act, confirm, refresh): 2–6.
114
+
115
+ If your estimate lands wildly outside the anchor for the skill's shape,
116
+ re-examine before writing — either the skill really is bigger/smaller
117
+ (fine, say so in `notes`) or a step estimate is off. A per-item skill
118
+ whose whole-run total sums to under ~100 turns almost certainly has a
119
+ missing `iterations` block.
99
120
 
100
121
  ## What NOT to do
101
122
 
@@ -1,3 +1,3 @@
1
1
  """rote — graduate fuzzy AI skills into deterministic workflows."""
2
2
 
3
- __version__ = "0.6.0"
3
+ __version__ = "0.8.0"
@@ -223,13 +223,17 @@ def _pipeline_hash(pipeline: Pipeline) -> str:
223
223
  # Hash the full validated contents: node wiring, inputs, retries, and
224
224
  # edges all participate, so any regeneration that changes behavior gets
225
225
  # a new workflow type — name/version/counts alone miss rewires.
226
- # Provenance (``Node.source``) is excluded: it's tooling metadata, and
227
- # annotating where a node came from must not re-version in-flight
228
- # workflows whose behavior didn't change.
226
+ # Provenance is excluded it's tooling metadata, and annotating where
227
+ # a pipeline came from must not re-version in-flight workflows whose
228
+ # behavior didn't change. That covers ``Node.source`` (which SKILL.md
229
+ # section produced a node) and ``source_skill`` (where the skill bundle
230
+ # lived): the latter is a filesystem path the graduator re-points per
231
+ # output location, so hashing it would mint a new workflow type on
232
+ # every re-graduation to a different directory.
229
233
  payload = pipeline.model_dump_json(
230
234
  by_alias=True,
231
235
  exclude_none=True,
232
- exclude={"nodes": {"__all__": {"source"}}},
236
+ exclude={"source_skill": True, "nodes": {"__all__": {"source"}}},
233
237
  )
234
238
  return hashlib.sha256(payload.encode()).hexdigest()[:8]
235
239