Development record
2026-10-17 — Refines dcn-2610162040034117: the headless approver judges a glob removal's directory where it leads and approves the text it hands back only when it is intact. A glob removal is approved, as dcn-2610161930191632 narrows it, only when its fixed directory lies inside the working tree both as written and followed through symlinks, read by the same resolution the check of removals outside the tree uses, and only when no earlier part of the line writes or removes a file, since such a part (an ln, a cp -s or cp -l, a mkdir) could make that directory a link after the line was judged; otherwise it is refused as a target the line does not fix, as before. And the text an approval hands back, cd -- '<dir>' && {, a newline, the command, a newline and }, is read again with the guard's own parse and approved only when its top level is exactly that change of directory joined by && to one group holding the whole command, with nothing after it. A command that would close the group before its end or leave the text unparsable as one group (a } or ) with nothing open for it to close, a fi, done or esac with nothing open, a } after a command's words, which zsh reads as a close, or a backslash at its very end) is refused under the new id tree-group-broken, so the agent is told why rather than handed a shell error, and no part of the command runs outside the change of directory.
2026-10-17 — Refines dcn-2610162040034117: the headless approver judges a glob removal's directory where it leads and approves the text it hands back only when it is intact. A glob removal is approved, as dcn-2610161930191632 narrows it, only when its fixed directory lies inside the working tree both as written and followed through symlinks, read by the same resolution the check of removals outside the tree uses, and only when no earlier part of the line writes or removes a file, since such a part (an ln, a cp -s or cp -l, a mkdir) could make that directory a link after the line was judged; otherwise it is refused as a target the line does not fix, as before. And the text an approval hands back, cd -- '<dir>' && {, a newline, the command, a newline and }, is read again with the guard's own parse and approved only when its top level is exactly that change of directory joined by && to one group holding the whole command, with nothing after it. A command that would close the group before its end or leave the text unparsable as one group (a } or ) with nothing open for it to close, a fi, done or esac with nothing open, a } after a command's words, which zsh reads as a close, or a backslash at its very end) is refused under the new id tree-group-broken, so the agent is told why rather than handed a shell error, and no part of the command runs outside the change of directory.
Frontmatter
| id | dcn-2610170618185599 |
|---|---|
| type | decision |
| date | 2026-10-17 |
.abcd/work/decisions/dcn-2610170618185599-refines-dcn-2610162040034117-the.md
open on GitHub ↗ · commit history ↗
mentions3
- dcn-26101619301916322026-10-16 — Refines dcn-2610161821420088: the headless approver runs each approved command from the working tree it judged it for. The host keeps the shell's directory from one call to the next, so an allow hands the command back as cd -- '<dir>' && <command>, the directory quoted as one literal word by internal/shellquote; a change of directory inside the line still holds for the rest of it. A relative removal target after a cd the line spells out and joins with && is read in that directory, and one after a change of directory the line leaves unknown is refused as a name the line does not place, with that reason. A removal (rm, rmdir, unlink, mv's sources, a find that deletes, an rsync --delete) whose fixed target lies outside the launched working tree, the run's own directories (the brief's and the receipt's, which the runner passes as --run-dir) and the temporary directory is refused. A write outside the working tree is judged as before, by the protected set alone: the OS sandbox of spc-2610161751056096 is what bounds those writes, not the approver. Three narrowings keep ordinary work from being refused: a git -c core.pager or core.sshCommand setting whose value is a fixed command is read as that command, as a bang alias already is, and approved when that command is clean, where git runs from the working tree and no earlier part of the line writes or removes a file; a glob removal whose fixed directory is inside the working tree and unprotected is approved unless a protected name could match the glob: one its literal text aims at (a name starting with a dot only by a pattern that spells the dot, as the shell does by default), an entry already standing in that directory that is or holds a protected path, or a protected path below it; and the words after a script's or a module's name belong to it, not to the interpreter, so node app.js -p 3000 and python3 -m pytest -c pytest.ini are approved. <!-- record-lint: forward-looking -->
- dcn-26101620400341172026-10-16 — Refines dcn-2610161930191632: an approved command comes back as cd -- '<dir>' && {, a newline, the command, a newline and }, so nothing of it runs when the change of directory fails. The approver's list of agent launches (the program names, the flags that turn a harness's own checks off, the wrappers and package runners it reads through, the packages that ship a harness and the variables a harness reads its settings from) is a mistake filter in adr-42's sense: it steers an agent that starts another harness by an ordinary route. A harness started by a route outside that list, such as another program name, a copied binary or a configuration file it reads, is bounded by the OS sandbox for launched agents (spc-2610161751056096), not by the list. So a further form is added to the list when it is common, not to close the class. <!-- record-lint: forward-looking -->
- dcn-26101707042885492026-10-17 — Refines dcn-2610170618185599: when the headless approver reads the text it hands back, it counts a { or a keyword as opening, closing or leading a command only where the line leaves that word wholly unquoted, with no quote, escape or empty quote pair in it, the same test it already applies to a }. A quoted { ("{", '{', \{, {"") or a quoted keyword ('then', \!, "time", 'function') is a command's name to the shell, and the words after it are arguments, so a line of one, then a }, then a command, is refused under tree-group-broken, since under bash and sh that } closes the group and the command after it runs outside the change of directory. And a glob removal is refused as a target the line does not fix when its pattern holds a /: a wildcard in a directory part (build/*/*, ./build/*/*, build/*/x.o) or a trailing / (build/*/). Such a glob lists directories the line never names, any of which may be a symlink out of the working tree, which the shell would expand into. This refusal was chosen over following every matching directory through symlinks, as the simpler rule; a glob whose wildcards stand only in its last name, such as rm -rf build/*, is judged as before.