Skip to content

fix: make pane input descriptions harm-first - #356

Merged
EtanHey merged 1 commit into
mainfrom
fix/tool-description-safety
Aug 4, 2026
Merged

fix: make pane input descriptions harm-first#356
EtanHey merged 1 commit into
mainfrom
fix/tool-description-safety

Conversation

@EtanHey

@EtanHey EtanHey commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Summary

  • Lead every pane-writing MCP tool and payload-field description with Etan's exact harm-first warning, while retaining the numeric runtime caps.
  • Route all three spawn prompt paths through the same pre-mutation over-cap, dense-line, and multi-paragraph validator; this closes the legacy new_worktree_split / spawn_in_workspace bypass found during review.
  • Keep list_surfaces condensed output unchanged and make the cost and rarity of verbose:true explicit.

Design decisions

submit_verified:null

Do not blanket-fail null. Normal interactive send_to already attempts submit verification and converts attempted-but-unconfirmed delivery to submit_verified:false, which returns non-ok. A successful null means verification was intentionally not attempted, including press_enter:false and allow_busy:true. PR #326, now merged, complements this by hardening spawn/boot settlement; it does not require a second generic relay mechanism. The recommendation was posted in the #326 thread.

list_surfaces

Live measurement on 11 surfaces:

  • condensed structured response: 3,779 bytes
  • verbose structured response: 9,262 bytes
  • verbose / condensed: 2.45x

The condensed pane, column, and real-CWD fields are required for deterministic placement and routing, so this is case (a): callers should avoid verbose:true unless a specific raw field is needed.

Review disposition

  • CodeRabbit MAJOR — fixed: legacy spawn prompt paths bypassed the existing guards. Added one shared validator and six RED/GREEN pre-mutation cases.
  • CodeRabbit MINOR — waived: qualifying the harm-first warning would conflict with Etan's exact required leading wording.
  • A second CodeRabbit pass was attempted after the fix, but the free OSS service rate-limited before analysis with a 22-minute wait.

Verification

  • npx vitest run tests/pointer-discipline.test.ts tests/server.test.ts --reporter=dot — 229 passed
  • npm run typecheck — passed
  • npm run build — passed
  • npm run pre-pr — 63 passed
  • isolated npm test -- --reporter=dot — 106 files, 2,393 passed
  • push hook — 106 files, 2,393 passed; run_tests.sh exit 0
  • built-binary stdio MCP client — 10 affected descriptions served with the exact prefix; live list_surfaces returned 3,779 / 9,262 bytes (2.45x)

Scope

No release was run. This PR is ready for the lead-routed Codex review and is intentionally not merged by the worker.


Note

Medium Risk
Touches central MCP input validation and spawn entry points; mis-wiring could block legitimate spawns or leave unsafe prompts accepted, though behavior is heavily regression-tested and mostly aligns existing guards with previously missing paths.

Overview
MCP tool descriptions now lead with a shared harm-first rule (max 2–3 short lines; longer inline payloads break the receiving pane; use file-backed Read and follow <path>) instead of byte-threshold / dense-routing prose. That prefix is applied to all pane-writing tools (send_input, send_command, send_to, broadcast, spawn/split/surface tools, and send_to_agent) and to inline text/command/prompt and boot_prompt_path field descriptions.

Runtime behavior adds assertSpawnPromptInputAllowed so spawn_agent, new_worktree_split, and spawn_in_workspace all run the same over-cap, dense-line, and multi-paragraph checks before workspace/pane mutation. Legacy new_worktree_split / spawn_in_workspace paths previously skipped these guards; they also omit allow_long_inline override messaging. list_surfaces tool and verbose arg descriptions now state condensed-by-default output and that verbose raw fields cost materially more tokens and are rarely needed (response shape unchanged).

Tests pin the description contract and six pre-mutation rejection cases; design/implementation plan docs are added.

