Development record
2026-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 -->
2026-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.
Frontmatter
| id | dcn-2610161930191632 |
|---|---|
| type | decision |
| date | 2026-10-16 |
.abcd/work/decisions/dcn-2610161930191632-refines-dcn-2610161821420088-the.md
open on GitHub ↗ · commit history ↗
mentions4
- dcn-26101618214200882026-10-16 — Refines dcn-2610161624025493: the headless approver's own checks read the guard's parse of the line, not its raw text. Writes and removals are the targets the guard's write model resolves (writesOf), in every shell state the reading placed each segment in, plus the operands of touch, mkdir, chmod, chown, truncate, rm, rmdir, unlink, mv's sources, ln's link targets and a sed script's w commands; a target the line does not fix (a variable, a substitution's output, a glob, or a relative name after an unknown change of directory) is refused, and a fixed one is compared, with its existing directories resolved through symlinks and its case folded where fsutil.CaseFoldingFS says the filesystem folds, against the protected set: the git common directory with its hooks and config, each core.hooksPath directory, each checkout's .git, the home shell start-up files and git configuration, the host's settings directories, .mcp.json and .abcd/guard.json, plus the harness's protected names as path components relative to the working tree or home. A line that only names a protected path is no longer refused. git config is refused with --global, --system or --file on a write, on setting a key that starts a program (the gitutil.ExecPins keys and git's documented program settings) and on opening the editor, and so is any git command setting such a key for itself. The approver takes --base, the lane's base or the worktree's HEAD at an interview's launch, and the working tree's registry is in force only where it is at least as strong as the one at that commit: any weakening, of a warn or a blocker, committed during the run or not, refuses every command, and a shell launch with no base is not started. An allow returns only the command and its description, and a call asking to run outside the host's sandbox is refused. Critical removals widen to home's direct children and the repository's other checkouts, and to a find that deletes from home or above, a move of a critical directory, an rsync --delete into one and a recursive chmod or chown of one. Code reaching an interpreter outside -c/-e is refused: on the standard input of a listed interpreter or ex that names no script file, an awk program that runs a command, a sed script with the e command or flag, tar's options that run a program, and a vim or ex command holding a !. A glob in a removal target is refused as not fixed except where it lists a critical directory, which the critical check refuses first. The checks read the line itself; a script the line runs is read for the registry's hazards, not for its own writes. The guard stays a mistake filter (adr-42) and this adds no sandbox (dcn-2610161619296615).
- 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-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.
- spc-2610161751056096a-person-who-leaves-abcd-to-run-on-its