Memory anchoring¶
src/memory_anchor.py
walks the auto-memory store and flags references that no longer
resolve against the current repository.
Why it exists¶
Claude's auto-memory accumulates notes that look like:
Fixed the
add_skill()bug insrc/skill_loader.py:42. See alsodocs/intent-interview.md.
Those backtick references rot as the codebase moves. A renamed file, a
deleted module, a moved doc — and the memory silently points at
nothing. memory_anchor turns that silent rot into a loud dashboard.
Where it looks¶
Memory files live under:
The module recursively scans that tree. You can override the root with
--memory-root (useful for tests or multi-project setups).
What counts as a reference¶
Only tokens inside backtick code spans qualify. The heuristic is deliberately conservative to keep false positives low:
- known extension (
.py,.md,.json,.yml,.ts,.rs, …), or - contains a
/with a dotted final segment.
Tokens with whitespace, () suffixes, http(s):// prefixes, or leading
- are rejected up front. A trailing :<digits> is parsed as a line
suffix — : without digits is preserved (keeps Windows drive letters
intact).
How resolution works¶
For each extracted reference, the module asks whether it resolves:
- tilde-expand, if
~/… - if absolute, does the path exist?
- does
repo_root / pathexist? - does
repo_root / src / pathexist?
If any candidate hits, the ref is live; otherwise dead.
CLI¶
# JSON report for downstream tooling
python -m memory_anchor scan
# Human dashboard
python -m memory_anchor dashboard
# CI gate: exit 2 if any dead references remain
python -m memory_anchor check --strict
# Override repo / memory roots
python -m memory_anchor check --strict \
--repo-root /path/to/repo \
--memory-root /path/to/project/memory
When --repo-root is omitted, the module walks upward from the current
directory to the nearest .git/ ancestor.
Data model¶
@dataclass(frozen=True)
class AnchorRef:
raw: str # exactly the backtick contents
path: str # path sans trailing :<line>
line: int | None
exists: bool
@dataclass(frozen=True)
class MemoryAnchorFile:
memory_path: str
refs: tuple[AnchorRef, ...]
# derived: .live, .dead
@dataclass(frozen=True)
class AnchorReport:
generated_at: float
repo_root: str
memory_root: str
files: tuple[MemoryAnchorFile, ...]
# derived: .all_refs, .live_count, .dead_count, .has_dead
Related¶
- Skill health dashboard — structural and drift checks for the skill + agent catalog.