No way to see which permission rule, hook or classifier decided a tool call
Summary
There is no way to see, after the fact, which permission rule or hook decision resolved a tool call. Nothing in --debug, the transcript JSONL, or any hook event records "this call was allowed by rule X" / "asked because of rule Y" / "hook Z returned ask". Maintaining a non-trivial permission configuration (we have ~250 Bash rules across three settings files plus a PreToolUse hook) is guesswork without it.
What is missing
For each tool call, a record of:
- the final outcome (allow / ask-then-approved / ask-then-denied / deny)
- what produced it: the matching rule's text and which settings file it came from, the hook and its
permissionDecision, or the auto-mode classifier - for
ask, whether the prompt was shown or resolved automatically
Either an opt-in log file (a permissions.decisionLog path, or lines in the existing debug log) or a hook event fired after the permission outcome is decided (PostPermission or similar, with the same payload) would do. A hook event is more general, since it can feed any sink.
Why it matters
Three things we have found by accident that a decision log would have shown on day one:
- A broad
askrule in~/.claude/settings.jsonsilently voided a narrowerallowin the project file for weeks. The docs are clear that lists merge andaskbeatsallow, but nothing at runtime says "this allow never matches because that ask does". - Seven
mcp__render__*ask rules matched nothing after the server became a plugin (tools were renamespaced tomcp__plugin_render_render__*). A rule that matches nothing looks identical to one that never had to fire. - A PreToolUse hook
allowfortrashinside a worktree was overruled on every call by aBash(trash *)ask rule for months. The hook's reason was never displayed, so the dialog looked like a rule prompt with no way to tell which layer raised it.
In all three, the only way to distinguish "the rule fired" from "the rule is dead" was to reason about pattern text, because approvals are not observable and the model cannot see whether a call was gated.
We now log our own hook's decisions to a file from inside the hook, but that covers one layer of three and cannot see rules or the classifier.
Environment
Claude Code 2.1.274, macOS, default permission mode auto, seatbelt sandbox enabled.
Related
- #86754 and #89561 are both about
askdecisions being resolved without a visible prompt; a decision log would make that class of bug diagnosable from the user's side.