Skip to content

Cost governance

mini-ork meters every dispatch per model at list price and enforces a spend cap in two places: once before a run starts, and once around every individual dispatch. This page explains the levers, what happens when they trip, and how to check what a run actually cost.

The one lever that matters

Variable Default Effect
MO_DAILY_BUDGET_USD 50 Rolling 24-hour USD spend cap

The window is rolling, not a calendar day. The gate sums llm_calls.cost_usd over the trailing 24 hours and compares it with the cap. Budget returns when older calls age out — there is no midnight reset.

Raise it for one session by exporting it before the run:

MO_DAILY_BUDGET_USD=400 mini-ork run framework-edit kickoff.md

The budget: block in agents.yaml is descriptive

agents.yaml accepts a budget: block (daily_cap_usd, per_run_usd, …), and the Python SDK client prints it, but no gate reads it. MO_DAILY_BUDGET_USD is the enforced lever. Treat the YAML block as documentation of intent, not a control.

Two enforcement points

flowchart TD
    A[mini-ork run] --> B{trailing 24h spend<br/>>= MO_DAILY_BUDGET_USD?}
    B -- yes --> C[exit 75<br/>verdict: blocked<br/>blocked_reason: cost_limit]
    B -- no --> D[execute nodes]
    D --> E{per-dispatch check}
    E -- over cap --> F[refuse dispatch<br/>rc 42<br/>cost_limit row written]
    E -- under cap --> G[dispatch to lane]

Before the run

mini-ork run refuses to dispatch anything, exits 75 (EX_TEMPFAIL), and records a blocked verdict with blocked_reason: cost_limit.

This is deliberate. An in-run trip used to fail every node within seconds, and that looked identical to a model that tried and failed. Saying it once, before anything is spent, is the honest failure.

During the run

mini_ork.dispatch.llm_dispatch.cost_circuit_open refuses each individual dispatch, writes a cost_limit refusal row, and returns 42. The scheduler re-checks the same cap before every epic admission.

If the state database is missing or unreadable, the gate fails open — absent spend reads as $0.00. A broken database never blocks work.

What a trip looks like

A cost cap trip pauses the run rather than destroying it. Resume it after raising the cap:

MO_DAILY_BUDGET_USD=150 mini-ork board resume <run-id>

board resume is the documented verb for a cost-paused run. The run continues from its last checkpoint; succeeded nodes are not re-executed.

Premium feature knobs

Some features carry a cost multiplier above 1.5× the baseline. Those are premium: they are off for a run unless you opt in explicitly.

Gate Default Behaviour
MO_ACCEPT_PREMIUM unset (off) 1 allows premium knobs; otherwise they are dropped
multiplier — A feature above 1.5× is premium; baseline features keep their defaults

The check runs server-side in the launch path (mini_ork.web.control.launch_run), not in a UI, and reports what it dropped as blocked_premium. A caller cannot smuggle a premium knob past it by posting raw environment to the run API.

When a premium knob is dropped, the run still starts — it just runs the baseline path. That is why a stray click cannot triple a bill.

Per-run orchestrator budget

The detached per-run watcher (mini-ork orchestrator watch <run_id>) makes no model calls but watches a node's cost and duration between boundaries.

Variable Default Effect
MO_RUN_ORCHESTRATOR off Enables the per-run orchestrator
MO_ORCH_BUDGET_USD — Budget the orchestrator shares with the run
MO_WATCH_COST_USD 1.0 Per-node cost overrun threshold
MO_WATCH_STALL_SECONDS 300 Stall detection
MO_WATCH_LONG_NODE_SECONDS 900 Long-node detection

Overruns are written as cost_overrun events in run_events. With MO_ORCH_WATCH_AUTONOMY set to steer (the default), the watcher can steer the run rather than only record the event.

Recursive self-improvement caps

The recursive loop has its own budget gates, separate from the daily cap:

Variable Default Effect
MO_RECURSION_BUDGET_CAP_TOTAL_USD 150.0 Total spend across iterations
MO_RECURSION_BUDGET_CAP_PER_ITER_USD unset Per-iteration cap
MO_OPTIMIZER_BUDGET "4" GEPA optimizer budget
MO_GATE_HACKABILITY_BUDGET_USD 0.50 Per-audit proposer budget

Inspecting spend

Spend is recorded per call, so it is always queryable:

sqlite3 .mini-ork/state.db \
  "SELECT date(created_at,'unixepoch') AS day, round(sum(cost_usd),2) AS usd
     FROM llm_calls GROUP BY day ORDER BY day DESC LIMIT 7;"

Per-run:

sqlite3 .mini-ork/state.db \
  "SELECT run_id, round(sum(cost_usd),4) AS usd, count(*) AS calls
     FROM llm_calls WHERE run_id='<run-id>' GROUP BY run_id;"

The board document also carries per-run cost:

mini-ork board --json | jq '.runs[] | {id, recipe, cost_usd, status}'

And the IDE run page shows the same numbers, with per-node attribution.

Choosing lanes to control cost

Cost scales with the lane each role uses. A practical default:

Role Cheap lane Why
planner mid-tier Plan quality matters, but the plan is small
implementer strong This is where correctness is decided
reviewer strong Cross-model dissent catches what a same-family judge misses
reflector cheap Pattern extraction, not creative work
verifier mid-tier Verification is mostly mechanical

Change lanes in agents.yaml; see Providers and lanes.

Lane health is a separate gate

A run can fail before spending anything because a lane is dead — an expired key, an exhausted balance, a model that no longer exists.

Variable Default Effect
MO_LANE_HEALTH_GATE "1" Probe every model the DAG will dispatch; abort with rc 75 / lane_unhealthy if any is dead
MO_LANE_REQUIRE_VERIFICATION "0" 1 also aborts when a lane could not be verified (lane_unverified)

Lane health is not a cost control, but it belongs here because it is the other gate that stops a run before any money is spent.

Practical advice

  • Set MO_DAILY_BUDGET_USD to what you are willing to lose in a day, not to what a run should cost.
  • Use board after the first few runs to see which role dominates spend.
  • Keep the reviewer on a strong, different model family. Downgrading it saves cents and costs verdicts.
  • Dry-run the shape of a large plan before executing it: MO_DRY_RUN=1.