rabadon

overview / commands / rabadon repair

rabadon repair [dir] [--cmd "<check>"]

Close the repair loop. A caught red check -> claude -p proposes a fix in an isolated copy -> the same check re-runs there. Green with test files hash-unchanged -> the verified patch is held at .rabadon/repair-<ts>.patch (never auto-applied). Fake or test-weakening fixes are rejected.

maturity: stable — shipping and tested

Needs the claude CLI (or RABADON_CLAUDE_BIN) as the proposer.

Flags: --cmd "<check>" (the deterministic check to run; otherwise inferred from the test suite or the net's last red verdict) · --timeout <sec> (proposer wall clock, default 240) · --approve (run the repair the arm asked about in ask mode) · --apply (apply the newest held patch — the only thing in rabadon that edits your tree, and a human types it).

The arm also starts itself, under a policy you set once. rabadon init writes repair.mode into $RABADON_DIR/config.json and never asks again:

modewhat happens when the trigger fires
ask (default)one line on stderr and a REPAIR_ASK ledger event. Nothing runs until rabadon repair --approve.
auto-proposeruns without asking, and never touches your tree — the patch waits at .rabadon/repair-<ts>.patch for rabadon repair --apply.
offthe arm is disabled. The signals still reach the ledger.

A value that is none of those three is read as off, and said out loud: a policy nobody can parse is not permission to spend.

The trigger is both halves of one sentence, and one half is not enough: the root_migration signal — the same error out of a third different move — and the injections did not help, meaning R4 has already answered that signal with everything it had, at least one of those answers reached the agent, and the error has since come out of three more different moves. Firing on the first sighting would bill for what an injection fixes for free. It fires at most once per session, and the proposer call it makes is booked on the ledger as a COST carrying characters in, characters out, and a token figure marked "estimated":1.

The text handed to the proposer names files, never lines (Law 2): every file.py:44, line 3 and L44 is stripped out of the whole assembled prompt, and the files the failing output names — the ones that actually exist in the tree — are listed instead. The arbiter's raw output is framed by rabadon's own words, never forwarded on its own.

Exit: 0 held a patch, or the check was already green · 2 REPAIR_FAIL (still red, test-tampered, or a zero-diff flake) · 3 no runnable check, no proposer, or the check resolves outside the isolated copy · 4 FLAKY — the same check answered differently on two runs of the same tree, so this run graded nothing.

One run of a check is not a verdict. Both decisions that can throw work away are sampled twice. On the way in, a green entry run is confirmed before repair goes home: green then red exits 4 instead of reporting nothing to repair — a check that flakes green would otherwise drop a break still sitting in your tree. On the way out, a red arbiter run is re-sampled before it may reject the proposal: red twice is REPAIR_FAIL as before, but red then green is held and labelled FLAKY, because a suite that flakes red once (expressjs/express flaked red on 3 of 6 pristine runs) would otherwise destroy a correct, source-only fix you already paid a proposer call for. A flaky hold is never called VERIFIED, and the ledger carries its own reason (flaky check: arbiter samples disagree) — never check still red after proposal, which would be a false sentence written into a hash-chained record.

Exit 3 is what pip install -e . does: the editable install writes an absolute path into a .pth in site-packages, the copy inherits it, and the copy's interpreter imports your tree instead. repair refuses before it copies anything, names the file, and records nothing — grading a proposal against code it never touched is not a verdict in either direction. Same for an absolute symlink back into the tree (npm link) and for a bin/ shim your check invokes by name. Give it a check that stands up inside a fresh copy: --cmd "PYTHONPATH=src python3 -m pytest".

rabadon repair rabadon repair --cmd "npm test"

Apply a held patch yourself: patch -p1 < .rabadon/repair-<ts>.patch.

commands

rabadon init