CLIO Coder
All tutorials

Safety and autonomy6 min read

Run it unattended within limits

Start a headless run with a cost ceiling and a timeout, ask read-only questions, schedule turns, and know what stops each one.

Written for
v0.8.0
Works in
Terminal · Desktop alpha
Basis
Recorded session
Updated
Terminal recording of clio-coder run --json writing its events to run.jsonl and exiting 0, jq printing the final answer that 6 of 7 tests pass with the absolute-zero test failing, and the last event, run_end, with outcome succeeded and safety decisions of 1 allowed, 0 blocked and 0 permission requests.
A headless run: JSON events to a file, exit status 0, the answer, and the run_end event that closes the stream. Terminal · v0.8.0 Recording · 0:18
Transcript
An unattended CLIO Coder run with JSON output
Recorded with CLIO Coder 0.8.0. Each section is the terminal screen at that moment.

== answer

~/projects/calibration-lab $ clio-coder run --json "Run the test check and report how many tests pass." > run.jsonl; echo "exit $?"
exit 0
~/projects/calibration-lab $ jq -r 'select(.type == "turn_end").message.content[] | select(.type == "text").text' run.jsonl
Test check finished: **6 of 7 pass, 1 fails.**

The failing test is `absolute zero: 0 K = -273.15°C = -459.67°F` at `calibration.test.mjs:20`. It throws `RangeError: Kelvin temperature 0 is below absolute zero (0 K)` from `kelvinToCelsius` at `temperature.mjs:22` — the guard rejects `0 K` even though 0 K is absolute zero itself and the test expects it to be valid.

All six other tests pass, including the `rejects temperatures below absolute zero with RangeError` case, so the boundary condition is off-by-one: `0 K` should be allowed.
~/projects/calibration-lab $

== run-end

s." > run.jsonl; echo "exit $?"
exit 0
~/projects/calibration-lab $ jq -r 'select(.type == "turn_end").message.content[] | select(.type == "text").text' run.jsonl
Test check finished: **6 of 7 pass, 1 fails.**

The failing test is `absolute zero: 0 K = -273.15°C = -459.67°F` at `calibration.test.mjs:20`. It throws `RangeError: Kelvin temperature 0 is below absolute zero (0 K)` from `kelvinToCelsius` at `temperature.mjs:22` — the guard rejects `0 K` even though 0 K is absolute zero itself and the test expects it to be valid.

All six other tests pass, including the `rejects temperatures below absolute zero with RangeError` case, so the boundary condition is off-by-one: `0 K` should be allowed.
~/projects/calibration-lab $ tail -n 1 run.jsonl | jq .
{
  "type": "run_end",
  "startedAt": "2026-11-13T00:18:10.188Z",
  "endedAt": "2026-11-13T00:18:16.582Z",
  "exitCode": 0,
  "outcome": "succeeded",
  "safety": {
    "decisions": {
      "allowed": 1,
      "blocked": 0,
      "permissionRequested": 0
    },
    "blockedAttempts": []
  }
}
~/projects/calibration-lab $
On this pageBefore you start Read how a run ended Pick the right tool

You can start a task and leave. Headless clio-coder run does one turn with nobody attached, and the limits you set are enforced. This guide covers a bounded headless run, a read-only one, the timeouts that apply when you walk away from an open session, and scheduled turns.

Before you start

  1. Put your decisions in the prompt

    The judge reads only your prompt in a headless run, so name the actions the job needs:

    Shell

    clio-coder run --max-cost-usd 2 --timeout 1800 \
      "Fix the failing parser test, run the test check, and commit on this branch. Do not push."

    --max-cost-usd caps the priced spend this run adds. Reaching it stops the run and its workers with exit 4 and budget_ceiling. Calls already in flight can pass the cap, and unpriced routes cannot be bounded, so pair it with --timeout, which exits 124 on expiry. The session ceiling, 5 USD by default, still applies.

  2. Ask a question that cannot change anything

    Shell

    clio-coder run --read-only "Which modules have no tests? Report paths."

    --read-only is an enforced ceiling on the main agent, not a request. No file outside CLIO Coder's home changes: writes are refused, and shell commands run only in a read-only sandbox.

  3. Stream it to another program

    Shell

    clio-coder run --json "Summarize the last five commits."

    The stream opens with a session header carrying its schema version and ends with one run_end frame carrying the exit code and outcome. Read the result from that frame. Continue later with --continue or --session <id prefix>.

  4. Walk away from an open session

    In the terminal, an unanswered approval card is denied after safety.unattendedAfterSeconds (300 by default) and the turn continues. A question takes its recommended option. Any key stops the countdown. When you return, /decisions shows what was chosen, and the exit summary counts it on an Unattended line.

  5. Schedule turns while idle

    /loop 30m Check the nightly build log and report anything new.
    /loop 5m --command make check
    /loop

    A bare /loop lists the jobs. A loop runs only while its conversation is idle and never replays missed runs. The --command form polls a program directly, with no shell.

What you should see

A finished turn with its exit code, not a parked prompt. An exit of 4 means the cost ceiling stopped it. clio-coder telemetry why-stopped <session> explains a stop afterward.

Read how a run ended

Stderr names the session and ends with an observed: line that counts tool calls, refusals, writes, and checks. When --max-cost-usd meets a route with no price, stderr says the cap cannot bound it; declare targets[].pricing to fix that. A script should read the exit code or the run_end frame, not the prose answer.

In a separate run of the first step, made for this guide apart from the session at the top of the page, Qwen 3.8 27B worked on a repository with one failing parser test. It fixed the parser, ran the test, committed on the branch, and pushed nothing. Stderr read:

clio-coder run: --max-cost-usd does not bound unpriced routes: litellm/qwen3.8-27b; declare targets[].pricing to bound them
clio-coder run: session sjajclvyg6cw
clio-coder run: observed: 10 tool calls, 0 refused · 1 write · checks passed: npm run test, git diff

A --json run of the third step from the same set ended with this frame:

JSON

{"type":"run_end","startedAt":"2026-11-12T19:02:57.365Z","endedAt":"2026-11-12T19:03:09.119Z","exitCode":0,"outcome":"succeeded","safety":{"decisions":{"allowed":3,"blocked":0,"permissionRequested":0},"blockedAttempts":[]}}
Exit What it means
0 The run settled and succeeded
1 It failed: the model was unreachable, a worker did not deliver, or a refused call left the task undone
2 A usage error, including --autonomy yolo as root outside a container
3 At yolo, the safety judge blocked too many calls in a row; review the run, then --continue
4 A cost ceiling, or at yolo an autonomous cap on tool calls, minutes, or tokens, stopped it; --continue resumes
124 --timeout expired; the run still settled and sealed
130, 143 Ctrl+C or a SIGTERM stopped it

Pick the right tool

When you want Use What still stops it
One bounded task clio-coder run at default Judge, sandbox, cost ceiling, timeout
A long job you leave running clio-coder run --autonomy yolo Contained by default: the sandbox and judge stay on, and a call the judge does not allow is refused back to the run. Ceilings, timeout, the loop guard, and the tool budget also stop it
A recurring check /loop in an open session Pause or stop it; needs the session to stay open

Boundaries

By The CLIO Coder team · CLIO Coder

Continue with the documentation