Code routines
Routines whose work is done by scripts. The model runs exact commands and reports; scripts fetch, filter, count and write. When to choose one, the rules a script follows, and weekly-marketing-report in full.
What it is
A code routine does its work in scripts under lib/, plus the bundled routineos.py. The prompt is a list of exact commands. The model runs them, reads their output, and writes a short report. The model’s own work shrinks to what no script can do: building the run input from a script’s output and phrasing the Inbox.
Nothing in routine.toml says “this is a code routine”. It is a design pattern: a prompt made of commands, and, in a pack, stages with kind = "code". See Routine kinds.
Why it exists
Counting, comparing, filtering and deduplicating are cheap and exact in code, and slow, costly and occasionally wrong in a model. A model asked for a percentage change can round differently on two runs; a script cannot. A routine that is mostly arithmetic should spend almost no tokens, and give the same answer for the same input every time.
Code also makes a routine testable on your machine. You can run lib/week_over_week.py against two JSON files before any cloud run and see exactly what the run will see.
When to choose it
Choose code for a step when:
- The answer is arithmetic, a comparison or a filter, and must be the same on every run.
- The input is structured: an export, an API response, a CSV, JSON from a connector.
- A person will ask “why did this number change?” and the answer must be traceable.
Do not choose code when:
- The step needs reading or judgement: “is this a real lead”, “would a visitor notice this page is broken”. Make it a hybrid.
- The step is a style or a rubric applied to prose. Use a skill.
- The script would need a host the run’s network blocks, or a runtime the cloud run does not have. See Compute sandboxes.
Use cases
Each code-heavy shipped template keeps its arithmetic in one small standard-library script:
| Template | Script | What it computes | Maturity |
|---|---|---|---|
weekly-marketing-report |
lib/week_over_week.py |
Change per channel and metric, this week against last | Unpiloted |
candidate-shortlist |
lib/score_rubric.py |
A rubric score per candidate | Unpiloted |
engineering-digest |
lib/stale_items.py |
Items with no recent activity | Unpiloted |
invoice-exceptions |
lib/invoice_checks.py |
Invoices that break a rule | Unpiloted |
support-gap-digest |
lib/group_questions.py |
Questions grouped by topic | Unpiloted |
“Unpiloted” means no real run has checked the template yet. See the catalog.
How it works
Where scripts live
- Scripts live in
lib/next toroutine.tomland are delivered with the repo the routine clones. A script on your machine does not exist in a cloud run. routineos.py(underskills/routineos/scripts/) doeslearnandwrite. Every routine’s store write goes through it, so the same rules hold as for any other routine.lib/budget.py(copied fromassets/lib/budget.pywhen you scaffold withnew --app) is the budget check a run calls first.
How the prompt calls a script
The prompt gives each step as a runnable command with every flag, never “call function X from file Y”. references/prompt-craft.md records why: a pilot run could not import budget under python3 -I, because isolated mode ignores the working directory, and the run typed the state by hand instead (registry python-isolated-ignores-cwd, pilot-verified). Run each script by its path.
From templates/weekly-marketing-report/prompt.md, step 2:
Run
python3 lib/week_over_week.py --this this-week.json --last last-week.json. Use its output as the only source of change figures; do not compute percentages yourself.
The second sentence matters as much as the first. Without it, a model may restate a change it worked out itself.
What a script follows
The shipped scripts share a shape. Follow it in your own:
- Standard library only. No
pip install, so no setup script is needed. - JSON in, JSON out. Read files named on the command line; print JSON to stdout.
- Fail loudly, exit 2. On bad input, print
error: ...to stderr and exit 2. Never echo the input values. - No store access. A script returns results to the run;
routineos.py writeproduces the store batches. A script reaching the store directly from a run is owner-reported, not re-tested (registryscript-writes-store, unverified). If it fails, the budget check and the final health write become the first and last prompt steps, and the Desk labels the cap “prompt-enforced, not guaranteed”. - No secrets. A script that calls an API reaches it through a host with a network secret, never a key in a file or an environment variable. See Compute sandboxes.
The budget check
lib/budget.py has two commands:
python3 lib/budget.py decide --max-runs 1 --state budget.json
python3 lib/budget.py record --steps 4 --state budget.json --out budget-next.json
decide prints {"allow": ..., "runs": ..., "reason": ...}, for example "daily cap reached (1 of 1 runs)". record writes the new state/budget document to --out, ready for an ArtifactData set. --state - reads the state from stdin; omit --state when the document does not exist yet.
In a pack
Packs (since 0.6.0) declare a code step as a stage with kind = "code" and a command. pack validate holds the command to a narrow shape:
- One plain line that starts
python3, with no shell operators (pipes,;,&&, redirects). - The script comes straight after
python3, so-mand-care refused. - The script is
skills/routineos/scripts/routineos.pyor alib/*.pyfile the pack ships: “command names lib/x.py, which is not in files”.
Install never runs pack code. It writes the files and prints a connector report.
How it compares
| Code | Skill | Workflow | Hybrid | |
|---|---|---|---|---|
| Who does the work | Scripts | The model, guided by a skill | Whatever each stage says | The model reads, code rules |
| Same answer every run | Yes | No | Per stage | For the code half |
| Testable on your machine | Yes, with sample JSON | Partly | Per code stage | The code half |
| Token cost | Lowest | Medium | Depends on stages | Medium |
Few routines are pure code: something has to fetch the numbers and phrase the result. Most are code-heavy hybrids, like weekly-marketing-report below.
Worked example
weekly-marketing-report compares this week’s marketing numbers with last week’s and checks a few key pages, every Sunday at 21:00 UTC (schedule = "0 21 * * 0", type watch). It is unpiloted.
-
Install it with its script:
python3 skills/routineos/scripts/routineos.py pack install site/templates/weekly-marketing-report.json --dir weekly-marketing-reportThe folder gets
lib/week_over_week.pywith the routine files. The connector report lists an analytics MCP connector (read, required) and the built-in web tool (read, optional). -
Read the stages. Two of five are the model; three are code:
Stage Kind Does collectmodel Reads this week’s and last week’s totals from the analytics connector into this-week.jsonandlast-week.jsoncomparecode python3 lib/week_over_week.py --this this-week.json --last last-week.jsonauditmodel Opens each page in pages_to_auditand records issues seenlearncode routineos.py learnwritecode routineos.py write; writes runs, entities and inbox -
Test the script before any run. With
this-week.json:{"search": {"clicks": 1320, "spend": 410.5}, "email": {"opens": 900}}and
last-week.json:{"search": {"clicks": 1100, "spend": 410.5}, "email": {"opens": 0}}python3 lib/week_over_week.py --this this-week.json --last last-week.jsonprints:{"email": {"opens": {"value": 900, "previous": 0, "change_pct": null}}, "search": {"clicks": {"value": 1320, "previous": 1100, "change_pct": 20.0}, "spend": {"value": 410.5, "previous": 410.5, "change_pct": 0.0}}}change_pctisnullwhen last week was 0 or absent. A non-number such as"lots"stops it witherror: --this: every metric must be a numberand exit code 2. -
The model records one
metricper line of output and writes analertfor each change of at leastchange_alert_pct(default 20) up or down, insidemax_inbox_per_run. With the default,search/clicksat 20.0 alerts;email/opensdoes not, because it has no change figure. -
Tune it on the Desk. A person who finds 20% too noisy raises
change_alert_pctto 30. The next run’s alerts change; the script and the prompt do not.
A pure-code variant is possible when the numbers come from a file rather than a connector. Hiring Signals, for example, could read a job board’s export with a script instead of searching:
python3 lib/budget.py decide --max-runs 1 --state budget.json
python3 lib/fetch_postings.py --out postings.json
python3 skills/routineos/scripts/routineos.py learn --docs snapshot.json --run run.json --out learn
python3 skills/routineos/scripts/routineos.py write --docs snapshot.json --run run.json --out batches
python3 lib/budget.py record --steps 4 --state budget.json --out budget-next.json
lib/fetch_postings.py is illustrative; write your own. The model’s only work is to build run.json from postings.json and phrase the Inbox.
Common pitfalls
- A script the cloud run cannot reach. Keep it in the repo, not on your machine.
- Scaffolding with
new --from-templateand expectinglib/. It copiesroutine.toml,prompt.md,README.mdanddefinition.jsononly. Usepack installto get the template’s scripts. - Calling a function instead of a script. “Call
decide()frombudget.py” failed underpython3 -Iin a pilot. Run the script by path with every flag. - Letting the model redo the arithmetic. Say the script’s output is the only source of the figure.
- Treating a failed source as zero. A source that did not load is reported as dropped, and the run is
partial. - Network from a script. A script’s fetch goes through the run’s network; check the host is reachable at the environment’s level. See Compute sandboxes.
- Third-party packages. A
pip installneeds a setup script, and setup scripts have their own rules (registrysetup-script-rules). Standard library first. - Forcing judgement into code. “Is this a real lead” is not a regex. Make it a hybrid.