Skip to content

Claude Code plugin hooks (Stop) displace user content in Telegram final message #107

@nathanschram

Description

Problem

When Claude Code runs via Untether (Telegram), only the final assistant text message is visible to the user. Claude Code plugin hooks that fire at session end (Stop hooks) can cause Claude to waste its final response addressing hook concerns instead of outputting user-requested content.

Observed incident

In the BIP project chat, a user asked Claude to outline a document. Claude completed successfully (rc=0, 46.7s) but the user received only:

No files were modified in this interaction — I only read the backlinks doc and outlined it in the chat. The hook fired as a false positive. No context doc updates needed.

The actual document outline was generated in an intermediate assistant turn but was replaced by this hook-response message.

Root cause

Two compounding issues:

1. PitchDocs context-guard-stop.sh false positive

The hook checks git status --porcelain (ALL dirty files) rather than session-modified files. In BIP, freshly-installed PitchDocs infrastructure (.claude/rules/context-quality.md) was untracked and matched the structural pattern, triggering the hook even though no structural changes occurred in the current session.

2. Content displacement in Untether

When a Stop hook returns "decision": "block", Claude gets one more turn. In Untether's single-message model:

  • Intermediate assistant text appears as progress edits (replaced by each new turn)
  • The result.result from the final CompletedEvent becomes the persistent message
  • If the final turn addresses a hook concern, that meta-commentary replaces the actual content

Cross-project comparison

Same hooks installed in 4 projects — only BIP triggers due to git state:

Project Structural dirty? Context docs dirty? Blocks?
BIP YES (untracked hook infra) NO (CLAUDE.md committed) YES — false positive
Scout NO N/A No
Brand Copilot YES YES (CLAUDE.md also dirty) No
littlebearapps.com N/A (no hooks) N/A N/A

Proposed fixes

Untether side (this repo)

Preamble enhancement: Add hook-awareness guidance to the default preamble in runner_bridge.py:

- If hooks fire at session end, your final response MUST still contain the user's
  requested content. Hook concerns are secondary — briefly note them AFTER the main
  content, never instead of it.

PitchDocs side (separate repo)

  1. Untether detection: Check $UNTETHER_SESSION env var and skip blocking
  2. False positive fix: Exclude hook infrastructure files from structural check, or use git diff --name-only instead of git status --porcelain
  3. Auto-commit on install: Commit hook infrastructure files during /context-guard install

Audit document

Full analysis at docs/audits/pitchdocs-context-guard-interference.md

Metadata

Metadata

Assignees

No one assigned

    Labels

    engine:claudeClaude Code CLI (Anthropic)enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions