Skip to content

allow_shell does not remove the shell from the Constitution #2637

@buko

Description

@buko

Setting allow_shell = false still has issues:

allow_shell = false does not remove shell tools from the Constitution (system prompt)

Repo: Hmbown/CodeWhale
Area: prompt assembly / tool gating
Priority: medium
Reported: 2026-06-03

Summary

Setting allow_shell = false in config removes shell tools (exec_shell, exec_shell_wait, exec_shell_interact, task_shell_start, task_shell_wait) from the model-visible tool catalog, but the Constitution (system prompt, compiled from crates/tui/src/prompts/base.md / base.txt) unconditionally describes shell tools in the Toolbox section.

The model reads the Constitution, sees shell tools listed, attempts to call one, and gets "Tool 'exec_shell' is not available in the current tool catalog" — even with the improved error message from #2412.

Root Cause

The prompt assembly in crates/tui/src/prompts.rs uses include_str! to embed static .md/.txt files compiled into the binary:

tool taxonomy -> base.md -> personality overlay -> mode delta -> approval policy

base.md and base.txt contain an unconditional Toolbox section:

- **Shell**: `task_shell_start` + `task_shell_wait` for long-running commands,
  diagnostics, tests, searches, and servers; `exec_shell` for bounded
  cancellable foreground commands; `exec_shell_wait`, `exec_shell_interact`.

There is no conditional rendering in prompts.rs to strip or annotate this section based on session.allow_shell or the current mode.

The Gap

Tool availability is gated at two layers:

Layer Conditionally respects allow_shell?
Tool catalog (tool registration) ✅ Yes — tools are removed from catalog
Tool error messaging (PR #2412) ✅ Yes — "not available" message explains why
Constitution / system prompt (Toolbox) No — always describes shell tools

The third layer is the problem. The model is led to believe shell tools exist, tries to use them, fails, and relies on the error message to recover. This wastes a turn, burns tokens, and creates a poor user experience.

Steps to Reproduce

  1. Set allow_shell = false in config (either global [workspace.'/path'] or per-project overlay)

  2. Start a session in Agent mode

  3. Examine the system prompt — the Toolbox section lists exec_shell and related tools

  4. Ask the model to run a shell command — it attempts exec_shell and gets:

    Tool 'exec_shell' is not available in the current tool catalog. Shell tools are gated by allow_shell; enable allow_shell = true for trusted workspaces, or switch to an auto-approve mode that permits shell access. You can also use tool_search_tool_bm25 to discover tools.

Suggested Fix

Two approaches:

Approach A — Dynamic prompt assembly (preferred)

Replace the static include_str!("base.txt") with a function that renders the Toolbox section conditionally. prompts.rs already assembles the prompt from multiple layers — the Toolbox section should be generated in Rust code, not embedded as raw text:

fn render_toolbox_block(allow_shell: bool) -> String {
    let mut tools = vec![
        "- **Planning / tracking**: `checklist_write` ...",
        "- **File I/O**: `read_file`, `list_dir`, ...",
        "- **Structured search**: `grep_files`, `file_search`, ...",
    ];

    if allow_shell {
        tools.insert(2, "- **Shell**: `task_shell_start` + `task_shell_wait` ...");
    }

    tools.join("\n")
}

Approach B — Post-processing strip

Strip or comment out the shell line from base.txt after the full prompt is assembled, if allow_shell is false. Simpler but fragile — would break if the Toolbox section wording is edited.

Evidence

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdocumentationImprovements or additions to documentation

    Projects

    Status
    In progress

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions