Skip to content

fix(guard): note each unresolved command once - #200

Merged
AbysmalBiscuit merged 1 commit into
mainfrom
196-fix-guard-note-each-unresolved-command
Sep 27, 2026
Merged

AbysmalBiscuit merged 1 commit into
mainfrom
196-fix-guard-note-each-unresolved-command

Conversation

@AbysmalBiscuit

Copy link
Copy Markdown
Owner

TL;DR (human written)


Closes #196


Agent summary

The pre-tool-use guard repeated its "could not be fully resolved" notes once per loop iteration, and added the tasks-and-apps note to commands nothing in the project runs. A 4-iteration loop with five segments came back with 20 notes. Most of them named printf, jq or node, so the agent could not act on them.

Fix

  • decide keeps each distinct finding once per hook call. Blocks go through the same path, so cargo build repeated in a loop returns one deny reason instead of one per iteration.
  • The tasks-and-apps note now needs a program something could claim: a task run, an app launch, or a catalog dev server such as vite. Both sides are unwrapped the way the matcher does it, so a doppler run -- cargo build task still counts as cargo. When the program word itself is unresolved, the note stays whenever the project has tasks or apps.

That same loop now yields 2 notes: one for cargo build "$(date)" and one for the [harness.commands] rule node "$y" might hit.

A side effect: vite "$x" in a project with no tasks or apps now gets the note. The guard denies a resolved vite there too, so the note is accurate.

Tests

Three end-to-end tests in tests/harness_guard.rs send a hook payload through the devkit hook pre-tool-use binary:

  • The loop test failed before the fix with 9 notes, 3 of them distinct.
  • The printf/jq test failed before the fix because a printf note came back.
  • The keep-note test (cargo build "$x", nitro "$x", vite "$x", "$tool" build) passes both before and after. It checks that the fix does not drop notes it should keep.

Not fixed here

The guard redirects a filtered test run to a task: cargo nextest run --test harness_guard to test, and cargo test --test harness_guard to test-doc, which runs only doctests. A filtered run is not the task. That belongs in its own issue.


🤖 Generated with Claude Opus 5.5 (claude-opus-5-5) via Claude Code

The pre-tool-use guard pushed a finding per invocation with no
deduplication, so a loop repeated every unresolved note once per
iteration. It also added the tasks-and-apps note to any unresolved
command, including printf and jq, which nothing in the project runs.

decide now keeps each distinct finding once, blocks included. The
tasks-and-apps note needs a program some task run, app launch or
catalog entry could claim. An unresolved program word keeps the note
whenever the project has tasks or apps.

Closes #196

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@AbysmalBiscuit
AbysmalBiscuit merged commit 2bcd2fa into main Sep 27, 2026
13 checks passed
@AbysmalBiscuit
AbysmalBiscuit deleted the 196-fix-guard-note-each-unresolved-command branch September 27, 2026 19:55
AbysmalBiscuit added a commit that referenced this pull request Sep 27, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.14.7](v0.14.6...v0.14.7)
(2026-09-27)


### Features

* **brief:** add a rules section
([#160](#160))
([7ba41e2](7ba41e2))
* **brief:** list tasks by app and trim the brief
([#162](#162))
([d991929](d991929))
* **command:** model permission changes
([#174](#174))
([4aea742](4aea742))
* **config:** describe task args and variables
([#159](#159))
([34e0076](34e0076))
* **docm:** treeless clones and per-library excludes
([#157](#157))
([a4f4410](a4f4410))
* **forge:** support GitLab and Forgejo alongside GitHub
([#183](#183))
([1926908](1926908))
* **harness:** claim bounded shell write sets
([#158](#158))
([a72acf1](a72acf1))
* **hook:** parse Cursor and Antigravity payloads with pabal 0.2
([#191](#191))
([00e2918](00e2918))
* **hooks:** add before_worktree_remove
([#140](#140))
([1ea3aed](1ea3aed))
* **issue:** add dashboard chart aggregation modes
([#144](#144))
([61ad36a](61ad36a))
* **issue:** label the y-axis of dashboard charts
([#143](#143))
([99e7b29](99e7b29))
* **issues:** create issues from enforced templates
([#185](#185))
([3a38874](3a38874))
* **issue:** take the summary from the tracker
([#138](#138))
([30425fb](30425fb))
* **rules:** add rules add, edit and remove
([#150](#150))
([7052dd4](7052dd4))
* **rules:** honor repo-rules-agent.toml in query
([#151](#151))
([e7a1d1f](e7a1d1f))
* **template:** render templates on demand
([#165](#165))
([2da3c36](2da3c36))
* **templates:** add variable descriptions
([#152](#152))
([918f594](918f594))


### Bug Fixes

* **command:** end the fresh-path exemption on placement
([#125](#125))
([40ae328](40ae328))
* **common:** keep live blocks from climbing over output
([#141](#141))
([2e2b919](2e2b919))
* **git:** fail fast when ssh needs a prompt
([#142](#142))
([8fe7ae3](8fe7ae3))
* **guard:** note each unresolved command once
([#200](#200))
([2bcd2fa](2bcd2fa))
* **guard:** redirect only commands a task covers
([#198](#198))
([f6c0488](f6c0488))
* **hooks:** keep enforcement on past a broken layer
([#190](#190))
([8846a35](8846a35))
* **issue:** allow setup --slug without an issue id
([#127](#127))
([7c28ba7](7c28ba7))
* **issue:** dashboard spinners, x labels and status colors
([#149](#149))
([b35e2ed](b35e2ed))
* **issue:** hold worktrees past their merged PR
([#189](#189))
([a27a28b](a27a28b))
* **issue:** make status, info, end and MCP agree
([#193](#193))
([cd9b46a](cd9b46a))
* **issue:** report ambiguous branch PRs in review finish
([#192](#192))
([ff2cf62](ff2cf62))
* **locks:** free every root on release --all
([#199](#199))
([d55ba01](d55ba01))
* **tasks:** refuse a blank required arg
([#173](#173))
([d1b32b3](d1b32b3))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
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.

fix(guard): note each unresolved command once

1 participant