Problem
cmd/showcache.go:25-34 reads the display cache file and then deletes it in two separate operations:
data, err := os.ReadFile(cachePath) // line 25
...
os.Remove(cachePath) // line 34
The UserPromptSubmit hook runs uncompact show-cache on every prompt submission. If the user submits two prompts in quick succession before the first hook invocation finishes, two concurrent processes can both reach os.ReadFile before either has executed os.Remove. Both will succeed, both will print the context bomb to stdout, and Claude Code receives the context injected twice in the same conversation.
Reproduction
- Install Uncompact hooks.
- In a Claude Code session, submit two prompts very rapidly back-to-back immediately after a
Stop event writes the display cache.
- Observe the context bomb appearing twice in the second conversation turn.
Fix
Replace the read-then-delete pattern with an atomic rename-then-read:
tmpPath := cachePath + ".consuming"
if err := os.Rename(cachePath, tmpPath); err != nil {
if os.IsNotExist(err) {
return nil // Another invocation already consumed it.
}
return nil // Rename failed — silent exit.
}
data, err := os.ReadFile(tmpPath)
os.Remove(tmpPath) // Clean up the temp file.
os.Rename is atomic on all POSIX systems. Only the first invocation to win the rename will see the data; subsequent ones get IsNotExist and exit cleanly.
Files: cmd/showcache.go:25-34
@claude please implement this