--- name: memory-dream description: "Lint + consolidate the ClaudeTools repo memory store (.claude/memory/): audits index, backlinks, file paths, duplicate clusters, stale facts. Read-only default; --apply-safe does low-risk fixes; merges/deletes surfaced as proposals. Triggers: memory dream, consolidate/lint/clean up/dedupe memory." --- # Memory Dream A read-only-by-default analyzer that flags issues in the shared memory store. Mutating ops are gated behind `--apply-safe` (for low-risk fixes) or the PROPOSED section (for judgment calls a human resolves by hand). ## The two-store model (important) There are TWO separate memory stores on every machine: - REPO store -- `.claude/memory/` (88+ `*.md` files + `MEMORY.md` index). Tracked in git, syncs to all machines via Gitea. **This is the source of truth.** `CLAUDE.md` mandates writing here. - HARNESS PROFILE store -- `$HOME/.claude/projects//memory/`. Machine local, NOT in git, NOT synced. This is the store the Claude Code harness auto-injects into the system prompt at session start. The two drift over time. `memory-dream` reports that drift in its report section. The companion script `.claude/scripts/sync-memory.sh` is what actually reconciles them: it runs in **mirror mode** (since 2026-06-02) — repo is authoritative, profile is synced to match (deletions propagate; repo content wins on conflict). PROFILE-side hygiene lives in `sync-memory.sh`, not here. ## What it checks `scripts/memory_dream.py` runs six READ-ONLY analyses over the REPO store: 1. INDEX RECONCILE -- orphan files (no `MEMORY.md` line), index lines whose target file is missing, and frontmatter `name:` vs filename signals. 2. BACKLINKS -- `[[name]]` references in bodies whose target slug has no file. 3. REFERENCED-ARTIFACT VALIDITY -- conservatively extracts repo-relative file paths / script names from each body (backtick-wrapped single tokens only) and flags ones not found in the repo. Reported as **verify**, never delete (many are legitimately server-side or in sibling repos). 4. DUPLICATE / OVERLAP CLUSTERS -- groups memories by type + token-overlap / shared slug-prefix and lists candidate mergeable clusters (e.g. the many `feedback_syncro_*` files). **Proposes** merges; never performs them. 5. STALE DATED FACTS -- flags `project`-type memories with an "as of " style claim older than ~6 months for re-verification. 6. DRIFT vs PROFILE STORE -- locates the harness profile memory dir for this project and reports profile-only files (candidates to migrate INTO the repo) and repo-only files (candidates to push OUT to profile). Report only. The report ends with a `## PROPOSED (needs human approval)` section that is NEVER auto-applied. ## Modes - Default (no flag) -- **REPORT ONLY. Mutates nothing.** Writes a timestamped report to `.claude/memory/_reports/YYYY-MM-DD-HHMM-dream.md` (created if missing) and prints it to stdout. - `--apply-safe` -- performs ONLY additive, non-destructive fixes and prints each action: - (a) append missing index lines to `MEMORY.md` for orphan files, under the correct `## ` header, never reordering or removing existing lines; - (b) copy profile-only memory files INTO the repo store (additive migration). If a same-named repo file already exists it is SKIPPED and the conflict is reported -- it is never overwritten. - `--no-file` -- print to stdout only; skip writing the `_reports/` file. - `--report-file ` -- write the report to an explicit path. ### What dream does NOT auto-do `memory-dream` does NOT, even with `--apply-safe`: - delete a repo memory file (cluster dedup is a judgment call — pick which file becomes canonical, fold the others' content, retire the originals deliberately); - remove or reorder index lines (index cleanups are also surfaced as proposals); - overwrite a file whose content differs; - perform a proposed merge. These stay in the report's `## PROPOSED` section. The rationale isn't "never delete" any more (the fleet-wide additive safety net was dropped 2026-06-02; see `feedback_memory_sync_destructive_ok.md`) — it's that merges and dedups require human judgment about which file is canonical and how to combine content. Profile-side deletion DOES happen automatically — but in `sync-memory.sh`, not here. ### The operator MUST execute consolidations — do not just propose and leave The script is additive-only **by design** (auto-merging would corrupt the store — see the trap below), but the SESSION running `/memory-dream` is NOT additive-only. **Executing the `## PROPOSED` consolidations is the expected work of a dream run, not a someday-maybe.** Parking proposals indefinitely is exactly what causes the fleet drift this skill exists to prevent. Each run, after reading the report: 1. **Triage every `[MERGE?]` cluster with judgment** (the detector is a coarse net): - **Intentional `X` + `X_history` (or `_archive`/`_detail`) splits are NOT duplicates** — current-state vs on-demand archive, cross-linked in frontmatter. Leave them. (The detector skips these as of 2026-06-11; older reports may still list them.) - **Topically-clustered but distinct facts** (e.g. several `reference_gitea_*`) — merge into one topic file ONLY if genuinely redundant or it reads better as one; otherwise leave. - **True duplicates / superseded files** — merge: pick the canonical file, fold in the others' unique content, `git rm` the retired ones, fix `MEMORY.md`. 2. **Deletions are first-class.** `git rm` of a retired memory is correct and propagates to every machine's profile store via `sync-memory.sh` mirror mode (repo authoritative). This is the fleet-consistency mechanism — use it. 3. **Commit + sync** so the consolidated store reaches the fleet. If a cluster genuinely needs a decision you can't make, leave it AND say so explicitly in your summary — don't silently skip the whole PROPOSED section. **The auto-merge trap (why the script stays additive):** blindly merging the detector's clusters destroys deliberate structure — verified 2026-06-11 when the flagged `project_*` / `project_*_history` pairs turned out to be intentional splits and the `gitea` / `syncro` clusters were distinct facts, not copies. Consolidation needs the operator's judgment; the script must never do it unattended. ## Running it This machine's Python launcher is `py` (per identity.json); the script also runs under `python` / `python3`. Stdlib only -- no pip deps. ```bash # REPORT ONLY (default) -- writes _reports/-dream.md and prints it bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "$CLAUDETOOLS_ROOT/.claude/skills/memory-dream/scripts/memory_dream.py" # report to stdout only, write nothing bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "$CLAUDETOOLS_ROOT/.claude/skills/memory-dream/scripts/memory_dream.py" --no-file # additive-only fixes (append orphan index lines, migrate profile-only files) bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "$CLAUDETOOLS_ROOT/.claude/skills/memory-dream/scripts/memory_dream.py" --apply-safe ``` `CLAUDETOOLS_ROOT` resolves from the env var, else `claudetools_root` in `.claude/identity.json`, else the repo root derived from the script's own location -- no hardcoded drive letters. ## Cleanup / approve workflow 1. Run with no flag. Read the report (stdout or `_reports/-dream.md`). 2. Run `--apply-safe` to take the safe additive wins: orphan index lines get added, profile-only memories get migrated into the repo (conflicts skipped and reported). 3. Work the `## PROPOSED` section by hand: - `[MERGE?]` -- decide whether to consolidate a cluster. If yes, author a new combined memory (or set of files for a rule/history split), retire the originals via `git rm`, update `MEMORY.md`. Deletions are now first-class — `sync-memory.sh` mirror mode will propagate them to every profile store on the next run. - `[REVERIFY?]` -- confirm the dated fact still holds; update the body and its date if it changed. - `[STALE-REF?]` -- confirm the referenced path moved/renamed; repoint or annotate. Many are legitimately server-side (`.service` units, `/opt/...`). - `[INDEX-CLEANUP?]` / `[DRIFT-RESOLVE?]` -- human picks the winner. 4. Commit the repo store changes so they sync to the fleet via Gitea. ## Self-test `scripts/selftest.py` runs the analyzer against a synthetic fixture memory store in a temp dir and asserts each detector fires (orphan, missing target, broken backlink, stale path, cluster, profile drift) and that `--apply-safe` only touches the things it's supposed to (index appends + profile→repo copy of new files; no deletions, no merges, no overwrites of differing content). Run: ```bash bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "$CLAUDETOOLS_ROOT/.claude/skills/memory-dream/scripts/selftest.py" ```