Skip to content

Context buffer too small — sentinel misidentifies tool type #43

@martymcenroe

Description

@martymcenroe

Summary

The context buffer used for sentinel tool-type extraction is only ~2KB (the tail of self.context_buffer). The extract_permission_context_structured() function searches this buffer for the last TOOL_CALL_RE match to determine which tool (Bash, Write, Edit, etc.) triggered the permission prompt.

TOOL_CALL_RE = re.compile(
    r'(?:Bash|Read|Write|Edit|Glob|Grep|WebFetch|WebSearch|Skill|Task|NotebookEdit)'
    r'\(([^)]{0,500})\)',
    re.DOTALL
)

The problem: Claude Code's Ink-based UI renders tool invocations with ANSI escape sequences interspersed throughout the text. A single Bash(git status) call in the raw PTY stream might look like:

\x1b[36mBash\x1b[0m\x1b[2m(\x1b[0mgit status\x1b[2m)\x1b[0m

The regex runs after ANSI stripping on the context buffer, but the buffer accumulates raw bytes. If the ANSI-stripped version of the tool call doesn't land within the last 2KB of context, sentinel gets either:

  • ("unknown", "", ...) — which means the command bypasses sentinel entirely (not in any scope)
  • The wrong tool type from an earlier invocation still in the buffer

Impact

  • False negatives: A Bash(rm -rf important/) command gets classified as unknown → bypasses sentinel scope check → auto-approved without safety evaluation
  • Misclassification: A Write operation gets classified as Bash (from a previous tool call in the buffer) → evaluated with wrong system prompt context

Reproduction

  1. Have Claude perform a sequence of Read operations (which produce large output — file contents rendered to terminal)
  2. Follow with a Bash command that triggers a permission prompt
  3. The Read outputs push the Bash tool call out of the 2KB context window
  4. Sentinel sees unknown tool type → skips evaluation

Proposed Fix

Two complementary changes:

  1. Increase context buffer to 8KB or make it configurable
  2. Extract tool type at detection time: When the permission prompt pattern is matched, walk backward through the raw buffer looking for the most recent tool call line, rather than searching a fixed-size tail

A more robust approach would be to track tool invocations as they stream through _reader_pty() — maintaining a self.last_tool_type / self.last_tool_args that updates whenever a tool call pattern is seen, regardless of buffer position.

Affected Files

  • src/unleashed-c-21.pyextract_permission_context_structured() and self.context_buffer management
  • src/sentinel_gate.py — receives the potentially-wrong tool type

Context

The context buffer was designed for the session mirror and friction logging — it was never meant to be a reliable tool-type detector. Sentinel's correctness depends on accurate tool identification because the scope routing (SENTINEL_SCOPES) uses the tool type to decide whether to evaluate at all. A Bash command misidentified as unknown gets the same treatment as a Read — instant auto-approval, no safety check. This is the quiet failure mode: sentinel is enabled, appears to be working, but silently misses commands.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingon-iceFeature/fix on hold — not in active development

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions