Skip to content

fix(v2): accept host-added deny rules in verifier ownership check - #84

Merged
william-ricchiuti merged 1 commit into
mainfrom
wr/v2-2.0.26-audit-verdict
Oct 19, 2026
Merged

william-ricchiuti merged 1 commit into
mainfrom
wr/v2-2.0.26-audit-verdict

Conversation

@william-ricchiuti

Copy link
Copy Markdown
Owner

Every completion audit fails on OpenCode 2.0.26. Found while verifying #81; main is affected too.

Root cause

OpenCode 2.0.26 appends {"action":"browser","resource":"*","effect":"deny"} to every agent. assertVerifierOwnership compared permissions by exact JSON, so it rejected the plugin's own goal-verify agent. With completionAudit: true, every completion was rejected. The real error was hidden because the auditor ignored the {error} response and reported "auditor returned no single clear final-line verdict".

Changes

  • permissionsOnlyNarrow: the owned rules must appear in order. Extra rules are accepted only if they are deny, which cannot widen access under first- or last-match evaluation. Changed, missing, reordered, allow or ask rules are still rejected.
  • Auditor: when the prompt returned {error} and still no verdict is found after the messages fallback, the reason is auditor prompt failed: <reason>. A verdict that reached the session history still wins.
  • scripts/live-v2-host.mjs: 2.0.26 exposes session.remove, so the verifier child is now deleted after its verdict. The script accepts retention (2.0.16) or removal (2.0.26).
  • CHANGELOG and docs/compatibility.md updated.

Verification

  • 2 new tests; both fail on main and pass here.
  • npm run release:check: exit 0 (492 tests, mutation 90/90, type/packed contracts, smoke, audit 0 vulns).
  • Live smoke:v2-host on 2.0.16 and 2.0.26, in normal mode and in GOAL_V2_FOREIGN_VERIFIER=1 mode: all pass. A foreign verifier still rejects completion on both.

Note: this PR and the #81 PR both add CHANGELOG "Unreleased" entries; whichever merges second needs a small changelog conflict resolved.

🤖 Generated with Claude Code

OpenCode 2.0.26 appends a browser deny to every agent, so the exact
permission comparison rejected the plugin's own goal-verify agent and
every completion audit failed. Accept extra deny rules only, surface
auditor prompt errors instead of "no verdict", and let the live smoke
accept verifier child removal on hosts that expose session.remove.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rodrigolopezguerra

Copy link
Copy Markdown

Thanks for #84. I hit the same rejection on OpenCode 2.0.32 (macOS arm64, plugin 0.11.0) and can confirm the browser deny rule is still there. There is a second case that #84 doesn't cover, and I think it affects a lot of people running unattended goals.

Config permission blocks are appended to the verifier too. A project (or user) opencode.json with a permission block makes the host append those rules after the plugin's 24 verifier rules. Allow rules are included. Reproduced with scripts/live-v2-host.mjs, adding this to the project opencode.json:

"permission": { "external_directory": { "*": "deny" }, "doom_loop": "deny", "edit": "allow",
  "read": { "*": "allow", "*.env": "deny" }, "bash": { "*": "allow", "git push*": "deny" } }

Live goal-verify permissions are the expected 24 rules in order, followed by:

Host Appended rules
2.0.16 external_directory * deny, doom_loop * deny, edit * allow, read .env deny, shell * allow, shell git push deny (31 total)
2.0.32 the same plus browser * deny (32 total)

Rejecting here is the right call: with last-match-wins, edit * allow and shell * allow would re-enable edit and shell for the verifier. The practical effect is still that the built-in verifier can't be used together with a permission block. That block is exactly what people add to sandbox unattended goal runs (external_directory: deny, git push: deny, and so on).

Some possible directions:

  1. Document the incompatibility and the workaround, if there is one. For example, should users move global permission rules into per-agent agent.build.permission, so nothing is appended to goal-verify? I haven't verified that this keeps config rules off the verifier.
  2. If the V2 plugin API lets the verifier agent opt out of inheriting config-level rules, register it that way.
  3. If not, make the startup check report the specific cause, e.g. "config permission grants edit/shell globally; the verifier cannot be isolated". The current message is "owned verifier agent registration was not confirmed".

Happy to test a branch on 2.0.32.

@william-ricchiuti
william-ricchiuti merged commit d6d35ba into main Oct 19, 2026
10 checks passed
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.

2 participants