write_file on ~/.ssh/authorized_keys is allowed by an action policy that only says “write is permitted”. Path scope is a separate check with its own file, its own codes and its own failure mode.
Schema
Both lists are required. A missing
exclusions is invalid, not “no exclusions”, because an empty list and a forgotten list should not look the same.
Globs support ** for any depth (including zero directories), * within one path segment, and ? for one character. Matching is case-insensitive on macOS and Windows.
What happens to a proposal
The check runs before the policy and before path rules:shell_scope: "deny"plus capabilityexecuteis refused asSHELL_SCOPE_DENIED.- A file tool with no resolvable path is refused as
PATH_REQUIRED. Other tools without a path are not judged here. - The path is resolved:
~expands to your home, relative paths resolve from the working directory, and symlinks are resolved withrealpath. A write target that does not exist yet resolves its existing parent, so a symlinked directory cannot be used to escape a root. - An exclusion that matches the resolved path, or the path as written, is refused as
PATH_SCOPE_DENIEDwith rulepath-exclusion. Exclusions win over roots. - For
readandwrite, the path must sit under a configured root. Outside every root:PATH_SCOPE_DENIEDwith rulepath-root. Inside a root that does not grant the capability:PATH_SCOPE_DENIEDwith rulepath-access. - Other capabilities (
send,execute,spend) are not path-judged, except by theshell_scopecheck above.
read_file, write_file, patch, apply_patch, search_files, list_dir, create_file, delete_file, edit_file, str_replace_editor, notebook_edit, and the Claude-style names Read, Write, Edit, Glob, Grep, plus read_files, write_to_file and replace_in_file.
The hook finds a path in
tool_input under one of path, file_path, target_file, notebook_path, directory or cwd. A tool that names its target in any other field is not path-scoped, and PATH_REQUIRED fires only for the known file-tool set above. If you run a harness with its own tool names, check what the hook actually sees with attesso log --json or the ledger entry before trusting the scope.Deny shell outright
Shell and execute tools are not command-parsed and not path-scoped. A deployment that does not want a hook to execute anything can say so in one field:execute capability and nothing else. If a task needs a shell, this is the control that stops it; if it needs a scoped shell, Bounds is the wrong tool and the harness’s own sandbox is the right one.
What this is not
- Not a sandbox and not a container. The agent process still has whatever filesystem access its operating system grants. Path scope refuses a tool call; it does not confine the process.
- Not a command parser.
executehas no path argument to inspect, soshell_scopeis all-or-nothing. - Not a substitute for the policy. A path inside a root can still be refused by the action policy, and both refusals are recorded with their own codes.

