Invariant
An archiver that runs on every session exit must have a bounded footprint, or
a documented way to bound it. Unbounded growth turns a preservation tool into
a disk-exhaustion mechanism.
Current violation (observed 2026-08-12)
session-end-archive.sh writes one gzipped archive per session into
CLAUDE_TRANSCRIPT_ARCHIVE_DIR (default ~/.claude/transcript-archive) and
never prunes. There is no retention window, no size quota, no count cap,
and the README documents no cleanup path.
The failure mode is quiet and self-reinforcing:
- The archive directory grows without bound, one entry per session forever.
- When the filesystem fills,
gzip -c > "$tmp" fails.
- The global
trap 'exit 0' ERR converts that to a successful exit, and
exec 2>/dev/null suppresses the diagnostic.
- Archiving has silently stopped, at exactly the moment it is least likely to
be noticed, and unrelated writes on the same filesystem start failing too.
This is sharper in a devcontainer than on a host, which is what prompted
filing it: harmon-init wires the hook into a container where
~/.claude is a fixed-size Docker volume, so the ceiling is lower and shared
with everything else on that volume (evanharmon1/harmon-init#816).
Not an argument against the hook — losing transcripts to a 30-day sweep is the
worse failure. It is an argument that "keep forever" needs to be a choice
rather than the only behaviour.
Verify
grep -nE 'find .* -mtime|prune|retention|quota|MAX_' \
templates/claude-hooks/session-end-archive/session-end-archive.sh
grep -niE 'retention|prune|cleanup|disk' \
templates/claude-hooks/session-end-archive/README.md
No matches from either means neither a mechanism nor a documented manual path
exists. Resolved when one does.
Options
- Retention by age — an env-configurable window
(CLAUDE_TRANSCRIPT_ARCHIVE_KEEP_DAYS), pruned opportunistically after a
successful archive. Simple, and it mirrors the cleanupPeriodDays behaviour
the hook exists to outlive, just with a longer default.
- Cap by count or size — keep the newest N, or stay under N MB. Bounded
regardless of session cadence, but needs a stable sort and is more code in a
script whose failure paths are all silent.
- Document only — ship a cleanup recipe in the README and leave the script
alone. Cheapest, and honest about being best-effort, but the failure it
leaves is silent.
Whichever is chosen, the disk-full path deserves a thought: today it is
indistinguishable from success.
Acceptance criteria
Found while wiring this hook into harmon-init's devcontainer
(evanharmon1/harmon-init#816).
Invariant
An archiver that runs on every session exit must have a bounded footprint, or
a documented way to bound it. Unbounded growth turns a preservation tool into
a disk-exhaustion mechanism.
Current violation (observed 2026-08-12)
session-end-archive.shwrites one gzipped archive per session intoCLAUDE_TRANSCRIPT_ARCHIVE_DIR(default~/.claude/transcript-archive) andnever prunes. There is no retention window, no size quota, no count cap,
and the README documents no cleanup path.
The failure mode is quiet and self-reinforcing:
gzip -c > "$tmp"fails.trap 'exit 0' ERRconverts that to a successful exit, andexec 2>/dev/nullsuppresses the diagnostic.be noticed, and unrelated writes on the same filesystem start failing too.
This is sharper in a devcontainer than on a host, which is what prompted
filing it: harmon-init wires the hook into a container where
~/.claudeis a fixed-size Docker volume, so the ceiling is lower and sharedwith everything else on that volume (evanharmon1/harmon-init#816).
Not an argument against the hook — losing transcripts to a 30-day sweep is the
worse failure. It is an argument that "keep forever" needs to be a choice
rather than the only behaviour.
Verify
No matches from either means neither a mechanism nor a documented manual path
exists. Resolved when one does.
Options
(
CLAUDE_TRANSCRIPT_ARCHIVE_KEEP_DAYS), pruned opportunistically after asuccessful archive. Simple, and it mirrors the
cleanupPeriodDaysbehaviourthe hook exists to outlive, just with a longer default.
regardless of session cadence, but needs a stable sort and is more code in a
script whose failure paths are all silent.
alone. Cheapest, and honest about being best-effort, but the failure it
leaves is silent.
Whichever is chosen, the disk-full path deserves a thought: today it is
indistinguishable from success.
Acceptance criteria
Found while wiring this hook into harmon-init's devcontainer
(evanharmon1/harmon-init#816).