Skip to content

session-end-archive: archives accumulate without bound, and a full disk silently stops archiving #440

Description

@evanharmon1

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:

  1. The archive directory grows without bound, one entry per session forever.
  2. When the filesystem fills, gzip -c > "$tmp" fails.
  3. The global trap 'exit 0' ERR converts that to a successful exit, and
    exec 2>/dev/null suppresses the diagnostic.
  4. 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

  1. 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.
  2. 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.
  3. 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

  • A retention mechanism exists, or the README documents a cleanup path
  • If a mechanism: it is configurable, and its default is stated in the README
  • Pruning never deletes the archive it just wrote, and never runs before it
  • Pruning failure remains non-fatal — it must not make session exit noisy
  • The existing offline test suite covers the pruning path

Found while wiring this hook into harmon-init's devcontainer
(evanharmon1/harmon-init#816).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions