rabadon

overview / rules / baseline-rm-rf-outside

baseline-rm-rf-outside

A recursive delete outside the project tree cannot be undone by git.

what it refuses, and what it lets through

a recursive rm whose target lands outside the project tree and outside the system temp area. The target is resolved, not pattern-matched: .., ~ and a symlinked parent land where they really land, and a wildcard or a brace is expanded the way a shell expands it before it is judged — rm -rf /tmp/*/../../Users/you/work is refused because every word it expands to leaves the temp area, while rm -rf /tmp/build-*/out passes. The temp carve-out is your scratch, not the shared root's contents: under /tmp, /var/tmp or $TMPDIR the first component of the target must be a name you wrote, so rm -rf /tmp/scratch-* and rm -rf /tmp/proj-out/* pass while rm -rf /tmp/*, rm -rf /tmp/*.log and rm -rf /tmp/*/x are refused — they name another session's mktemp tree as surely as your own. $TMPDIR is read from the environment, which is the one input an agent writes for itself, so a root named there is honoured only when it lands inside the machine's real temp area (/tmp, /var/tmp, /var/folders) and not under $HOME: TMPDIR=/usr/local rm -rf /usr/local/lib and TMPDIR=/srv rm -rf /srv/www are refused, because an assignment does not make a directory disposable. The cost, stated: an unusual $TMPDIR such as /run/user/1000 is no longer read as scratch, and a recursive delete there is refused when it is also outside the project tree — silence baseline-rm-rf-outside by id if that is your setup. rm -rf ./build and rm -rf node_modules pass.

From the floor table in docs/guard.md.

The way out

A guard with no way out is a trap, so here is the way out, measured on 2026-08-30 rather than asserted. Deleting a project of your own that carries its own .rabadon is ordinary work and passes — rm -rf ./old-project, mv ./old-project /tmp/bin, both held by assertion in native/law_family_test.sh. The subject of this rule is the law, not the tree around it.

For a leftover somewhere else on the disk, the refusal now names the rule that is actually speaking, which before today it did not:

rm -rf ~/scratch/old-sandbox -> Rule: baseline-rm-rf-outside — a recursive delete outside the project tree (only the user can override this: they may add "baseline-rm-rf-outside" to disabled[] in .rabadon/guard.json or run `rabadon off`. If this rule looks wrong for the task, stop and ask the user; do not switch supervision off yourself.)

Both of those work, and so does disabled: ["baseline-law-unmade"] for the law itself — that path is asserted in the suite too, because an override named in every refusal message that does not actually open the door is worse than no override at all. RABADON_OFF=1 <command> is NOT one of them: an env prefix is part of the command text and the gate reads it as a silencer being spelled at it. That spelling was published by an earlier phase and is wrong; use rabadon off.

the refusal, as the agent reads it

rabadon BLOCKED this action. Rule: baseline-rm-rf-outside — a recursive delete outside the project tree cannot be undone by git rm -r '<operand>' expands to more than 256 words, so where it lands cannot be computed — name the paths, or silence baseline-rm-rf-outside by id Adjust the approach instead of retrying the same action. (only the user can override this: they may add "baseline-rm-rf-outside" to disabled[] in .rabadon/guard.json or run `rabadon off`. If this rule looks wrong for the task, stop and ask the user; do not switch supervision off yourself.)
rabadon BLOCKED this action. Rule: baseline-rm-rf-outside — a recursive delete outside the project tree cannot be undone by git rm -r '<operand>' resolves to <resolved path>, outside the project tree (<project root>) Adjust the approach instead of retrying the same action. (only the user can override this: they may add "baseline-rm-rf-outside" to disabled[] in .rabadon/guard.json or run `rabadon off`. If this rule looks wrong for the task, stop and ask the user; do not switch supervision off yourself.)

Rendered from the strings in native/baseline.h and the envelope in native/gate_pre.h (Judge::refuse). A word in <angle brackets> is filled in from the command at the moment it is refused.

in watch mode the same verdict is recorded, and nothing is stopped

rabadon (watch) would have blocked this. Rule: baseline-rm-rf-outside — a recursive delete outside the project tree cannot be undone by git rm -r '<operand>' expands to more than 256 words, so where it lands cannot be computed — name the paths, or silence baseline-rm-rf-outside by id Nothing was stopped. `rabadon on` makes this a real refusal.

switching it off

Only the user can switch it off, by id, in the project's guard:

{ "disabled": ["baseline-rm-rf-outside"] }

or by pausing supervision with rabadon off, which records the change. Every other rule keeps running.

in the field

70 of the 778 records in the published field dataset (field.jsonl, 2026-07-27 to 2026-08-26) carry this id. STOP is a refusal in enforce mode; WOULD_BLOCK is the same verdict recorded in watch mode, where nothing is stopped.

eventrecords
WOULD_BLOCK70

Counted from field.jsonl, the file the field page is built from.

rules

baseline-law-unmade

commands

rabadon on | off | status | toggle

measurements

the refusals that cannot be undone