From 4439198719f96b5ec122c463dc640f44cd0f157c Mon Sep 17 00:00:00 2001 From: Rian Stockbower Date: Fri, 17 Jul 2026 07:27:03 -0400 Subject: [PATCH 1/2] fix(llm): run Claude background reviewers in auto permission mode Background Claude tasks launched with --permission-mode acceptEdits could block on read-only Bash requests (e.g. git diff): enabling the Bash tool does not approve its execution, so the reviewer stalled until the task timeout. Switch the --bg transport to --permission-mode auto. Foreground print mode (-p) keeps acceptEdits. validateArgs now enforces the transport-specific mode: auto with --bg, acceptEdits with -p, and rejects the wrong pairing for either transport. The issue text also proposed --enable-auto-mode; that flag was removed in claude CLI v2.1.111 (--permission-mode auto is the replacement), so it is deliberately not passed and not allowlisted. Closes #515 --- internal/llmadapters/subprocess.go | 8 ++++++-- internal/llmadapters/subprocess_test.go | 23 +++++++++++++---------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/internal/llmadapters/subprocess.go b/internal/llmadapters/subprocess.go index bed68c27..4edd6298 100644 --- a/internal/llmadapters/subprocess.go +++ b/internal/llmadapters/subprocess.go @@ -596,7 +596,7 @@ func (a *SubprocessAdapter) buildArgsForSession(req Request, scratch string, res args := []string{ "--bg", "--tools", tools, - "--permission-mode", "acceptEdits", + "--permission-mode", "auto", "--add-dir", scratch, } if workspace := req.ReviewerWorkspace; workspace != nil { @@ -708,9 +708,13 @@ func (a *SubprocessAdapter) validateArgs(args []string, scratch string, req Requ if req.ReviewerWorkspace != nil { requiredTools = "Read,Write,Bash" } + requiredPermissionMode := "acceptEdits" + if containsFlag(checkedArgs, "--bg") { + requiredPermissionMode = "auto" + } required := map[string]string{ "--tools": requiredTools, - "--permission-mode": "acceptEdits", + "--permission-mode": requiredPermissionMode, } for flag, value := range required { got, ok := flagValueOK(checkedArgs, flag) diff --git a/internal/llmadapters/subprocess_test.go b/internal/llmadapters/subprocess_test.go index c6f866e5..923157a6 100644 --- a/internal/llmadapters/subprocess_test.go +++ b/internal/llmadapters/subprocess_test.go @@ -92,7 +92,7 @@ func TestSubprocessClaudeBackgroundLaunchSafety(t *testing.T) { t.Fatalf("args = %#v, want --bg", record.AdapterArgs) } assertFlagValue(t, record.AdapterArgs, "--tools", "Read,Write") - assertFlagValue(t, record.AdapterArgs, "--permission-mode", "acceptEdits") + assertFlagValue(t, record.AdapterArgs, "--permission-mode", "auto") assertFlagValue(t, record.AdapterArgs, "--model", "claude-sonnet-4-6") assertFlagValue(t, record.AdapterArgs, "--effort", "high") if containsFlag(record.AdapterArgs, "--settings") { @@ -2003,22 +2003,25 @@ func TestClaudeTransportXORValidation(t *testing.T) { adapter := NewClaudeCLIAdapter(SubprocessOptions{}) base := []string{ "--tools", "Read,Write", - "--permission-mode", "acceptEdits", "--add-dir", scratch, "--", "positional prompt", } cases := []struct { - name string - prefix []string - wantErr bool + name string + prefix []string + permissionMode string + wantErr bool }{ - {"bg only", []string{"--bg"}, false}, - {"foreground only", []string{"-p", "--output-format", "json"}, false}, - {"both transports", []string{"--bg", "-p", "--output-format", "json"}, true}, - {"neither transport", nil, true}, + {"bg only", []string{"--bg"}, "auto", false}, + {"foreground only", []string{"-p", "--output-format", "json"}, "acceptEdits", false}, + {"acceptEdits with bg", []string{"--bg"}, "acceptEdits", true}, + {"auto with foreground", []string{"-p", "--output-format", "json"}, "auto", true}, + {"both transports", []string{"--bg", "-p", "--output-format", "json"}, "auto", true}, + {"neither transport", nil, "acceptEdits", true}, } for _, tc := range cases { - args := append(append([]string(nil), tc.prefix...), base...) + args := append(append([]string(nil), tc.prefix...), "--permission-mode", tc.permissionMode) + args = append(args, base...) err := adapter.validateArgs(args, scratch, Request{}) if tc.wantErr && !errors.Is(err, ErrUnsafeSubprocessConfig) { t.Fatalf("%s: err = %v, want ErrUnsafeSubprocessConfig", tc.name, err) From 96cc1ac7d0875b24f6c3827f0c3d897d7482b3ae Mon Sep 17 00:00:00 2001 From: Rian Stockbower Date: Fri, 17 Jul 2026 09:08:42 -0400 Subject: [PATCH 2/2] test(llm): assert permission mode on background resume path --- internal/llmadapters/subprocess_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/llmadapters/subprocess_test.go b/internal/llmadapters/subprocess_test.go index 923157a6..5e237d2c 100644 --- a/internal/llmadapters/subprocess_test.go +++ b/internal/llmadapters/subprocess_test.go @@ -191,6 +191,7 @@ func TestSubprocessClaudeBackgroundResume(t *testing.T) { assertFlagValue(t, record.AdapterArgs, "--resume", "prior-session") assertFlagValue(t, record.AdapterArgs, "--model", "claude-sonnet-4-6") assertFlagValue(t, record.AdapterArgs, "--effort", "high") + assertFlagValue(t, record.AdapterArgs, "--permission-mode", "auto") if record.StdinBytes != 0 { t.Fatalf("stdin bytes = %d, want empty stdin for resumed Claude bg prompt", record.StdinBytes) }