clikae v0.27.0 — the agent stops parsing prose
burn --json and conduct --json
AGENTS.md's first non-negotiable rule is judge by the artifact/output, never the exit code — and clikae made an agent read that judgement out of sentences.
burn is the worst case. With rerouting, the tank that did the work is often not the one you named, and the only record of which was a line of prose.
clikae burn codex work --prompt-file t.md --artifact out.md --json \
| jq -r 'select(.ok) | "\(.engine)/\(.tank) wrote \(.artifact_bytes)B"'burn: {ok, engine, tank, artifact, artifact_bytes, reason, reset,
rerouted_from[], elapsed_s, run_id}
conduct: {out_dir, captured, dry, other,
legs:[{engine, tank, status, detail, output, output_bytes}]}
One object on stdout, every word of progress on stderr. artifact_bytes is the artifact's own measurement, so the evidence rule 1 asks for travels with the verdict rather than being a second call the caller has to remember.
reason separates the two failures that read alike in prose and are not the same thing: every reachable tank is dry — wait, or add fuel — from no fresh artifact and no limit, where the task itself failed. conduct's status separates EMPTY from DRY for the same reason: clikae never judges, so the caller is the one who has to rank the legs.
Audited the whole surface for this: 33 commands, 5 had --json. These two were the gap on the axis AGENTS.md actually cares about.
The suite could not safely run beside a copy of itself — and the hooks guarantee it does
clean's live guard runs ps -axo command= so it can never offer a session a process still has open. Correct for the command; fatal for concurrency. Suite A's clikae processes appear in suite B's snapshot, the fixtures use fixed session ids, and B decides those sessions are live and skips the rows it is asserting on.
pre-commit runs the suite and so does pre-push, so git commit && git push overlaps them by construction.
Reproduced by starting a second run 25 s into the first:
round 1 A=0 B=0 notok_A=0 notok_B=0
round 2 A=1 B=1 notok_A=4 notok_B=2 <- both red
round 3 A=1 B=1 notok_A=2 notok_B=1
round 4 A=1 notok_B=1
Every failure was [ "$status" -eq 0 ] on a clikae clean. Three of four rounds.
This is the explanation for a pre-push red that ~218 isolated runs could not reproduce — 10 full suites, 5 sequential and 3 concurrent copies of the file, and 200 runs of the exact file at the exact commit in a worktree, all green. The condition every one of them was missing was another suite running beside it.
scripts/test.sh now takes $TMPDIR/clikae-test-suite.lock and waits, saying what it is waiting for. A suite that is red for a reason outside the code teaches you to ignore red, which is the one thing a gate cannot afford.
(lockf -k, not lockf — without -k two processes both get rc=0, the same trap the ephemeral slot lock hit in 0.25.0.)