From aeb506c97b11a2f54ab174b31e4e5627e3b5b674 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 28 Sep 2026 03:18:27 -0700 Subject: [PATCH 1/2] fix(runner): make Grok isolated-write lanes usable - allowlist the Grok shell tool by its real name: grok 1.0.41 calls it run_terminal_command, so run_terminal_cmd was ignored and every shell call fell through to a headless approver that cancelled the lane ("User cancelled the execution for tool run_terminal_command") - run isolated-write Grok lanes under bypassPermissions: acceptEdits still routes multi-line commands (heredocs) to that approver; the workspace sandbox stays on and is what confines the writer, read-only keeps plan Verified on grok 1.0.41: a runner lane executed a shell command and a python heredoc and completed; 50 runner tests pass. Co-Authored-By: Claude Fable 5.1 --- .../poteto-mode/references/provider-dispatch.md | 2 +- .../poteto-mode/scripts/runner/commands.test.ts | 6 +++--- .../skills/poteto-mode/scripts/runner/commands.ts | 11 +++++++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/plugins/pstack/skills/poteto-mode/references/provider-dispatch.md b/plugins/pstack/skills/poteto-mode/references/provider-dispatch.md index 74ab90da..714f7272 100644 --- a/plugins/pstack/skills/poteto-mode/references/provider-dispatch.md +++ b/plugins/pstack/skills/poteto-mode/references/provider-dispatch.md @@ -80,7 +80,7 @@ Start the background process, continue launching the other lanes, then drain the The runner and its preflight have no implicit timeout. Do not invent a duration from role, mode, or a convenient round number; real implementation lanes can run for 90 minutes or much longer. Pass `--timeout` only when the user, an external service deadline, or a measured task contract supplies a real bound. That value starts at wrapper entry, before module loading and argument parsing, and remains one absolute deadline across setup, preflight, model execution, and output capture. It is never a fresh allowance per child, and long waits are armed in runtime-safe chunks without shortening the supplied deadline. Otherwise supervise liveness through the retained background task/session handle and cancel manually only on evidence that the run is dead. Cancel through that retained handle so the runner receives SIGINT or SIGTERM, sends it to an active child when one remains, stops waiting on inherited output pipes, removes the empty output reservation, and writes a `cancelled` receipt. Preserve that receipt; a retry is a new attempt with new unique output and receipt paths. Unchanged running state is not a dropout, and Claude's ten-minute foreground ceiling is never a reason to terminate a healthy lane. -Read-only mode maps to Claude plan mode with project-only settings and an explicit tool list, Codex's read-only sandbox, and Grok plan mode plus its `read-only` sandbox and read-oriented tool list. Grok's built-in read-only profile deliberately keeps its own state and system temporary directories writable, so point a read-only Grok lane at the actual checkout rather than a worktree under `/tmp`, `/var/tmp`, or the host's temporary directory. `isolated-write` maps to Claude `acceptEdits` with project-only settings, Codex `workspace-write`, and Grok `acceptEdits` plus its `workspace` sandbox and write-capable tool list. Give every writer only a dedicated worktree or output directory. Never route a writer into the primary checkout. +Read-only mode maps to Claude plan mode with project-only settings and an explicit tool list, Codex's read-only sandbox, and Grok plan mode plus its `read-only` sandbox and read-oriented tool list. Grok's built-in read-only profile deliberately keeps its own state and system temporary directories writable, so point a read-only Grok lane at the actual checkout rather than a worktree under `/tmp`, `/var/tmp`, or the host's temporary directory. `isolated-write` maps to Claude `acceptEdits` with project-only settings, Codex `workspace-write`, and Grok `bypassPermissions` plus its `workspace` sandbox and write-capable tool list. Grok's `acceptEdits` still sends multi-line shell commands to an approver that a headless run cannot answer, so the sandbox, not the approver, is what confines a Grok writer. Give every writer only a dedicated worktree or output directory. Never route a writer into the primary checkout. Every concurrent external lane needs distinct prompt, output, and receipt paths. The launcher reserves output and receipt paths exclusively and refuses to overwrite them. diff --git a/plugins/pstack/skills/poteto-mode/scripts/runner/commands.test.ts b/plugins/pstack/skills/poteto-mode/scripts/runner/commands.test.ts index ea697ff2..0d57f3f0 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/runner/commands.test.ts +++ b/plugins/pstack/skills/poteto-mode/scripts/runner/commands.test.ts @@ -100,7 +100,7 @@ describe("invocationCommand", () => { "--sandbox", "read-only", "--tools", - "read_file,grep,list_dir,run_terminal_cmd", + "read_file,grep,list_dir,run_terminal_command", "--disallowed-tools", "Agent,search_tool,use_tool", "--output-format", @@ -124,11 +124,11 @@ describe("invocationCommand", () => { expect(grok.args).toEqual( expect.arrayContaining([ "--permission-mode", - "acceptEdits", + "bypassPermissions", "--sandbox", "workspace", "--tools", - "read_file,grep,list_dir,run_terminal_cmd,search_replace", + "read_file,grep,list_dir,run_terminal_command,search_replace", ]) ); expect(grok.args).not.toContain("--always-approve"); diff --git a/plugins/pstack/skills/poteto-mode/scripts/runner/commands.ts b/plugins/pstack/skills/poteto-mode/scripts/runner/commands.ts index 5f2b10c6..8192424e 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/runner/commands.ts +++ b/plugins/pstack/skills/poteto-mode/scripts/runner/commands.ts @@ -51,7 +51,7 @@ function grokSandbox(mode: AccessMode): string { } function grokTools(mode: AccessMode): string { - const readonly = ["read_file", "grep", "list_dir", "run_terminal_cmd"]; + const readonly = ["read_file", "grep", "list_dir", "run_terminal_command"]; return [...readonly, ...(mode === "isolated-write" ? ["search_replace"] : [])].join(","); } @@ -59,6 +59,13 @@ function permissionMode(mode: AccessMode): string { return mode === "read-only" ? "plan" : "acceptEdits"; } +// Grok's acceptEdits still routes multi-line shell commands (heredocs) to an +// approver that a headless run cannot answer, which cancels the lane. The +// workspace sandbox stays on, so bypassPermissions only removes the prompt. +function grokPermissionMode(mode: AccessMode): string { + return mode === "read-only" ? "plan" : "bypassPermissions"; +} + function effortOverride(effort: Effort): string { return `model_reasoning_effort=${JSON.stringify(effort)}`; } @@ -129,7 +136,7 @@ export function invocationCommand(options: RunnerOptions): CommandSpec { "--reasoning-effort", options.effort, "--permission-mode", - permissionMode(options.mode), + grokPermissionMode(options.mode), "--sandbox", grokSandbox(options.mode), "--tools", From 0a0a03b8f1538376210cea146559a4800401fa0f Mon Sep 17 00:00:00 2001 From: f Date: Mon, 28 Sep 2026 15:20:15 -0700 Subject: [PATCH 2/2] review: pin the Grok writer argv and state what bypassPermissions changes Codex GPT-6 Astra review round. The writer test asserts the complete Grok argv instead of arrayContaining, so a flag cannot drift from its value. The runner comment and provider-dispatch.md say that bypassPermissions is Grok's always-approve policy (deny rules and hooks still apply) and that the workspace profile is what confines the writer: reads everywhere, writes only to the lane's cwd, Grok state, and the system temp dirs, child network open. Verified against the Grok sandbox and permissions docs; bun tests, typecheck, manifests, and static invariants pass. Co-Authored-By: Claude Fable 5.1 --- .../references/provider-dispatch.md | 2 +- .../scripts/runner/commands.test.ts | 36 ++++++++++++------- .../poteto-mode/scripts/runner/commands.ts | 10 ++++-- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/plugins/pstack/skills/poteto-mode/references/provider-dispatch.md b/plugins/pstack/skills/poteto-mode/references/provider-dispatch.md index 714f7272..7374f283 100644 --- a/plugins/pstack/skills/poteto-mode/references/provider-dispatch.md +++ b/plugins/pstack/skills/poteto-mode/references/provider-dispatch.md @@ -80,7 +80,7 @@ Start the background process, continue launching the other lanes, then drain the The runner and its preflight have no implicit timeout. Do not invent a duration from role, mode, or a convenient round number; real implementation lanes can run for 90 minutes or much longer. Pass `--timeout` only when the user, an external service deadline, or a measured task contract supplies a real bound. That value starts at wrapper entry, before module loading and argument parsing, and remains one absolute deadline across setup, preflight, model execution, and output capture. It is never a fresh allowance per child, and long waits are armed in runtime-safe chunks without shortening the supplied deadline. Otherwise supervise liveness through the retained background task/session handle and cancel manually only on evidence that the run is dead. Cancel through that retained handle so the runner receives SIGINT or SIGTERM, sends it to an active child when one remains, stops waiting on inherited output pipes, removes the empty output reservation, and writes a `cancelled` receipt. Preserve that receipt; a retry is a new attempt with new unique output and receipt paths. Unchanged running state is not a dropout, and Claude's ten-minute foreground ceiling is never a reason to terminate a healthy lane. -Read-only mode maps to Claude plan mode with project-only settings and an explicit tool list, Codex's read-only sandbox, and Grok plan mode plus its `read-only` sandbox and read-oriented tool list. Grok's built-in read-only profile deliberately keeps its own state and system temporary directories writable, so point a read-only Grok lane at the actual checkout rather than a worktree under `/tmp`, `/var/tmp`, or the host's temporary directory. `isolated-write` maps to Claude `acceptEdits` with project-only settings, Codex `workspace-write`, and Grok `bypassPermissions` plus its `workspace` sandbox and write-capable tool list. Grok's `acceptEdits` still sends multi-line shell commands to an approver that a headless run cannot answer, so the sandbox, not the approver, is what confines a Grok writer. Give every writer only a dedicated worktree or output directory. Never route a writer into the primary checkout. +Read-only mode maps to Claude plan mode with project-only settings and an explicit tool list, Codex's read-only sandbox, and Grok plan mode plus its `read-only` sandbox and read-oriented tool list. Grok's built-in read-only profile deliberately keeps its own state and system temporary directories writable, so point a read-only Grok lane at the actual checkout rather than a worktree under `/tmp`, `/var/tmp`, or the host's temporary directory. `isolated-write` maps to Claude `acceptEdits` with project-only settings, Codex `workspace-write`, and Grok `bypassPermissions` plus its `workspace` sandbox and write-capable tool list. Grok's `acceptEdits` (tested on grok 1.0.41) still sends multi-line shell commands to an approver that a headless run cannot answer, so the sandbox, not the approver, is what confines a Grok writer: `bypassPermissions` is Grok's always-approve policy and skips ordinary tool approvals (deny rules and hooks still apply), while the `workspace` profile allows reads everywhere, writes only to the lane's cwd, Grok's own state, and the system temporary directories, and does not block child network access (on macOS no profile does). Give every writer only a dedicated worktree or output directory. Never route a writer into the primary checkout. Every concurrent external lane needs distinct prompt, output, and receipt paths. The launcher reserves output and receipt paths exclusively and refuses to overwrite them. diff --git a/plugins/pstack/skills/poteto-mode/scripts/runner/commands.test.ts b/plugins/pstack/skills/poteto-mode/scripts/runner/commands.test.ts index 0d57f3f0..67f5de0d 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/runner/commands.test.ts +++ b/plugins/pstack/skills/poteto-mode/scripts/runner/commands.test.ts @@ -113,7 +113,7 @@ describe("invocationCommand", () => { ]); }); - it("uses bounded write modes without blanket bypasses", () => { + it("confines writers with sandboxes rather than approvals", () => { const codex = invocationCommand(options({ mode: "isolated-write" })); expect(codex.args).toEqual( expect.arrayContaining(["--sandbox", "workspace-write"]) @@ -121,17 +121,29 @@ describe("invocationCommand", () => { const grok = invocationCommand( options({ provider: "grok", model: "grok-4.6", mode: "isolated-write" }) ); - expect(grok.args).toEqual( - expect.arrayContaining([ - "--permission-mode", - "bypassPermissions", - "--sandbox", - "workspace", - "--tools", - "read_file,grep,list_dir,run_terminal_command,search_replace", - ]) - ); - expect(grok.args).not.toContain("--always-approve"); + expect(grok.args).toEqual([ + "--prompt-file", + "/tmp/prompt.md", + "--model", + "grok-4.6", + "--reasoning-effort", + "max", + "--permission-mode", + "bypassPermissions", + "--sandbox", + "workspace", + "--tools", + "read_file,grep,list_dir,run_terminal_command,search_replace", + "--disallowed-tools", + "Agent,search_tool,use_tool", + "--output-format", + "streaming-messages-json", + "--cwd", + "/tmp/worktree", + "--no-subagents", + "--disable-web-search", + "--verbatim", + ]); const claude = invocationCommand( options({ provider: "claude", model: "fable", mode: "isolated-write" }) diff --git a/plugins/pstack/skills/poteto-mode/scripts/runner/commands.ts b/plugins/pstack/skills/poteto-mode/scripts/runner/commands.ts index 8192424e..7fc9fd7e 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/runner/commands.ts +++ b/plugins/pstack/skills/poteto-mode/scripts/runner/commands.ts @@ -59,9 +59,13 @@ function permissionMode(mode: AccessMode): string { return mode === "read-only" ? "plan" : "acceptEdits"; } -// Grok's acceptEdits still routes multi-line shell commands (heredocs) to an -// approver that a headless run cannot answer, which cancels the lane. The -// workspace sandbox stays on, so bypassPermissions only removes the prompt. +// Grok's acceptEdits (grok 1.0.41) still routes multi-line shell commands +// (heredocs) to an approver that a headless run cannot answer, which cancels +// the lane. bypassPermissions is Grok's always-approve policy: ordinary tool +// approvals are skipped (deny rules and hooks still apply), so the workspace +// sandbox is what confines the writer: reads anywhere, writes only to the +// lane's cwd, Grok's own state, and the system temp dirs; child network +// stays open. function grokPermissionMode(mode: AccessMode): string { return mode === "read-only" ? "plan" : "bypassPermissions"; }