Reviewed by Cursor Bugbot for commit 0e26c55. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Make pane-writing tool descriptions and field descriptions lead with harm-first input guidance

  • Adds a PANE_BREAKAGE_GUIDANCE constant to server.ts and prepends it to the description and relevant field descriptions of all pane-writing tools (send_input, send_command, spawn_agent, new_split, new_surface, broadcast, send_to, send_to_agent, new_worktree_split, spawn_in_workspace).
  • Adds assertSpawnPromptInputAllowed to centralize prompt validation logic, replacing three separate inline/dense/multiline guard calls in spawn_agent and introducing pre-mutation guards in new_worktree_split and spawn_in_workspace.
  • new_worktree_split and spawn_in_workspace now reject over-cap, dense, or multi-paragraph inline prompts before any workspace or pane mutation; neither tool supports the allow_long_inline override.
  • Updates list_surfaces description to document that results are condensed by default and that verbose increases token usage and is rarely needed.
  • Adds tests in pointer-discipline.test.ts verifying that all pane-writing tool and field descriptions start with the harm-first warning, and that prompt guards fire before cmux mutations.
📊 Macroscope summarized 0e26c55. 3 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted

🗂️ Filtered Issues

No issues evaluated.

Summary by CodeRabbit

  • Documentation

    • Added safety guidance warning that long inline content can disrupt receiving panes and should be written to files instead.
    • Documented condensed list_surfaces output by default, with optional verbose details.
    • Added implementation guidance for consistent validation across pane and workspace creation tools.
  • Tests

    • Expanded coverage to verify oversized and dense inline prompts are rejected before changes occur.
    • Added checks for safety warnings across pane-writing tools and prompt fields.

@cursor

cursor Bot commented Aug 4, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_d24f2bf1-d5c3-4879-8d54-40758210a60e)

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@EtanHey, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 47 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0218a67a-9570-4708-8b97-13e310ff7a1c

📥 Commits

Reviewing files that changed from the base of the PR and between 50be412 and 0e26c55.

📒 Files selected for processing (5)
  • docs/plans/2026-08-04-tool-description-safety-design.md
  • docs/plans/2026-08-04-tool-description-safety.md
  • src/server.ts
  • tests/pointer-discipline.test.ts
  • tests/server.test.ts
📝 Walkthrough

Walkthrough

This PR adds two documentation files describing a design and implementation plan for tool description safety, focused on harm-first pane-writing warnings and a unified spawn prompt validator. It also updates two test files to validate this wording and spawn-path guard behavior.

Changes

Tool Description Safety

Layer / File(s) Summary
Design document
docs/plans/2026-08-04-tool-description-safety-design.md
Documents harm-first wording for ten pane-writing tools, a unified pre-mutation spawn validator, preserved existing guards, list_surfaces verbose-mode notes, submit_verified:null handling, and verification steps.
Implementation plan document
docs/plans/2026-08-04-tool-description-safety.md
Documents the plan to centralize harm-first warnings, add description-level tests, introduce a shared spawn validator, update list_surfaces guidance, and lists verification and PR steps.
Test updates for pane warnings and spawn validation
tests/pointer-discipline.test.ts, tests/server.test.ts
Adds parameterized tests confirming new_worktree_split and spawn_in_workspace reject unsafe inline prompts without mutation, replaces prior wording checks with standardized pane-breakage warning checks across tools and fields, and adds list_surfaces documentation coverage.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

A rabbit reads each pane-bound line,
"Keep it short, or use a file," the docs now shine.
Spawn paths guarded, prompts held tight,
Tests confirm the warnings write just right.
Thump thump — safety docs done, hop into the night! 🐇📄

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: make pane input descriptions harm-first' clearly and specifically summarizes the main change: updating tool descriptions to prioritize harm warnings before other content.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/tool-description-safety

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread src/server.ts
return;
}

const overrideGuidance =

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium src/server.ts:1306

When assertInteractiveMultilineInputAllowed rejects a multi-paragraph prompt from new_worktree_split or spawn_in_workspace, the error message tells the caller to pass boot_prompt_path, but neither tool exposes that argument, so the remediation is unusable. The message is built unconditionally for every tool, but the boot_prompt_path guidance only applies to the original launchers. Consider branching the guidance so these legacy tools are directed to use a short Read and follow <path> prompt instead, or document why they are intentionally given a non-actionable path.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @src/server.ts around line 1306:

When `assertInteractiveMultilineInputAllowed` rejects a multi-paragraph `prompt` from `new_worktree_split` or `spawn_in_workspace`, the error message tells the caller to pass `boot_prompt_path`, but neither tool exposes that argument, so the remediation is unusable. The message is built unconditionally for every tool, but the `boot_prompt_path` guidance only applies to the original launchers. Consider branching the guidance so these legacy tools are directed to use a short `Read and follow <path>` prompt instead, or document why they are intentionally given a non-actionable path.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 50be412497

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +34 to +37
- Lead every pane-writing tool description with that guidance: `send_to`,
`send_input`, `send_command`, `broadcast`, `spawn_agent`, `new_split`,
`new_surface`, `new_worktree_split`, `spawn_in_workspace`, and the deprecated
`send_to_agent` alias.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include the public interact send path in the safety policy

