Skip to content

feat(cli): deny a role's pane what the guard watches - #34

Merged
Bakhomious merged 1 commit into
mainfrom
step/guard-hook
Nov 3, 2026
Merged

Bakhomious merged 1 commit into
mainfrom
step/guard-hook

Conversation

@Bakhomious

Copy link
Copy Markdown
Contributor

Step guard-hook (docs/plan.md §0): baton hook guard, the plugin's preToolUse hook.

What it does

In a role's pane (decision 52):

  • denies a bash call whose script the guard can't read, or that runs a command that the guard watches (decision 51), but baton's commands for a role: baton wait, handover, ask, status and help;
  • denies a create or edit of a file in baton's folder, however its path names it: relative, through a symlink, in other case on a disk that ignores case, or a new file in a new folder (an os.SameFile walk up the path's folders);
  • says nothing otherwise, and nothing in the orchestrator's pane until guard-orchestrator;
  • fails (so copilot denies) when it can't decide: input that isn't a JSON object, arguments that don't fit the tool, no BATON_ROLE, a folder outside a repo.

Each reason starts with "baton's guard stopped this call, and none of it ran." and says what the role can do instead.

hooks.json gains the preToolUse hook, run through BATON_BIN as the stop hook is.

Checks

  • Tests: guard_test.go (27 script rows, other tools, the orchestrator's pane) and guard_files_test.go (13 paths × create/edit, a repo without baton's folder, 9 failure rows, an output it can't write). guard.go 100%, internal/cli 99.8%, total 99.3%.
  • Probes: 50 mutants, 49 caught; the one survivor is equivalent (resolve's root guard, which EvalSymlinks never reaches at / or .).
  • Smoke with the built binary and fake hook input: denials and silences as designed; the hook answers in about 10 ms.
  • Live check (the user's OK via nudge): copilot -p with Claude Haiku 5.5, --allow-all, deny rules, a throwaway COPILOT_HOME and a throwaway repo with no remote, 0.21 AI credits. The hook denied tmux -V and a create in baton's folder, and let a create and an edit elsewhere run. The hook's input showed toolArgs as objects for bash, create and edit (fact 36). The user's ~/.copilot and Keychain item were unchanged.

Known gaps

The guard stops a role's mistakes, not a role that sets out to get past it (decision 52, risks 2, 11 and 12): bash's own writes (into baton's folder, or over the binary that BATON_BIN names), a hard link to a file in baton's folder, data in a web_fetch URL, and writes to the plugin's cache, whose folder baton trusts by its name. The new step plugin-check closes the cache gap for the file tools and the next panes; belt is for the rest.

Decision 52 is a lead's call, flagged for the end review.

baton hook guard is the plugin's preToolUse hook. In a role's pane it
denies a bash call that the guard can't read or that runs a command
that the guard watches, but baton's commands for a role, and a create
or edit of a file in baton's folder, however its path names it. It
says nothing in the orchestrator's pane yet, and fails, so that
copilot denies, when it can't decide (decision 52).

A live check with the real copilot under --allow-all saw the hook deny
tmux and a create in baton's folder, and let other writes run (fact 36).
@sonarqubecloud

sonarqubecloud Bot commented Nov 3, 2026

Copy link
Copy Markdown

@Bakhomious
Bakhomious merged commit bf479dc into main Nov 3, 2026
3 checks passed
@Bakhomious
Bakhomious deleted the step/guard-hook branch November 3, 2026 22:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant