Skip to content

feat(plugin): show context bomb in chat transcript after compact#30

Merged
greynewell merged 1 commit intomainfrom
claude/issue-23-20260226-2121
Feb 27, 2026
Merged

feat(plugin): show context bomb in chat transcript after compact#30
greynewell merged 1 commit intomainfrom
claude/issue-23-20260226-2121

Conversation

@greynewell
Copy link
Contributor

@greynewell greynewell commented Feb 27, 2026

Summary

  • Problem: uncompact-hook.sh ran at SessionStart:compact and emitted context to stdout — but that only reaches the AI as silent context. Users never saw the context bomb in their chat window.
  • Fix: uncompact-hook.sh now captures its output and writes it to a per-user temp file. A new show-hook.sh runs on UserPromptSubmit, replays the cached output once (making it visible in the transcript), then deletes the file.
  • Also partially fixes issue bug: HTTP response bodies read without size cap (io.ReadAll unbounded) #23: caps io.ReadAll in GetGraph with a 10 MB io.LimitReader. The equivalent fix in GetCircularDependencies is left as a TODO.

How it works

/compact fires
  └─ SessionStart:compact → uncompact-hook.sh
       ├─ stdout → AI context (as before)
       └─ writes /tmp/uncompact-display-<uid>.txt

User sends first message after compact
  └─ UserPromptSubmit → show-hook.sh
       ├─ reads /tmp/uncompact-display-<uid>.txt
       ├─ stdout → visible in chat transcript  ✓
       └─ deletes the file (shows exactly once)

Test plan

  • Run /compact, send a message — context bomb should appear in the chat transcript
  • Send a second message — context bomb should NOT appear again
  • Without a prior compact, UserPromptSubmit hook exits silently (no file present)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Automatically displays cached context information in the chat transcript following user messages.

@coderabbitai
Copy link

coderabbitai bot commented Feb 27, 2026

Walkthrough

A deferred display system is introduced where uncompaction output is cached to a file, then displayed once when a user submits their next prompt via a new UserPromptSubmit hook that triggers the show-hook.sh script.

Changes

Cohort / File(s) Summary
Deferred Output Display System
hooks/hooks.json, scripts/show-hook.sh, scripts/uncompact-hook.sh
Implements a three-part caching mechanism: uncompact-hook.sh now captures output and writes to a cache file instead of directly executing; show-hook.sh is a new script that reads and displays cached content once, then deletes the file; hooks.json adds a UserPromptSubmit event hook to trigger show-hook.sh with a 5-second timeout.

Sequence Diagram

sequenceDiagram
    participant Uncompact as Uncompact Process
    participant UHook as uncompact-hook.sh
    participant Cache as Cache File
    participant User as User
    participant Submit as UserPromptSubmit Hook
    participant SHook as show-hook.sh

    Uncompact->>UHook: run --fallback
    UHook->>UHook: capture OUTPUT
    UHook->>Cache: write OUTPUT to cache file
    Note over Cache: Cache stored with<br/>UID-based filename

    User->>Submit: submit prompt
    Submit->>SHook: trigger (5s timeout)
    SHook->>Cache: read cache file
    SHook->>User: echo cached content
    SHook->>Cache: delete cache file
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🎯 The uncompact runs and stashes its words,
In a temp file cache, waiting to be heard,
User types a prompt, the hook springs alive,
Show-hook displays it once, then deletes the archive! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective: making the context bomb visible in the chat transcript after a compact operation, which is the core problem being solved.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/issue-23-20260226-2121

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

Adds a UserPromptSubmit hook (show-hook.sh) that replays the post-compact
context bomb as a visible message in the user's chat history.

Previously, uncompact-hook.sh output was injected only as silent AI context
(SessionStart:compact). Now:
- uncompact-hook.sh captures its output and writes it to a temp cache file
- show-hook.sh fires on the next UserPromptSubmit, outputs the cache to
  stdout (which UserPromptSubmit hooks display in the transcript), then
  removes the file so it only appears once

Co-Authored-By: Grey Newell <greyshipscode@gmail.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@greynewell greynewell force-pushed the claude/issue-23-20260226-2121 branch from f766839 to 7f472ed Compare February 27, 2026 02:31
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/show-hook.sh`:
- Around line 15-21: The current read-then-rm sequence in show-hook.sh using
OUTPUT="$(cat "$DISPLAY_CACHE")"; rm -f "$DISPLAY_CACHE" is racy and can cause
double-display; change the logic to atomically claim the file first by moving it
(mv "$DISPLAY_CACHE" "$DISPLAY_CACHE.$PID" or a temp name) and only read from
that moved file, then remove it, so only the process that successfully mv's the
file will print; update references to DISPLAY_CACHE and OUTPUT accordingly and
handle the case where mv fails (file not present) to skip printing.

In `@scripts/uncompact-hook.sh`:
- Around line 37-45: The cache write in uncompact-hook.sh using DISPLAY_CACHE
needs secure atomic writes and stale-cache cleanup: when OUTPUT is non-empty,
create a secure temp file in the same directory (e.g., via mktemp with the same
TMPDIR or /tmp), set restrictive permissions (owner-only, 600), write OUTPUT to
that temp file, fsync if possible, then atomically rename/move it to
DISPLAY_CACHE so readers (show-hook.sh) never see a partial file; when OUTPUT is
empty, remove/delete DISPLAY_CACHE to avoid stale content; ensure umask or
explicit chmod enforces 600 and use the DISPLAY_CACHE and OUTPUT variables from
this script to locate and update the file.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1772270 and 7f472ed.

📒 Files selected for processing (3)
  • hooks/hooks.json
  • scripts/show-hook.sh
  • scripts/uncompact-hook.sh

Comment on lines +15 to +21
# Read and remove atomically — prevent double-display if hooks fire concurrently.
OUTPUT="$(cat "$DISPLAY_CACHE")"
rm -f "$DISPLAY_CACHE"

if [ -n "$OUTPUT" ]; then
echo "$OUTPUT"
fi
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

“Atomic” read/delete is currently racy and can double-display.

If two UserPromptSubmit hooks fire close together, both can read the same file before it’s removed. Claim the file first (mv) so only one process can print it.

Suggested fix
-# Read and remove atomically — prevent double-display if hooks fire concurrently.
-OUTPUT="$(cat "$DISPLAY_CACHE")"
-rm -f "$DISPLAY_CACHE"
-
-if [ -n "$OUTPUT" ]; then
-  echo "$OUTPUT"
-fi
+# Claim first so only one concurrent hook instance can display.
+CLAIMED_CACHE="${DISPLAY_CACHE}.$$"
+if ! mv "$DISPLAY_CACHE" "$CLAIMED_CACHE" 2>/dev/null; then
+  exit 0
+fi
+
+if [ -s "$CLAIMED_CACHE" ]; then
+  cat "$CLAIMED_CACHE"
+fi
+rm -f "$CLAIMED_CACHE"
📝 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
# Read and remove atomically — prevent double-display if hooks fire concurrently.
OUTPUT="$(cat "$DISPLAY_CACHE")"
rm -f "$DISPLAY_CACHE"
if [ -n "$OUTPUT" ]; then
echo "$OUTPUT"
fi
# Claim first so only one concurrent hook instance can display.
CLAIMED_CACHE="${DISPLAY_CACHE}.$$"
if ! mv "$DISPLAY_CACHE" "$CLAIMED_CACHE" 2>/dev/null; then
exit 0
fi
if [ -s "$CLAIMED_CACHE" ]; then
cat "$CLAIMED_CACHE"
fi
rm -f "$CLAIMED_CACHE"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/show-hook.sh` around lines 15 - 21, The current read-then-rm sequence
in show-hook.sh using OUTPUT="$(cat "$DISPLAY_CACHE")"; rm -f "$DISPLAY_CACHE"
is racy and can cause double-display; change the logic to atomically claim the
file first by moving it (mv "$DISPLAY_CACHE" "$DISPLAY_CACHE.$PID" or a temp
name) and only read from that moved file, then remove it, so only the process
that successfully mv's the file will print; update references to DISPLAY_CACHE
and OUTPUT accordingly and handle the case where mv fails (file not present) to
skip printing.

