From 1a31cf41286f3e2d19d8f346e94b958535a9f986 Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Fri, 25 Sep 2026 09:52:50 -0700 Subject: [PATCH 1/3] Add Auto permission mode Claude Code now ships `--permission-mode auto`, where a safety classifier approves routine actions and blocks risky ones without prompting. Expose it as a fifth mode between Accept Edits and Bypass: terminals, resumed sessions, the tab badge, and the scheduler's task runner all map it to the flag. Headless scheduled runs don't stall in Auto (blocked actions are denied, not prompted), so the task editor shows a softer note instead of the stall warning. Co-Authored-By: Claude Opus 5.5 (1M context) --- HOW-TO-USE.md | 16 ++++++++++++---- README.md | 5 +++-- TECHNICAL.md | 5 +++-- app/src-tauri/src/commands/terminal_commands.rs | 14 ++++++++++++++ app/src-tauri/src/models/project.rs | 5 +++++ app/src/components/layout/MainTabs.tsx | 1 + .../projects/PermissionModeControl.test.tsx | 12 ++++++++++-- .../projects/PermissionModeControl.tsx | 5 +++++ .../projects/home/TaskEditorModal.test.tsx | 8 +++++++- .../components/projects/home/TaskEditorModal.tsx | 12 +++++++++--- app/src/lib/types.ts | 2 +- container/triple-c-task-runner | 6 ++++-- 12 files changed, 74 insertions(+), 17 deletions(-) diff --git a/HOW-TO-USE.md b/HOW-TO-USE.md index 3236da7..ce29584 100644 --- a/HOW-TO-USE.md +++ b/HOW-TO-USE.md @@ -469,6 +469,7 @@ replaces the old Full Permissions on/off switch. | **Plan** | Proposes a plan and makes no changes | `--permission-mode plan` | | **Default** | Asks before each tool call | *(nothing — Claude Code's own default)* | | **Accept Edits** | Auto-approves file edits; other tools still prompt | `--permission-mode acceptEdits` | +| **Auto** | A safety classifier approves routine actions and blocks risky ones, without prompting | `--permission-mode auto` | | **Bypass** | Auto-approves every tool call | `--dangerously-skip-permissions` | New projects start in **Default**. Projects created before permission modes existed keep behaving @@ -480,12 +481,17 @@ the way they did: one that had Full Permissions on becomes **Bypass**, one that > has Docker socket access or reaches services on your network. The Overview tab tells you whether > the in-container sandbox is also on. +**Auto** sits between Accept Edits and Bypass: Claude Code's own classifier reviews each action, +lets routine work through and blocks things that look risky (such as destructive or +exfiltrating commands) — no prompts either way. Whether it is available depends on your Claude +Code account and model; see Claude Code's documentation on permission modes. + ### When a change takes effect - **Terminals** — the mode is applied when a terminal is opened, so it affects terminals you open from then on. A Claude session that is already running keeps the permissions it started with; close the tab and open a new terminal to change it. The badge on each terminal tab shows the mode - that terminal was launched with (`plan`, `ask`, `edits`, `bypass`). + that terminal was launched with (`plan`, `ask`, `edits`, `auto`, `bypass`). - **Resumed sessions** — a session resumed from the **Sessions** tab uses the project's current mode. - **Scheduled tasks** — these now honour the permission mode too (they previously always ran with @@ -494,8 +500,9 @@ the way they did: one that had Full Permissions on becomes **Bypass**, one that mode change to reach the scheduler. > Scheduled tasks run headless (`claude -p`) and cannot answer a permission prompt. In any mode -> other than **Bypass**, a task may simply stop early when Claude Code asks for approval. Its run -> log records which mode it used. +> other than **Auto** or **Bypass**, a task may simply stop early when Claude Code asks for +> approval. In **Auto**, actions the classifier blocks are denied and the run carries on without +> them. Its run log records which mode it used. --- @@ -1349,7 +1356,8 @@ Scheduled runs use the project's [permission mode](#permission-modes) — they n with `--dangerously-skip-permissions`. Because the mode travels into the container as an environment variable, **stop and start the project** after changing it for the scheduler to see the change. Remember that a headless run cannot answer a permission prompt, so in any mode other than -**Bypass** a task may stop early when Claude Code asks for approval; the run log records the mode +**Auto** or **Bypass** a task may stop early when Claude Code asks for approval (in Auto, blocked +actions are denied instead); the run log records the mode that was used. ### Creating Tasks diff --git a/README.md b/README.md index 7245a8c..ce6435f 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ progress modal. ## Permission Modes -`PermissionMode` in `models/project.rs` replaces the old `full_permissions` boolean. Four states, +`PermissionMode` in `models/project.rs` replaces the old `full_permissions` boolean. Five states, mapped to CLI flags by `PermissionMode::cli_args()`: | Mode | Serialized | CLI args passed to `claude` | @@ -122,6 +122,7 @@ mapped to CLI flags by `PermissionMode::cli_args()`: | **Plan** | `plan` | `--permission-mode plan` | | **Default** | `default` | *(none)* | | **Accept Edits** | `acceptEdits` | `--permission-mode acceptEdits` | +| **Auto** | `auto` | `--permission-mode auto` | | **Bypass** | `bypass` | `--dangerously-skip-permissions` | `Project.permission_mode` is `Option`; `effective_permission_mode()` falls back to @@ -531,7 +532,7 @@ Triple-C includes optional speech-to-text powered by [Faster Whisper](https://gi | `app/src/components/layout/StatusBar.tsx` | Project/terminal counts, Notes toggle, STT mic | | `app/src/components/projects/ProjectRow.tsx` | Select-only sidebar row; opens Project Home, with hover start/stop and terminal controls | | `app/src/components/projects/ProjectList.tsx` | Project list in sidebar | -| `app/src/components/projects/PermissionModeControl.tsx` | Plan / Default / Accept Edits / Bypass segmented control | +| `app/src/components/projects/PermissionModeControl.tsx` | Plan / Default / Accept Edits / Auto / Bypass segmented control | | `app/src/components/ui/` | Shared primitives: `Modal`, `Button`, `Toggle`, `Field`, `SegmentedControl`, `StatusIndicator`, `SaveIndicator`, `OverflowMenu`, `ToastHost`, `Tooltip` | | `app/src/hooks/useKeyboardShortcuts.ts` | `Ctrl+T`, `Ctrl+Shift+W`, `Ctrl+Tab`, `Ctrl+1..9`, `Ctrl+Shift+←/→` | | `app/src/hooks/useContainerProgress.ts` | `container-progress` event → inline progress lines | diff --git a/TECHNICAL.md b/TECHNICAL.md index 1911733..3111fd9 100644 --- a/TECHNICAL.md +++ b/TECHNICAL.md @@ -186,7 +186,7 @@ host keychain secrets. ### Permission Modes -`PermissionMode` (`models/project.rs`) is a four-state enum replacing the earlier `full_permissions` +`PermissionMode` (`models/project.rs`) is a five-state enum replacing the earlier `full_permissions` boolean. It reaches Claude Code by two different routes: | Mode | `cli_args()` — interactive terminals | `as_env_value()` — scheduler | @@ -194,6 +194,7 @@ boolean. It reaches Claude Code by two different routes: | `Plan` | `--permission-mode plan` | `plan` | | `Default` | *(no flag)* | `default` | | `AcceptEdits` | `--permission-mode acceptEdits` | `acceptEdits` | +| `Auto` | `--permission-mode auto` | `auto` | | `Bypass` | `--dangerously-skip-permissions` | `bypass` | `Project.permission_mode` is `Option`, and `effective_permission_mode()` resolves @@ -474,7 +475,7 @@ triple-c/ │ │ ├── ProjectRow.tsx # Select-only sidebar row │ │ ├── ProjectList.tsx # Sidebar project list │ │ ├── AddProjectDialog.tsx # New-project dialog - │ │ ├── PermissionModeControl.tsx # Plan/Default/Accept Edits/Bypass + │ │ ├── PermissionModeControl.tsx # Plan/Default/Accept Edits/Auto/Bypass │ │ ├── ConfirmRemoveModal.tsx # Project removal confirmation │ │ └── *Editor.tsx / *Modal.tsx # EnvVars, PortMappings, │ │ # ClaudeInstructions, ClaudeCodeSettings — diff --git a/app/src-tauri/src/commands/terminal_commands.rs b/app/src-tauri/src/commands/terminal_commands.rs index f7e1cd1..1d94b87 100644 --- a/app/src-tauri/src/commands/terminal_commands.rs +++ b/app/src-tauri/src/commands/terminal_commands.rs @@ -469,6 +469,20 @@ mod tests { assert!(!cmd[2].contains(" -n "), "empty name must add no flag: {}", cmd[2]); } + /// Auto mode is passed as a `--permission-mode` value, not its own flag. + #[test] + fn build_terminal_cmd_passes_auto_permission_mode() { + let mut p = project("anthropic", serde_json::Value::Null); + p.permission_mode = Some(crate::models::project::PermissionMode::Auto); + let cmd = build_claude_terminal_cmd(&p, None, None); + + assert!( + cmd[2].contains("exec claude '--permission-mode' 'auto'"), + "got: {}", + cmd[2] + ); + } + /// The Bedrock-profile path keeps its AWS validation *and* gains the /// prelude, immediately before the exec. #[test] diff --git a/app/src-tauri/src/models/project.rs b/app/src-tauri/src/models/project.rs index 70905fd..0f32161 100644 --- a/app/src-tauri/src/models/project.rs +++ b/app/src-tauri/src/models/project.rs @@ -166,6 +166,9 @@ pub enum PermissionMode { Default, /// Auto-accept file edits, prompt for everything else. AcceptEdits, + /// Claude Code's classifier approves safe actions and blocks risky ones, + /// without prompting. + Auto, /// Skip all permission prompts. Bypass, } @@ -180,6 +183,7 @@ impl PermissionMode { PermissionMode::AcceptEdits => { vec!["--permission-mode".to_string(), "acceptEdits".to_string()] } + PermissionMode::Auto => vec!["--permission-mode".to_string(), "auto".to_string()], PermissionMode::Bypass => vec!["--dangerously-skip-permissions".to_string()], } } @@ -191,6 +195,7 @@ impl PermissionMode { PermissionMode::Plan => "plan", PermissionMode::Default => "default", PermissionMode::AcceptEdits => "acceptEdits", + PermissionMode::Auto => "auto", PermissionMode::Bypass => "bypass", } } diff --git a/app/src/components/layout/MainTabs.tsx b/app/src/components/layout/MainTabs.tsx index ef90553..bf356e2 100644 --- a/app/src/components/layout/MainTabs.tsx +++ b/app/src/components/layout/MainTabs.tsx @@ -26,6 +26,7 @@ const MODE_BADGE: Record = plan: { text: "plan", className: "bg-[var(--bg-tertiary)] text-[var(--text-secondary)]" }, default: { text: "ask", className: "bg-[var(--bg-tertiary)] text-[var(--text-secondary)]" }, acceptEdits: { text: "edits", className: "bg-[var(--accent-muted)] text-[var(--accent)]" }, + auto: { text: "auto", className: "bg-[var(--accent-muted)] text-[var(--accent)]" }, bypass: { text: "bypass", className: "bg-[var(--warning-muted)] text-[var(--warning)]" }, }; diff --git a/app/src/components/projects/PermissionModeControl.test.tsx b/app/src/components/projects/PermissionModeControl.test.tsx index 84f115e..5249904 100644 --- a/app/src/components/projects/PermissionModeControl.test.tsx +++ b/app/src/components/projects/PermissionModeControl.test.tsx @@ -75,11 +75,11 @@ describe("PermissionModeControl", () => { vi.clearAllMocks(); }); - it("renders all four modes as a radio group with the effective one checked", () => { + it("renders all five modes as a radio group with the effective one checked", () => { render(); const group = screen.getByRole("radiogroup", { name: "Permission mode" }); expect(group).toBeInTheDocument(); - expect(screen.getAllByRole("radio")).toHaveLength(4); + expect(screen.getAllByRole("radio")).toHaveLength(5); expect(screen.getByRole("radio", { name: "Default" })).toHaveAttribute( "aria-checked", "true", @@ -92,6 +92,14 @@ describe("PermissionModeControl", () => { expect(onChange).toHaveBeenCalledWith("acceptEdits"); }); + it("offers Auto between Accept Edits and Bypass", () => { + render(); + const labels = screen.getAllByRole("radio").map((r) => r.textContent); + expect(labels).toEqual(["Plan", "Default", "Accept Edits", "Auto", "Bypass"]); + fireEvent.click(screen.getByRole("radio", { name: "Auto" })); + expect(onChange).toHaveBeenCalledWith("auto"); + }); + it("moves selection with the arrow keys", () => { render(); fireEvent.keyDown(screen.getByRole("radiogroup", { name: "Permission mode" }), { diff --git a/app/src/components/projects/PermissionModeControl.tsx b/app/src/components/projects/PermissionModeControl.tsx index e72930f..8d32012 100644 --- a/app/src/components/projects/PermissionModeControl.tsx +++ b/app/src/components/projects/PermissionModeControl.tsx @@ -9,6 +9,11 @@ export const PERMISSION_MODES: Segment[] = [ label: "Accept Edits", hint: "File edits are auto-approved; other tools still prompt.", }, + { + value: "auto", + label: "Auto", + hint: "A safety classifier approves routine actions and blocks risky ones, without prompting.", + }, { value: "bypass", label: "Bypass", diff --git a/app/src/components/projects/home/TaskEditorModal.test.tsx b/app/src/components/projects/home/TaskEditorModal.test.tsx index d0c15ef..68336e7 100644 --- a/app/src/components/projects/home/TaskEditorModal.test.tsx +++ b/app/src/components/projects/home/TaskEditorModal.test.tsx @@ -159,12 +159,18 @@ describe("TaskEditorModal", () => { }); it("warns that a headless run cannot answer a permission prompt", async () => { - // Bypass is the only mode where an unattended run is safe from stalling. + // Bypass (and Auto, below) are the modes where an unattended run cannot stall. await renderEditor(null, { ...baseProject, permission_mode: "bypass" }); expect(screen.getByText(/headless/i)).toBeInTheDocument(); expect(screen.queryByText(/cannot answer a permission prompt/i)).toBeNull(); }); + it("tells Auto mode that blocked actions are denied, not prompted", async () => { + await renderEditor(null, { ...baseProject, permission_mode: "auto" }); + expect(screen.queryByText(/cannot answer a permission prompt/i)).toBeNull(); + expect(screen.getByText(/blocks are denied/i)).toBeInTheDocument(); + }); + it("spells out the stall risk in any non-Bypass mode", async () => { await renderEditor(null, { ...baseProject, permission_mode: "default" }); expect(screen.getByText(/cannot answer a permission prompt/i)).toBeInTheDocument(); diff --git a/app/src/components/projects/home/TaskEditorModal.tsx b/app/src/components/projects/home/TaskEditorModal.tsx index 09a57ca..4bed6cc 100644 --- a/app/src/components/projects/home/TaskEditorModal.tsx +++ b/app/src/components/projects/home/TaskEditorModal.tsx @@ -301,11 +301,17 @@ export default function TaskEditorModal({ project, task, onClose, onSaved }: Pro terminal attached, using this project’s permission mode ( {modeLabel}).

- {mode !== "bypass" && ( + {mode === "auto" && ( +

+ In Auto mode nothing prompts: actions the safety classifier blocks are denied and the + run carries on without them, so check the log if a task seems to have skipped a step. +

+ )} + {mode !== "bypass" && mode !== "auto" && (

A headless run cannot answer a permission prompt. In {modeLabel} mode the task may - stall and produce an empty log; set the mode to Bypass in the Config tab for - unattended runs. + stall and produce an empty log; set the mode to Auto or Bypass in the Config tab + for unattended runs.

)} diff --git a/app/src/lib/types.ts b/app/src/lib/types.ts index 54bf911..849a5bf 100644 --- a/app/src/lib/types.ts +++ b/app/src/lib/types.ts @@ -126,7 +126,7 @@ export const CUSTOM_ENDPOINT_BACKENDS: readonly Backend[] = [ ]; /** Mirrors Rust `PermissionMode` (serde camelCase). */ -export type PermissionMode = "plan" | "default" | "acceptEdits" | "bypass"; +export type PermissionMode = "plan" | "default" | "acceptEdits" | "auto" | "bypass"; export type BedrockAuthMethod = "static_credentials" | "profile" | "bearer_token"; diff --git a/container/triple-c-task-runner b/container/triple-c-task-runner index 7ce583b..ceae1e8 100644 --- a/container/triple-c-task-runner +++ b/container/triple-c-task-runner @@ -61,12 +61,14 @@ TASK_TYPE=$(jq -r '.type' "$TASK_FILE") # project's permission setting. Keep this mapping in sync with # PermissionMode::cli_args() in app/src-tauri/src/models/project.rs. # NOTE: headless `claude -p` runs cannot answer a permission prompt, so any -# mode other than "bypass" means the task may stop early when Claude Code asks -# for permission. Unset or unrecognized values pass no flag (Claude's default). +# mode other than "bypass" or "auto" means the task may stop early when Claude +# Code asks for permission. In "auto", actions the classifier blocks are +# denied rather than prompted, so the run continues without them. Unset or unrecognized values pass no flag (Claude's default). PERMISSION_ARGS=() case "${TRIPLE_C_PERMISSION_MODE:-}" in plan) PERMISSION_ARGS=(--permission-mode plan) ;; acceptEdits) PERMISSION_ARGS=(--permission-mode acceptEdits) ;; + auto) PERMISSION_ARGS=(--permission-mode auto) ;; bypass) PERMISSION_ARGS=(--dangerously-skip-permissions) ;; *) PERMISSION_ARGS=() ;; esac -- 2.52.0 From 99d8493008bf70e1e20d305928b236c317bb980e Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Fri, 25 Sep 2026 09:54:29 -0700 Subject: [PATCH 2/3] Warn that Auto falls back to prompting when unavailable Claude Code starts in its prompting mode when auto isn't available for the session's model or backend, so a headless Auto task can still stall. Say so in the task editor, the task runner comment, and HOW-TO-USE. Co-Authored-By: Claude Opus 5.5 (1M context) --- HOW-TO-USE.md | 8 +++++--- app/src/components/projects/home/TaskEditorModal.test.tsx | 3 ++- app/src/components/projects/home/TaskEditorModal.tsx | 7 ++++--- container/triple-c-task-runner | 7 ++++--- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/HOW-TO-USE.md b/HOW-TO-USE.md index ce29584..d2b97ab 100644 --- a/HOW-TO-USE.md +++ b/HOW-TO-USE.md @@ -484,7 +484,8 @@ the way they did: one that had Full Permissions on becomes **Bypass**, one that **Auto** sits between Accept Edits and Bypass: Claude Code's own classifier reviews each action, lets routine work through and blocks things that look risky (such as destructive or exfiltrating commands) — no prompts either way. Whether it is available depends on your Claude -Code account and model; see Claude Code's documentation on permission modes. +Code account, model and backend (local and OpenAI-compatible backends won't qualify). When it +isn't available, Claude Code quietly starts in its normal prompting mode instead. ### When a change takes effect @@ -502,7 +503,8 @@ Code account and model; see Claude Code's documentation on permission modes. > Scheduled tasks run headless (`claude -p`) and cannot answer a permission prompt. In any mode > other than **Auto** or **Bypass**, a task may simply stop early when Claude Code asks for > approval. In **Auto**, actions the classifier blocks are denied and the run carries on without -> them. Its run log records which mode it used. +> them — but if Auto isn't available for the project's model or backend, Claude Code falls back +> to prompting and the task can stall the same way. Its run log records which mode it used. --- @@ -1357,7 +1359,7 @@ with `--dangerously-skip-permissions`. Because the mode travels into the contain environment variable, **stop and start the project** after changing it for the scheduler to see the change. Remember that a headless run cannot answer a permission prompt, so in any mode other than **Auto** or **Bypass** a task may stop early when Claude Code asks for approval (in Auto, blocked -actions are denied instead); the run log records the mode +actions are denied instead, unless Auto is unavailable and Claude Code falls back to prompting); the run log records the mode that was used. ### Creating Tasks diff --git a/app/src/components/projects/home/TaskEditorModal.test.tsx b/app/src/components/projects/home/TaskEditorModal.test.tsx index 68336e7..62e942e 100644 --- a/app/src/components/projects/home/TaskEditorModal.test.tsx +++ b/app/src/components/projects/home/TaskEditorModal.test.tsx @@ -165,10 +165,11 @@ describe("TaskEditorModal", () => { expect(screen.queryByText(/cannot answer a permission prompt/i)).toBeNull(); }); - it("tells Auto mode that blocked actions are denied, not prompted", async () => { + it("tells Auto mode that blocked actions are denied, and warns of the fallback", async () => { await renderEditor(null, { ...baseProject, permission_mode: "auto" }); expect(screen.queryByText(/cannot answer a permission prompt/i)).toBeNull(); expect(screen.getByText(/blocks are denied/i)).toBeInTheDocument(); + expect(screen.getByText(/falls back to prompting/i)).toBeInTheDocument(); }); it("spells out the stall risk in any non-Bypass mode", async () => { diff --git a/app/src/components/projects/home/TaskEditorModal.tsx b/app/src/components/projects/home/TaskEditorModal.tsx index 4bed6cc..90dc845 100644 --- a/app/src/components/projects/home/TaskEditorModal.tsx +++ b/app/src/components/projects/home/TaskEditorModal.tsx @@ -302,9 +302,10 @@ export default function TaskEditorModal({ project, task, onClose, onSaved }: Pro {modeLabel}).

{mode === "auto" && ( -

- In Auto mode nothing prompts: actions the safety classifier blocks are denied and the - run carries on without them, so check the log if a task seems to have skipped a step. +

+ In Auto mode, actions the safety classifier blocks are denied and the run carries on + without them. If Auto isn’t available for this project’s model or backend, + Claude Code falls back to prompting and the task may stall.

)} {mode !== "bypass" && mode !== "auto" && ( diff --git a/container/triple-c-task-runner b/container/triple-c-task-runner index ceae1e8..ac5922a 100644 --- a/container/triple-c-task-runner +++ b/container/triple-c-task-runner @@ -61,9 +61,10 @@ TASK_TYPE=$(jq -r '.type' "$TASK_FILE") # project's permission setting. Keep this mapping in sync with # PermissionMode::cli_args() in app/src-tauri/src/models/project.rs. # NOTE: headless `claude -p` runs cannot answer a permission prompt, so any -# mode other than "bypass" or "auto" means the task may stop early when Claude -# Code asks for permission. In "auto", actions the classifier blocks are -# denied rather than prompted, so the run continues without them. Unset or unrecognized values pass no flag (Claude's default). +# mode other than "bypass" means the task may stop early when Claude Code asks +# for permission. In "auto", classifier-blocked actions are denied instead of +# prompted, but if auto mode is unavailable for the session's model/backend, +# Claude Code falls back to prompting and the same stall applies. Unset or unrecognized values pass no flag (Claude's default). PERMISSION_ARGS=() case "${TRIPLE_C_PERMISSION_MODE:-}" in plan) PERMISSION_ARGS=(--permission-mode plan) ;; -- 2.52.0 From e198edfb7cf02d7c748b4ff1d25b75dbc48a4ce2 Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Fri, 25 Sep 2026 09:55:00 -0700 Subject: [PATCH 3/3] Soften backend claim for Auto availability; rewrap comments Co-Authored-By: Claude Opus 5.5 (1M context) --- HOW-TO-USE.md | 8 +++++--- container/triple-c-task-runner | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/HOW-TO-USE.md b/HOW-TO-USE.md index d2b97ab..f135150 100644 --- a/HOW-TO-USE.md +++ b/HOW-TO-USE.md @@ -484,8 +484,9 @@ the way they did: one that had Full Permissions on becomes **Bypass**, one that **Auto** sits between Accept Edits and Bypass: Claude Code's own classifier reviews each action, lets routine work through and blocks things that look risky (such as destructive or exfiltrating commands) — no prompts either way. Whether it is available depends on your Claude -Code account, model and backend (local and OpenAI-compatible backends won't qualify). When it -isn't available, Claude Code quietly starts in its normal prompting mode instead. +Code account, model and backend (local and OpenAI-compatible backends usually won't +qualify). When it isn't available, Claude Code quietly starts in its normal prompting mode +instead. ### When a change takes effect @@ -1359,7 +1360,8 @@ with `--dangerously-skip-permissions`. Because the mode travels into the contain environment variable, **stop and start the project** after changing it for the scheduler to see the change. Remember that a headless run cannot answer a permission prompt, so in any mode other than **Auto** or **Bypass** a task may stop early when Claude Code asks for approval (in Auto, blocked -actions are denied instead, unless Auto is unavailable and Claude Code falls back to prompting); the run log records the mode +actions are denied instead, unless Auto is unavailable and Claude Code falls back to +prompting); the run log records the mode that was used. ### Creating Tasks diff --git a/container/triple-c-task-runner b/container/triple-c-task-runner index ac5922a..cac8748 100644 --- a/container/triple-c-task-runner +++ b/container/triple-c-task-runner @@ -64,7 +64,8 @@ TASK_TYPE=$(jq -r '.type' "$TASK_FILE") # mode other than "bypass" means the task may stop early when Claude Code asks # for permission. In "auto", classifier-blocked actions are denied instead of # prompted, but if auto mode is unavailable for the session's model/backend, -# Claude Code falls back to prompting and the same stall applies. Unset or unrecognized values pass no flag (Claude's default). +# Claude Code falls back to prompting and the same stall applies. +# Unset or unrecognized values pass no flag (Claude's default). PERMISSION_ARGS=() case "${TRIPLE_C_PERMISSION_MODE:-}" in plan) PERMISSION_ARGS=(--permission-mode plan) ;; -- 2.52.0