The new “every pane-writing tool” list omits the public interact tool. In src/server.ts, interact(action="send") forwards arbitrary text directly to deliverAgentInput without the inline, dense-line, or multiline guards, and tests/enter-reliability.test.ts still asserts that a 2,000-character message succeeds. A caller using this API therefore receives none of the warning and can bypass the cap, reproducing the pane-wedging case this change is intended to prevent; include the interact description/field and guard its send action.

Useful? React with 👍 / 👎.

Comment thread src/server.ts
Comment on lines +1347 to 1348
opts.arg === "prompt" || opts.tool === "send_command"
? " For launcher boot prompts, put the full prompt in a file and pass boot_prompt_path."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid recommending an unsupported boot_prompt_path

When an over-cap prompt is passed to new_worktree_split or spawn_in_workspace, this shared branch tells the caller to pass boot_prompt_path, but neither tool exposes that argument in its schema. Following the new error therefore cannot work—the unknown field is not a supported delivery path—so make this guidance conditional on tools that actually accept boot_prompt_path and direct these deprecated tools only to submit a short Read and follow <path> prompt.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/plans/2026-08-04-tool-description-safety.md`:
- Line 13: Update the task headings in the document, including “Task 1” and the
corresponding task headings at the other referenced locations, from H3 (###) to
H2 (##). Keep the step-level headings as H3 (###).

In `@src/server.ts`:
- Around line 1306-1311: Update the shared over-cap and multi-paragraph error
guidance to be tool-specific: for new_worktree_split and spawn_in_workspace,
recommend a short “Read and follow <path>” prompt or redirect callers to
spawn_agent instead of advertising boot_prompt_path; retain boot_prompt_path
guidance only for tools that define it. Apply this consistently across the
affected error-message branches, including the logic around overrideGuidance.
- Around line 341-342: Update the text constant PANE_INPUT_BREAKAGE_GUIDANCE so
its first sentence warns that longer payloads break the receiving pane, then
state the 2–3 short-line limit and retain the existing file-writing instruction.

In `@tests/server.test.ts`:
- Line 1012: Strengthen the assertion for verboseDescription in the relevant
server test so it requires wording that explicitly conveys increased token
usage, such as “costs more tokens” or “increases token usage,” rather than
merely matching the word “token.”
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4394dcdc-aade-4f92-b2c2-7dc950c7a0ed

📥 Commits

Reviewing files that changed from the base of the PR and between 93aefa8 and 50be412.

📒 Files selected for processing (5)
  • docs/plans/2026-08-04-tool-description-safety-design.md
  • docs/plans/2026-08-04-tool-description-safety.md
  • src/server.ts
  • tests/pointer-discipline.test.ts
  • tests/server.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.ts: Build the project with TypeScript (tsc) and keep source code compatible with Node 20+ and Zod-based typing.
Use the ok(data) and err(error) helpers for consistent MCP tool responses.
All MCP tool handlers must return { content: TextContent[], structuredContent?, isError? }.

Files:

  • src/server.ts
src/server.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Register all MCP tools in server.ts, including the 33 tool handlers, and conditionally skip agent-lifecycle tools when skipAgentLifecycle: true.

Files:

  • src/server.ts
tests/**/*.test.ts

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.test.ts: Mirror source layout in tests (src/foo.ts -> tests/foo.test.ts).
Do not add integration tests that require a running cmux instance; tests should be fully mocked.

Files:

  • tests/server.test.ts
  • tests/pointer-discipline.test.ts
tests/server.test.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Server tests should mock the cmux client via createServer({ exec, skipAgentLifecycle }).

Files:

  • tests/server.test.ts
🧠 Learnings (1)
📚 Learning: 2026-03-15T10:42:35.917Z
Learnt from: EtanHey
Repo: EtanHey/cmuxlayer PR: 1
File: tests/quality-tracking.test.ts:171-200
Timestamp: 2026-03-15T10:42:35.917Z
Learning: In tests/quality-tracking.test.ts for the cmuxlayer project, ensure that at or above 80% context quality degradation, behavior depends on depth: depth-0 agents receive a /compact command; depth > 0 agents are killed and logged (kill + log). Respawn of non-root agents is out of scope for v1. Treat the design doc quality tracking section as the authoritative source for this behavior, and align test expectations accordingly.

Applied to files:

  • tests/server.test.ts
  • tests/pointer-discipline.test.ts
🪛 markdownlint-cli2 (0.23.1)
docs/plans/2026-08-04-tool-description-safety.md

[warning] 13-13: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)

🔇 Additional comments (7)
docs/plans/2026-08-04-tool-description-safety-design.md (1)

1-70: LGTM!

tests/pointer-discipline.test.ts (1)

786-906: LGTM!

Also applies to: 1200-1271

src/server.ts (5)

1359-1397: LGTM!

Also applies to: 1400-1429


5517-5528: LGTM!

Also applies to: 6025-6058, 6370-6388


6550-6627: LGTM!

Also applies to: 6740-6799


8655-8782: LGTM!


10249-10282: LGTM!

Also applies to: 11024-11133, 11173-11228


---

### Task 1: Pin pane-input description wording

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use H2 headings for the tasks.

Lines 13, 52, and 81 use ### directly after the document H1. Change each task heading to ##. Keep the step headings at ###.

Based on static analysis, this violates MD001.

Also applies to: 52-52, 81-81

🧰 Tools
🪛 markdownlint-cli2 (0.23.1)

[warning] 13-13: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/plans/2026-08-04-tool-description-safety.md` at line 13, Update the task
headings in the document, including “Task 1” and the corresponding task headings
at the other referenced locations, from H3 (###) to H2 (##). Keep the step-level
headings as H3 (###).

Source: Linters/SAST tools

Comment thread src/server.ts
Comment on lines +341 to +342
const PANE_INPUT_BREAKAGE_GUIDANCE =
"Max 2-3 short lines. Longer payloads BREAK the receiving pane — write the payload to a file and send one line: `Read and follow <path>`.";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Put the harm-first warning first.

PANE_INPUT_BREAKAGE_GUIDANCE starts with Max 2-3 short lines.. The PR requirement is for pane-writing descriptions to begin with the pane-breakage warning. Move Longer payloads BREAK the receiving pane to the first sentence, then keep the numeric limit.

Suggested wording
-  "Max 2-3 short lines. Longer payloads BREAK the receiving pane — write the payload to a file and send one line: `Read and follow <path>`.";
+  "Longer payloads BREAK the receiving pane. Keep payloads to 2-3 short lines; write longer payloads to a file and send one line: `Read and follow <path>`.";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const PANE_INPUT_BREAKAGE_GUIDANCE =
"Max 2-3 short lines. Longer payloads BREAK the receiving panewrite the payload to a file and send one line: `Read and follow <path>`.";
const PANE_INPUT_BREAKAGE_GUIDANCE =
"Longer payloads BREAK the receiving pane. Keep payloads to 2-3 short lines; write longer payloads to a file and send one line: `Read and follow <path>`.";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server.ts` around lines 341 - 342, Update the text constant
PANE_INPUT_BREAKAGE_GUIDANCE so its first sentence warns that longer payloads
break the receiving pane, then state the 2–3 short-line limit and retain the
existing file-writing instruction.

Comment thread src/server.ts
Comment thread tests/server.test.ts
expect(tool.description).toMatch(/condensed by default/i);
const verboseDescription = tool.inputSchema.shape.verbose.description;
expect(verboseDescription).toMatch(/raw cmux fields/i);
expect(verboseDescription).toMatch(/token/i);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Assert the token-cost relationship.

Line 1012 only requires the word token. A description can mention tokens without stating that verbose increases token usage and still pass. Assert costs more tokens or increases token usage.

Based on the implementation plan, verbose must state that it costs more tokens.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/server.test.ts` at line 1012, Strengthen the assertion for
verboseDescription in the relevant server test so it requires wording that
explicitly conveys increased token usage, such as “costs more tokens” or
“increases token usage,” rather than merely matching the word “token.”

@EtanHey
EtanHey force-pushed the fix/tool-description-safety branch from 50be412 to 0e26c55 Compare August 4, 2026 13:55
@EtanHey
EtanHey merged commit faefac0 into main Aug 4, 2026
1 check passed
@cursor

cursor Bot commented Aug 4, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_10fe109b-6ddd-4569-a61e-f323067de923)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant