feat: show context bomb in chat transcript after compact with acknowledgment#39
Conversation
WalkthroughThis PR introduces post-compact capability through a new Changes
Sequence DiagramsequenceDiagram
participant User as User/CLI
participant Cmd as cmd/run.go
participant Render as internal/template/render.go
participant Cache as Display Cache
User->>Cmd: Run with --post-compact flag
Cmd->>Render: Call Render(RenderOptions{PostCompact: true, ...})
Render->>Render: Generate initial content
alt Content fits within token budget
Render->>Render: Keep fullText as-is
else Content exceeds budget
Render->>Render: Truncate to budget
end
Render->>Render: Build post-compact note
Render->>Render: Append note to result
Render->>Cmd: Return (content + note, tokenCount, error)
Cmd->>Cache: Write result to cache
Cache->>User: Render output with acknowledgment
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cmd/run.go`:
- Around line 169-170: The no-cache fallback path drops the PostCompact option
so the acknowledgment instruction is lost; update the runWithoutCache call (and
any render options it builds) to include PostCompact: postCompact (same symbol
used when cache is enabled) so the PostCompact flag is forwarded into the render
options in degraded mode; locate runWithoutCache and the render/options
construction and add the PostCompact field to match the cached-path option
passing.
In `@internal/hooks/hooks.go`:
- Around line 187-190: The isAlreadyInstalled function currently checks for the
wrapper form on Stop but not on UserPromptSubmit, causing false negatives;
update isAlreadyInstalled so the UserPromptSubmit check also accepts the wrapper
script name by calling commandExistsInHooks(hooks["UserPromptSubmit"],
"uncompact show-cache", "uncompact-hook.sh") (mirroring the Stop check) so both
"uncompact show-cache" and the wrapper "uncompact-hook.sh" are considered
installed.
In `@internal/template/render.go`:
- Around line 139-143: The post-compact note is appended after budget
enforcement and can push result over MaxTokens; change the flow in the render
logic so that when opts.PostCompact is true you first construct or estimate the
note text (using the same fmt.Sprintf pattern), compute its token cost with
countTokens(note), then reserve that many tokens before enforcing/truncating to
MaxTokens (i.e., enforce/truncate result to MaxTokens - noteTokens), and finally
append the note and update result/resultTokens; update any code that uses
resultTokens, result, opts.PostCompact, and countTokens to use this
reserved-note-aware truncation so the final output never exceeds the token
budget.
In `@scripts/uncompact-hook.sh`:
- Around line 38-43: The DISPLAY_CACHE write is insecure because the path is
predictable and the write can be raced; change the flow where OUTPUT is written
to DISPLAY_CACHE so you first set a restrictive umask (umask 077), create a
secure temporary file with mktemp, write OUTPUT into that temp file, optionally
chmod to ensure 600, then atomically mv the temp into the DISPLAY_CACHE final
name (so the file is never visible with weak perms or partially-written); update
the block that checks OUTPUT and writes to DISPLAY_CACHE to use this pattern to
prevent information leakage and races.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
.github/workflows/claude.ymlcmd/install.gocmd/run.gocmd/showcache.gointernal/hooks/hooks.gointernal/template/render.goscripts/show-hook.shscripts/uncompact-hook.sh
uncompact-hook.sh was baking the status line into the display cache, then show-hook.sh / show-cache added it again on readback — producing two identical lines. Write only the raw context bomb to the cache; the display hook is responsible for appending the status line. Co-Authored-By: Grey Newell <greyshipscode@gmail.com> Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pass postCompact flag through to runWithoutCache so the acknowledgment instruction is not lost on the no-cache fallback path. Co-Authored-By: Grey Newell <greyshipscode@gmail.com> Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add show-hook.sh as an accepted wrapper form for the UserPromptSubmit hook in isAlreadyInstalled, mirroring the Stop hook check, to prevent false-negative results when the wrapper script form is installed. Co-Authored-By: Grey Newell <greyshipscode@gmail.com> Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reserve token budget for the post-compact acknowledgment note before enforcing the MaxTokens limit, so the final output never exceeds the configured token budget. Co-Authored-By: Grey Newell <greyshipscode@gmail.com> Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use umask 077 + mktemp + atomic mv to write the display cache securely, preventing information leakage and TOCTOU races on multi-user systems. Co-Authored-By: Grey Newell <greyshipscode@gmail.com> Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
52539aa to
fad86a1
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
internal/hooks/hooks.go (1)
205-210: Optional: align skip logic withisAlreadyInstalled.For
Stop, you check both"uncompact run"and"uncompact-hook.sh". ForUserPromptSubmit, you're only checking"uncompact show-cache"but not"show-hook.sh". If someone ever callsmergeHooksdirectly (or the flow changes), this could lead to duplicates.🔧 Suggested alignment
case "UserPromptSubmit": - matches = append(matches, "uncompact show-cache") + matches = append(matches, "uncompact show-cache", "show-hook.sh")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/hooks/hooks.go` around lines 205 - 210, The skip logic in the switch on event currently appends both "uncompact run" and "uncompact-hook.sh" for the "Stop" case but only "uncompact show-cache" for "UserPromptSubmit", which can cause duplicates when mergeHooks or other flows run; update the "UserPromptSubmit" branch in the same switch (in internal/hooks/hooks.go) to also append "show-hook.sh" (mirroring the pattern used for "Stop"), so skip detection aligns with isAlreadyInstalled and prevents duplicate hook entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@internal/hooks/hooks.go`:
- Around line 205-210: The skip logic in the switch on event currently appends
both "uncompact run" and "uncompact-hook.sh" for the "Stop" case but only
"uncompact show-cache" for "UserPromptSubmit", which can cause duplicates when
mergeHooks or other flows run; update the "UserPromptSubmit" branch in the same
switch (in internal/hooks/hooks.go) to also append "show-hook.sh" (mirroring the
pattern used for "Stop"), so skip detection aligns with isAlreadyInstalled and
prevents duplicate hook entries.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
cmd/run.gointernal/hooks/hooks.gointernal/template/render.goscripts/uncompact-hook.sh
🚧 Files skipped from review as they are similar to previous changes (1)
- scripts/uncompact-hook.sh
Summary
uncompact show-cachecommand that emits the display cache to stdout (for the UserPromptSubmit hook), making the context bomb visible as a chat message after compact--post-compactflag touncompact run; when set, appends an instruction asking Claude to acknowledge restoration with✓ Uncompact: context restored (~N tokens)— gives users a clear visible signal that context was re-injected[uncompact] Context restoredstatus line that appeared when both the hook and the display command each appended the line)SessionStart:compact,PreCompact, andPostToolUsehooks to the plugin'shooks.jsonTest plan
/compactand send a message — Claude's response should open with✓ Uncompact: context restored (~N tokens)[uncompact] Context restoredline appears (not two)go build ./...andgo vet ./...pass@claude please review
Summary by CodeRabbit
New Features
--post-compactflag to control output rendering with acknowledgment notes.Improvements