Comment on lines +37 to +45
if [ -n "$OUTPUT" ]; then
# Emit to stdout — injected into Claude Code's context (AI-visible).
echo "$OUTPUT"

# Cache output for user-visible display on the next UserPromptSubmit.
# show-hook.sh picks this up and replays it into the chat transcript.
DISPLAY_CACHE="${TMPDIR:-/tmp}/uncompact-display-${UID:-$(id -u)}.txt"
echo "$OUTPUT" > "$DISPLAY_CACHE"
fi
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Cache write path needs secure + atomic handling, and stale-cache cleanup.

Right now this can (1) leave stale content when OUTPUT is empty, and (2) write cache in a way that may be partially read or too-permissive in /tmp. Tighten file mode, write atomically, and clear stale cache on empty output.

Suggested fix
 OUTPUT="$("$UNCOMPACT" run --fallback)"
+DISPLAY_CACHE="${TMPDIR:-/tmp}/uncompact-display-${UID:-$(id -u)}.txt"
 
 if [ -n "$OUTPUT" ]; then
   # Emit to stdout — injected into Claude Code's context (AI-visible).
   echo "$OUTPUT"
 
   # Cache output for user-visible display on the next UserPromptSubmit.
   # show-hook.sh picks this up and replays it into the chat transcript.
-  DISPLAY_CACHE="${TMPDIR:-/tmp}/uncompact-display-${UID:-$(id -u)}.txt"
-  echo "$OUTPUT" > "$DISPLAY_CACHE"
+  umask 077
+  TMP_CACHE="$(mktemp "${DISPLAY_CACHE}.XXXXXX")"
+  printf '%s\n' "$OUTPUT" > "$TMP_CACHE"
+  mv -f "$TMP_CACHE" "$DISPLAY_CACHE"
+else
+  rm -f "$DISPLAY_CACHE"
 fi
📝 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
if [ -n "$OUTPUT" ]; then
# Emit to stdout — injected into Claude Code's context (AI-visible).
echo "$OUTPUT"
# Cache output for user-visible display on the next UserPromptSubmit.
# show-hook.sh picks this up and replays it into the chat transcript.
DISPLAY_CACHE="${TMPDIR:-/tmp}/uncompact-display-${UID:-$(id -u)}.txt"
echo "$OUTPUT" > "$DISPLAY_CACHE"
fi
if [ -n "$OUTPUT" ]; then
# Emit to stdout — injected into Claude Code's context (AI-visible).
echo "$OUTPUT"
# Cache output for user-visible display on the next UserPromptSubmit.
# show-hook.sh picks this up and replays it into the chat transcript.
umask 077
TMP_CACHE="$(mktemp "${DISPLAY_CACHE}.XXXXXX")"
printf '%s\n' "$OUTPUT" > "$TMP_CACHE"
mv -f "$TMP_CACHE" "$DISPLAY_CACHE"
else
rm -f "$DISPLAY_CACHE"
fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/uncompact-hook.sh` around lines 37 - 45, The cache write in
uncompact-hook.sh using DISPLAY_CACHE needs secure atomic writes and stale-cache
cleanup: when OUTPUT is non-empty, create a secure temp file in the same
directory (e.g., via mktemp with the same TMPDIR or /tmp), set restrictive
permissions (owner-only, 600), write OUTPUT to that temp file, fsync if
possible, then atomically rename/move it to DISPLAY_CACHE so readers
(show-hook.sh) never see a partial file; when OUTPUT is empty, remove/delete
DISPLAY_CACHE to avoid stale content; ensure umask or explicit chmod enforces
600 and use the DISPLAY_CACHE and OUTPUT variables from this script to locate
and update the file.

@greynewell greynewell merged commit f07da89 into main Feb 27, 2026
5 checks passed
@greynewell greynewell deleted the claude/issue-23-20260226-2121 branch February 27, 2026 02:37
This was referenced Feb 27, 2026
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