verb.go: attach WithReason at policy_denied + scope_unresolved sites #2

Open
opened 2026-05-23 20:53:51 +00:00 by coilysiren · 0 comments
Owner

Originally filed by @coilysiren on 2026-05-17T22:35:04Z - https://github.com/coilysiren/cli-guard/issues/72

Follow-up to coilysiren/cli-guard#60 / coilysiren/coily#168.

Two exitcode.New(...) sites in verb/verb.go are still missing .WithReason(...) chains, which forces consumers (coily) to keep a fallback why-line map alongside the constructor:

  • verb/verb.go:145 and verb/verb.go:151 - policy_denied from the shell-metacharacter gate.
  • verb/verb.go:160 - scope_unresolved when buildBaseRecord cannot resolve a commit scope.

Both kinds are high-frequency denials with well-known invariant-shaped reason strings. Today coily carries those reason strings in cmd/coily/denial_reason.go and looks them up by kind in emitErrorEnvelope as a fallback after errors.As(err, &exitcode.Reasoner). Once these sites chain .WithReason(...), the coily-side map can disappear entirely.

Fix - Add .WithReason(...) to the three sites with the reason strings currently stored in coily's denial_reason.go:

  • policy_denied: "policy gate caught a shell metacharacter in argv; the gate exists because some downstream verbs (tailscale ssh, aws ssm send-command) ship argv into a remote shell, and a per-tool toggle can't tell those verbs apart from the safe ones. Workarounds for the high-volume cases: --body 'markdown' -> --body-file /tmp/body.md (gh native); --change-batch '{...}' -> --change-batch file:///tmp/batch.json (aws native); --jq '...' -> external pipe (coily ops gh api X | jq '...') or coily-side --jq-file /tmp/q.jq"
  • scope_unresolved: "every audit row binds to a real commit scope; --commit-scope is the explicit hand-off when cwd cannot resolve one"

After it lands - bump coily's cli-guard ref, delete cmd/coily/denial_reason.go + its test, drop the fallback branch in emitErrorEnvelope.

_Originally filed by @coilysiren on 2026-05-17T22:35:04Z - [https://github.com/coilysiren/cli-guard/issues/72](https://github.com/coilysiren/cli-guard/issues/72)_ **Follow-up to coilysiren/cli-guard#60 / coilysiren/coily#168.** Two `exitcode.New(...)` sites in `verb/verb.go` are still missing `.WithReason(...)` chains, which forces consumers (coily) to keep a fallback why-line map alongside the constructor: - `verb/verb.go:145` and `verb/verb.go:151` - `policy_denied` from the shell-metacharacter gate. - `verb/verb.go:160` - `scope_unresolved` when buildBaseRecord cannot resolve a commit scope. Both kinds are high-frequency denials with well-known invariant-shaped reason strings. Today coily carries those reason strings in `cmd/coily/denial_reason.go` and looks them up by kind in `emitErrorEnvelope` as a fallback after `errors.As(err, &exitcode.Reasoner)`. Once these sites chain `.WithReason(...)`, the coily-side map can disappear entirely. **Fix** - Add `.WithReason(...)` to the three sites with the reason strings currently stored in coily's denial_reason.go: - `policy_denied`: "policy gate caught a shell metacharacter in argv; the gate exists because some downstream verbs (tailscale ssh, aws ssm send-command) ship argv into a remote shell, and a per-tool toggle can't tell those verbs apart from the safe ones. Workarounds for the high-volume cases: --body 'markdown' -> --body-file /tmp/body.md (gh native); --change-batch '{...}' -> --change-batch file:///tmp/batch.json (aws native); --jq '...' -> external pipe (coily ops gh api X | jq '...') or coily-side --jq-file /tmp/q.jq" - `scope_unresolved`: "every audit row binds to a real commit scope; --commit-scope is the explicit hand-off when cwd cannot resolve one" **After it lands** - bump coily's cli-guard ref, delete `cmd/coily/denial_reason.go` + its test, drop the fallback branch in `emitErrorEnvelope`.
coilysiren added
P2
and removed
P1
labels 2026-05-31 07:00:17 +00:00
Sign in to join this conversation.
No labels
P0
P1
P2
P3
P4
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
coilyco-flight-deck/cli-guard#2
No description provided.