Memory Hygiene

CLAUDE.md has evolving-claude-md. This governs the other half of the context an agent loads every session: the per-project memory directory under ~/.claude/projects/<slug>/memory/, written by the agent, largely unsupervised.

The failure modes differ, and the difference is the design. Bloat makes an agent ignore instructions; rot makes it confidently recall something false — a version that has moved on, a "latest migration" that isn’t, a path that no longer exists. So where the sibling prunes down, this one re-verifies and invalidates. Across the agent-memory ecosystem, systems evict by size, age, or recency; none evicts a fact because it became wrong. That is the gap this fills, and it needs no database — a lint and two hooks.

Install

/plugin install memory-hygiene@alexmskills

Two hooks register on enable: a SessionStart sweep and a PreToolUse write lint. Nothing to configure; silent when healthy.

The sweep

Every check is grounded against the actual tree — nothing is flagged by pattern alone:

Check Fires when

Vanished artifact

A backticked path/class/flag git grep can no longer find and that isn’t on disk. Memory-specific filters run first: --flags, <placeholders>, …​-abbreviated paths, and paths rooted outside this tree are pointers, not rot here — never checked.

Stale version pin

Memory states jhelm 1.3.1 and every parseable build-file spec now contradicts it. Release lines (4.1.x), examples, and struck-through lines stay silent.

Stale sequence fact

"latest is V27`" once `V28__*.sql exists on disk, or "next is `V28`" once V28 already happened.

Index drift

Both directions: MEMORY.md lines pointing at files that no longer exist, and memory files with no index line — invisible to recall.

Calibrated against 15 real memory corpora on the author’s machine (184 files): 15 findings, the majority verified rot — including a memory pinning a dependency two minor versions behind the build and three "epic complete" notes citing symbols since renamed.

Supersede, never delete

A flag is a re-verification request, not a verdict. When a fact is genuinely wrong, strike it through and append a dated correction in the same file. The reasoning stays: a memory that keeps a wrong conclusion but loses the why leads to confident re-emission, while an absent memory at least leads to abstention. Struck lines are skipped on the next sweep — the correction is also what silences the auditor.

The write contract

Location is the contract, enforced by the PreToolUse hook — the one mechanism that has held where prose instructions failed:

  • Fact files must open with frontmatter (name:, description:, metadata.type in user | feedback | project | reference), and added prose must not use relative dates — "last week" expires silently; 2026-08-11 does not.

  • MEMORY.md stays an index: one-line pointers only. Paragraph prose or frontmatter there is memory content in the wrong territory.

Writes outside a memory directory are never touched.

Shared core

Both this plugin and evolving-claude-md vendor the same freshness.py — stdlib-only, pure functions over (text, repo_root) — so "is this fact still true on disk?" has exactly one implementation. The marketplace harness pins the two copies byte-identical.