Skip to content

Bash routing nudge is hardcoded once-per-session; external-MCP nudge next to it is periodic + configurable #1026

Description

@macblackstuff

Summary

routing.mjs:840 uses guidanceOnce("bash", …), while routing.mjs:1012 uses guidancePeriodic("external-mcp", …, getExternalMcpNudgeEvery()). The periodic variant already exists, already has an env knob (CONTEXT_MODE_EXTERNAL_MCP_NUDGE_EVERY, default 10, bounds [1,100]), and its docstring gives a rationale that applies just as well to Bash:

A single one-shot nudge gets lost after the model's context compaction kicks in, and subsequent large MCP payloads flood context unchecked.

Asking for the same opt-in knob for the Bash nudge — e.g. CONTEXT_MODE_BASH_NUDGE_EVERY, defaulting to 0/unset = current one-shot behaviour, so nothing changes for anyone who does not set it.

I am aware #113 deliberately introduced the once-per-session throttle to stop advisory spam, and I am not asking to revert it. Default-off knob only.

Why it matters, with numbers

On this machine, across 2,319 session transcripts since 2026-05-15:

Tool Calls
Bash 41,921
ctx_execute + ctx_execute_file 4
ctx_batch_execute 2
ctx_search 1
ctx_fetch_and_index 1

The passive capture layer is doing excellent work over the same period — ctx_stats reports 166 MB kept out of context, 37,321 captures. That number is why this is a report rather than a complaint: the plugin's automatic half is clearly earning its place. It is the active half, where the agent is supposed to choose the sandbox over a raw dump, that is not being reached.

The one-shot nudge does fire — I received it this session. It then has to survive every subsequent turn and at least one compaction while competing with everything else in context. Empirically it does not.

Sharper framing, if useful for prioritisation

The habit that defeats it is the multi-line Bash block:

echo "=== A ==="; <cmd A>
echo "=== B ==="; <cmd B>
echo "=== C ==="; <cmd C>

That block exists because the agent wants several outputs in one round trip — which is exactly what ctx_batch_execute provides, with the raw bytes staying in the sandbox. The moment to say so is when that command is submitted, not once at the start of the session. A nudge that re-fires every N unbounded Bash calls would land on that shape repeatedly rather than once.

(Unrelated aside, since it affects anyone measuring the same thing: this shape also defeats rtk's Claude Code hook, which only inspects the first line of a multi-line command — rtk-ai/rtk#3342. Same habit, two tools.)

Verified before filing

  • ctx_doctor → all [OK] on v1.0.169; FTS5 native module PASS, all six hook scripts PASS.
  • WebFetch and curl redirects both fire correctly right now — I checked, because a first pass of my own analysis wrongly suspected they were failing open. isMCPReady() returns true with live sentinels; the deny path is healthy. Not an issue, recorded here only so the numbers above are not read as "the hooks are broken".

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions