overview / commands / rabadon trace
rabadon trace [run] [--days N] [--last]One run, step by step: which step was caught, what the check said, whether the repair was accepted or the fake fix refused, and what the repair cost. Reads the spool only — nothing here calls a model, and every number printed is already in the ledger.
maturity: stable — shipping and tested
A refusal is a run too. rabadon-gate and rabadon exec write one run per refusal, and it holds exactly two events — the failed check and the STOP — with no step ever announced, because the refusal returns before the step starts. That run renders as its caught step: the tool that was stopped, the rule id that fired, what the rule said, CAUGHT 1, and verdict: BLOCKED. Nothing was proposed and nothing was priced, so no repair, no cost and no model is printed — the header names the surface the refusal came through instead.
A WATCH verdict is not a catch and is never counted as one. rabadon off records what the arbiter WOULD have stopped and lets the command run; that renders as WOULD BLOCK, on its own line, against CAUGHT 0 — the same split rabadon usage keeps between refused and wouldRefuse.
The saved: line is the only sentence here that talks about steps which did NOT run, so every number in it is measured off the run rather than assumed from the catch. The steps it says NEVER ran are the plan's declared count minus the steps the run actually started — never "everything after the catch", because the run may have gone on for several steps before it stopped. The steps it says ran on a clean base are the ones that started after the catch. A catch on the run's last step has no range at all and prints none. A rejected repair followed by an accepted one is not a STOP; it is reported as both. And a run whose ledger holds no REPAIR_FAIL for a step never prints a refused fake fix for it, exactly as a run with REPAIRED 0 never says repaired.
[run] is a run id, not a file. A bare word is taken as a run whenever it is not a path that exists; a path (a .jsonl, or a directory whose newest day file wins) is taken as the ledger to read. --run <id> is the same thing spelled long. The id is matched as a substring, so the eight characters the header prints are enough to address the run.
The id is looked for across the whole --days window (default 7), not in the newest day file alone — a run written yesterday is still addressable today. When more than one day file carries the run, the header names the first and counts the rest.
Exit: 0 when a run rendered. 1 when none did — the message names the run and the window it searched, and stdout stays empty, so a caller can tell "not there" apart from "there and empty". 2 on an unknown flag or a second positional.