Development record
2026-10-30 — A find that deletes each match with rm and no recursive flag (find . -name x -exec rm {} \;) is not a guard hazard: it removes only the files the find names, one at a time, the same everyday delete dcn-2609280000000003 allows as rm -rf $(find ...), and a blocker there would teach sessions to route around the guard (adr-42, a mistake filter). Decided by the technical facilitator on delegation while judging iss-2609300536290307.
decisiondcn-2610301309039861
2026-10-30 — A find that deletes each match with rm and no recursive flag (find . -name x -exec rm {} \;) is not a guard hazard: it removes only the files the find names, one at a time, the same everyday delete dcn-2609280000000003 allows as rm -rf $(find ...), and a blocker there would teach sessions to route around the guard (adr-42, a mistake filter). Decided by the technical facilitator on delegation while judging iss-2609300536290307.
Frontmatter
| id | dcn-2610301309039861 |
|---|---|
| type | decision |
| date | 2026-10-30 |
.abcd/work/decisions/dcn-2610301309039861-a-find-that-deletes-each-match-with-rm.md
open on GitHub ↗ · commit history ↗
mentions3
- adr-42The guard's parse layer is a mistake filter, not a security boundary — matching is two-tier, and completeness is abandoned as a goal
- dcn-26092800000000032026-09-28 — guard registry, the rm targets (iss-2609282105242542, autonomous run A lane gateFix): the chapter's headline promised the catch of "an `rm -rf` with an unlucky glob" while the one rm entry fired only behind a `cd` chain, so `rm -rf /` and `rm -rf ~` were an allow. Two bundled entries close it through one new additive Pattern field, `arg_values` (some operand is exactly one of the listed words, by its known text; empty and dash-led values are load-time rejections like an empty prefix). `rm-rf-root-or-home` is a BLOCKER on `/`, `/*`, `~`, `$HOME`, `${HOME}` and their `/` and `/*` forms; `rm-rf-working-directory` is a WARN on `*`, `.`, `..`, `./`, `../`, `./*`, `../*` and `.*`, graded like `git clean` because emptying a build directory the same way is ordinary work and a blocker there would teach sessions to route around the guard. Both require the recursive flag alone, not `-f`: for an agent, whose stdin is not a terminal, rm does not prompt, so `-f` changes nothing about what is destroyed (the cd-chain entry keeps its `-f` requirement; changing a blocker's pattern is a separate act). This reverses the incidental stance that a bare `rm -rf *` is not a hazard (the lab finding iss-2609012040019014 and two tests that used it as their ordinary-work probe): those tests keep their point with `rm -rf ./build` as the probe, and a host workdir is still never read as a `cd`. A third operand residual is recorded beside the two unknown.go names: a target a substitution prints WHOLE (`rm -rf $(echo /)`) is read by its known text, as the `+` refspec prefix is, because `rm -rf $(find . -name '*.pyc')` is how an everyday delete names its targets and reading the word as every target would block it as a delete of `/`; glued text still counts (`"$(true)"/` is `/`). Parameter expansions stay unread except as the literal words the entry names (`rm -rf "$DIR"/*` with `DIR` unset is not seen). The per-byte work bar of the guard's cost tests moves from 20 to 24: the operand walk reads every token once per entry, and sixteen entries put the costliest asserted shape at about 20.
- iss-2609300536290307The guard registry names no recursive delete and nothing wires guard check into unattended runs: On abcd v0.9.0, abcd guard check answered allow for rm -rf /tmp/x, command rm -rf $S/x, rm -r ./scratch and find . -name x -exec rm {} \; (the form without check is refused as unknown). In an unattended run one such command raised a permission prompt that stalled the run for about 111 hours; a registry entry plus wiring the check into sub-agent runs would have failed it in seconds.