Files
claudetools/.claude/skills/memory-dream/SKILL.md
Mike Swanson 9b1c5c391d harness: fix py-vs-python3 doc gap — add py.sh resolver, repoint skill/command docs
The skill/command DOCS instructed Claude to run a bare `py ...`, which is the
Windows py-launcher — absent on Linux/macOS (exit 127, hit on GURU-KALI). A blind
py->python3 swap is wrong too: python3 is a broken MS Store shim on some Windows
boxes where `py` is the correct launcher.

Fix mirrors the resolution the .sh skill scripts already do:
- New .claude/scripts/py.sh: picks the interpreter that actually RUNS —
  identity.json python.command first, then py -> python3 -> python, each
  validated with `-c 'import sys'` so the MS Store stub is skipped. exec's it.
- Repointed all DOC invocations (10 files, ~70 sites) from `py ...` to
  `bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ...` (incl. the `py -c` and
  `py -` heredoc forms in checkpoint.md / mailbox.md).
- Left the .sh skill scripts untouched — they already resolve py/python/python3.
- errorlog.md: marked the GURU-KALI entry RESOLVED.

Depends on CLAUDETOOLS_ROOT (seeded by ensure-settings-env.py); py.sh also
self-resolves the repo root via git/cwd as a fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 09:48:54 -07:00

8.7 KiB

name, description
name description
memory-dream 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/<slug>/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 ## <Type> 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 <path> -- 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.

# REPORT ONLY (default) -- writes _reports/<stamp>-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/<stamp>-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 "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "$CLAUDETOOLS_ROOT/.claude/skills/memory-dream/scripts/selftest.py"