Development record
2026-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.
2026-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.
Frontmatter
| id | dcn-2610170704288549 |
|---|---|
| type | decision |
| date | 2026-10-17 |
.abcd/work/decisions/dcn-2610170704288549-refines-dcn-2610170618185599-when-the.md
open on GitHub ↗ · commit history ↗
mentions2
- dcn-26101706181855992026-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.
- dcn-26101707263832532026-10-17 — Refines dcn-2610170704288549: the guard counts a { or } as opening or closing a group, for what a pipe into the group reaches, only where the line leaves that word wholly unquoted, the same test the approver's own reading of the text it hands back uses. A } with a quote in it, even an empty pair (}"" or }''), is a command's name to the shell, so the commands after it still read the group's piped input, and a shell among them is refused as one reading a stream (echo 'touch ran' | { }""; bash; } is refused, as the same line with true in place of the } is). And a removal or a write in a command where an unquoted ( stands in a word, glued to it (build/l(:A), x(.), a/b(/), a redirection's target) or opening one after the command's name, is refused as a target the line does not fix, whatever follows the (. zsh reads such a ( as glob qualifiers or modifiers that can change the path the word names: build/l(:A) is the real path a symlink leads to, outside the working tree; bash refuses the line. A find or an rsync in such a command is read as removing, since the words after the ( are not read as its own. A quoted or escaped ( is text and stays allowed (rm -f 'a(b)').