YELLOW: spawn sub-Claude to resolve instead of falling through to dialog #19

Open
opened 2026-04-27 12:27:41 +00:00 by jbr870 · 0 comments
Owner

Summary

When the LLM scores a tool call as YELLOW, claude-permit currently falls through to Claude Code's built-in permission dialog, interrupting the user. Instead, claude-permit could spawn a separate claude --print sub-process that takes responsibility for resolving the YELLOW — proposing an alternative, asking the user a more focused question, or auto-resolving from policy — so the call never reaches the standard dialog.

User Stories Served

  • As a user, I want fewer raw permission dialogs, replaced by smarter resolutions ("we can do this safer way — proceeding") that I only have to intervene on when truly necessary.
  • As a maintainer, I want YELLOW handling to be programmable / scriptable rather than hard-coded into Claude Code's dialog flow.

Proposed Change

On YELLOW from the PreToolUse evaluator:

  1. Instead of returning passthrough (which triggers the dialog), spawn a sub-process: claude --print --model haiku with a dedicated resolver prompt.
  2. The resolver prompt receives:
    • The original tool call
    • The LLM's YELLOW reasoning
    • Active allow/deny rules + policy
    • The proposed alternative (if any — see related issue)
  3. The resolver returns one of:
    • approve — auto-approve, optionally with a stashed rule for PostToolUse promotion
    • approve_alternative — substitute a safer command (returned via decision.modify-style response if Claude Code supports it, otherwise stderr instruction back to calling Claude)
    • deny — escalate to RED with reasoning
    • ask_user — fall through to the original dialog only if nothing else worked, but with a much richer reason string

Why

Claude Code's permission dialog is binary (approve/deny on the exact call). It can't:

  • Suggest a rewrite
  • Explain at length
  • Look up project context
  • Negotiate scope ("approve this one but never again" vs "approve always")

A sub-Claude can do all of these. The dialog becomes the rare last resort.

Considerations

  • Latency: adds a second LLM call on the YELLOW path. Mitigation: cap with a short timeout; on timeout, fall through to the existing dialog (fail-open).
  • Cost: uses the user's subscription auth via claude --print, same as the current LLM call. Two haiku calls per YELLOW is acceptable.
  • Recursion: the sub-Claude must NOT trigger claude-permit hooks itself (or must do so with a special bypass flag) — otherwise infinite loop. Need an env-var or arg like CLAUDE_PERMIT_BYPASS=1.
  • Hook protocol limits: Claude Code's hook response schema may not currently support "rewrite the call." If not, the resolver's suggested alternative becomes a stderr message to the calling Claude rather than a true substitution.
  • UX: when the resolver acts silently, the user should see a brief, audit-style notification ("claude-permit resolved a YELLOW: rewrote XY") so the system stays observable. Consider routing through stderr or the audit log review skill (/audit).
  • Issue #5 (surface LLM reasoning) — would feed the resolver's input.
  • Sibling issue: "YELLOW propose alternative" — the resolver's first move would be to apply that proposed alternative.

Open Questions

  • Does Claude Code's hook protocol allow a hook to modify the tool call, or only approve/deny? If only approve/deny, the alternative-substitution path requires a different mechanism (e.g. the resolver writes the suggestion to a transcript file the calling Claude reads on its next turn).
  • Should the resolver have access to audit.jsonl history to learn user preferences ("user always denies this pattern → preemptively deny")?
  • Bypass-flag design: env-var, CLI flag, or settings.json entry? Needs to be unforgeable from prompt-injected input.
## Summary When the LLM scores a tool call as YELLOW, claude-permit currently falls through to Claude Code's built-in permission dialog, interrupting the user. Instead, claude-permit could spawn a **separate `claude --print` sub-process** that takes responsibility for resolving the YELLOW — proposing an alternative, asking the user a more focused question, or auto-resolving from policy — so the call never reaches the standard dialog. ## User Stories Served - As a user, I want fewer raw permission dialogs, replaced by smarter resolutions ("we can do this safer way — proceeding") that I only have to intervene on when truly necessary. - As a maintainer, I want YELLOW handling to be programmable / scriptable rather than hard-coded into Claude Code's dialog flow. ## Proposed Change On YELLOW from the PreToolUse evaluator: 1. Instead of returning passthrough (which triggers the dialog), spawn a sub-process: `claude --print --model haiku` with a dedicated *resolver prompt*. 2. The resolver prompt receives: - The original tool call - The LLM's YELLOW reasoning - Active allow/deny rules + policy - The proposed alternative (if any — see related issue) 3. The resolver returns one of: - `approve` — auto-approve, optionally with a stashed rule for PostToolUse promotion - `approve_alternative` — substitute a safer command (returned via `decision.modify`-style response if Claude Code supports it, otherwise stderr instruction back to calling Claude) - `deny` — escalate to RED with reasoning - `ask_user` — fall through to the original dialog *only if nothing else worked*, but with a much richer reason string ## Why Claude Code's permission dialog is binary (approve/deny on the exact call). It can't: - Suggest a rewrite - Explain at length - Look up project context - Negotiate scope ("approve this one but never again" vs "approve always") A sub-Claude can do all of these. The dialog becomes the rare last resort. ## Considerations - **Latency:** adds a second LLM call on the YELLOW path. Mitigation: cap with a short timeout; on timeout, fall through to the existing dialog (fail-open). - **Cost:** uses the user's subscription auth via `claude --print`, same as the current LLM call. Two haiku calls per YELLOW is acceptable. - **Recursion:** the sub-Claude must NOT trigger claude-permit hooks itself (or must do so with a special bypass flag) — otherwise infinite loop. Need an env-var or arg like `CLAUDE_PERMIT_BYPASS=1`. - **Hook protocol limits:** Claude Code's hook response schema may not currently support "rewrite the call." If not, the resolver's suggested alternative becomes a stderr message to the calling Claude rather than a true substitution. - **UX:** when the resolver acts silently, the user should see a brief, audit-style notification ("claude-permit resolved a YELLOW: rewrote `X` → `Y`") so the system stays observable. Consider routing through stderr or the audit log review skill (`/audit`). ## Related - Issue #5 (surface LLM reasoning) — would feed the resolver's input. - Sibling issue: "YELLOW propose alternative" — the resolver's first move would be to apply that proposed alternative. ## Open Questions - Does Claude Code's hook protocol allow a hook to *modify* the tool call, or only approve/deny? If only approve/deny, the alternative-substitution path requires a different mechanism (e.g. the resolver writes the suggestion to a transcript file the calling Claude reads on its next turn). - Should the resolver have access to `audit.jsonl` history to learn user preferences ("user always denies this pattern → preemptively deny")? - Bypass-flag design: env-var, CLI flag, or settings.json entry? Needs to be unforgeable from prompt-injected input.
Sign in to join this conversation.
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
jbr870/claude-permit#19
No description provided.