feat(plugin): show context bomb in chat transcript after compact#30
feat(plugin): show context bomb in chat transcript after compact#30greynewell merged 1 commit intomainfrom
Conversation
WalkthroughA 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
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
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>
f766839 to
7f472ed
Compare
There was a problem hiding this comment.
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.
| # 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 |
There was a problem hiding this comment.
“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.
| # 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.
| 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 |
There was a problem hiding this comment.
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.
| 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.
Summary
uncompact-hook.shran atSessionStart:compactand emitted context to stdout — but that only reaches the AI as silent context. Users never saw the context bomb in their chat window.uncompact-hook.shnow captures its output and writes it to a per-user temp file. A newshow-hook.shruns onUserPromptSubmit, replays the cached output once (making it visible in the transcript), then deletes the file.io.ReadAllinGetGraphwith a 10 MBio.LimitReader. The equivalent fix inGetCircularDependenciesis left as a TODO.How it works
Test plan
/compact, send a message — context bomb should appear in the chat transcript🤖 Generated with Claude Code
Summary by CodeRabbit