Install any skill in seconds. Free to start, no credit card required.
Get Started Free →This skill should be used when the user asks to "consolidate memory", "tidy memory", "rebuild MEMORY.md", or wants to normalize, deduplicate, prune, or rebuild project memory. Consolidates the project's memory as one unlayered store — the private harness memory (~/.claude/projects/<escaped-cwd>/memory) and the repo-local memory (docs/memory/).
.claude/skills/fradser-consolidate/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-12 | ✗→✓ | ▲ Improved | 179% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 196% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 125% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 167% | 0% |
| case-20 | ✗→✓ | ▲ Improved | 151% | 0% |
The project's memory lives in two locations that must stay identical for safe (public) files (idempotent):
~/.claude/projects/<escaped-cwd>/memory/ — harness, loaded by Claude Code, written first.memory/ — canonical, git-tracked, written second — safe files onlyResolve the harness path: ~/.claude/projects/<cwd-with-/→->/memory/ (probe both space-handling forms: /→-+ →- and /→-+space-kept).
Private files (user preferences, credentials, PII) live in harness only. They must never appear as files or index lines under .memory/.
When you encounter a decision, preference, lesson, or anything worth remembering, write it immediately — do not wait for /memory:consolidate.
*.md by theme keywords). If one already covers the topic, edit that file instead of creating a near-duplicate..memory/ is a public GitHub repo. Private = secrets/PII/credentials or user preferences / personal workflow habits. Safe technical content → both locations. Private → harness only; harness index line ends with (harness only).
.../memory/<filename>.md.memory/<filename>.md. If private → do not write to .memory/; if .memory/<filename>.md already exists, delete it.MEMORY.md: include every file (private lines marked (harness only)).memory/MEMORY.md: safe lines only — never copy (harness only) lines into the public indexFile naming: <type>_<kebab-slug>.md (type: feedback, project, reference)
Format:
markdown--- name: <kebab-slug> description: <one-line hook distinguishing this from similar files> type: feedback | project | reference --- <the fact> **Why:** <why this decision exists> **How to apply:** <actionable rules> **Related:** [[other-memory]] [[another-memory]]
Every memory file answers two questions only:
Remove all operation history (version numbers, dates-as-timeline, "first X then Y"). That lives in git log. Keep only the durable rationale and actionable rules.
Each line: one concise sentence, no version numbers, no date ranges, no timeline descriptions. One line per surviving file. Prefer ≤50 lines for scanability; if more files exist, keep every entry and group by theme — never drop entries to hit 50.
Good: feedback_git_commit_hook_needed.md — git PreToolUse hook intercepts git add/commit, redirects to /git:commit; allows chain + GIT_SKILL_FALLBACK=1 escape Bad: feedback_git_commit_hook_needed.md — git PreToolUse hook intercepts git add/commit; v0.5.3 command position anchoring + two exceptions + 26 regression tests
[[name]] cross-links when rewriting — preserve all from the original unless the target is intentionally deleted and the reference is removed in the same pass[[name]] in another memory file unless the reference is also removedrm only under the harness memory directory or .memory/ — never elsewhere.memory/ (file body or index line)User-invoked only. No auto-consolidation. Default failure mode of a weak run is cosmetic tidy while leaving thematic redundancy and factually dead notes. This procedure is fail-closed against that.
Work order: harness first, then sync to .memory/.
Do not Write, Edit, or rm any memory file until all three artifacts exist in this conversation:
*.md and .memory/*.md (including both MEMORY.md)After ground-truth probes (step 5), also hold a ground-truth table with tool-observed paths (path → found|missing|updated) before applying claim fixes. Fabricating these tables without Read/Grep/find/ls is a failed run.
Write inventory / cluster / staleness / report to temp files, then run:
bash# Pre-mutation (lift freeze only if exit 0) python3 "${CLAUDE_PLUGIN_ROOT}/scripts/validate-consolidate.py" \ --inventory /tmp/mem-inventory.txt \ --cluster /tmp/mem-cluster.txt \ --staleness /tmp/mem-staleness.txt \ --check=cluster,staleness # Post-sync before claiming done (exit 0 required) python3 "${CLAUDE_PLUGIN_ROOT}/scripts/validate-consolidate.py" \ --inventory /tmp/mem-inventory.txt \ --cluster /tmp/mem-cluster.txt \ --staleness /tmp/mem-staleness.txt \ --report /tmp/mem-report.md \ --harness "<harness-memory-dir>" \ --public "<repo>/.memory"
Exit 1 → fix artifacts / privacy and re-run. Exit 0 is required to lift the mutation freeze (pre) and to report consolidate complete (post). Do not claim G2/G3/G4/privacy by prose alone.
Do not claim consolidate complete unless every gate below is true:
| Gate | Requirement | |------|-------------| | G1 Read | Every *.md in harness and .memory/ was read (including both MEMORY.md) | | G2 Cluster | Theme-cluster map covers every non-index file before any merge/delete | | G3 Staleness | Every file scored with the staleness rubric (not calendar age alone) | | G4 Ground truth | Every project_* claim checked against the current tree with cited paths (or N/A with reason) | | G5 Merge bias | Every multi-file cluster either merged, or has an explicit one-line "keep separate because …" | | G6 Adversarial | Independent second pass ran when required (step 8); findings applied or rejected with reason | | G7 Report | Report includes inventory counts, cluster map, prune/merge table, ground-truth table, residual risks | | G8 Validator | validate-consolidate.py exit 0 on pre-mutation (cluster,staleness) and post-sync (full checks) |
If any gate fails mid-run, continue until it passes — do not stop at "normalized frontmatter + rebuilt index".
Read every *.md in both harness memory and .memory/, including MEMORY.md. Detect drift (name sets and content hashes / word-level diffs). List harness-only private files. Emit the inventory.
Plan only until artifacts in the freeze section exist:
YYYY-MM-DDname, description, type only (strip node_type, originSessionId, modified, nested metadata)description specific enough to distinguish similar filesNormalization alone is not consolidation.
Group every non-index file into theme clusters (e.g. deploy, billing, review pipeline, naming). Use overlapping keywords, shared [[links]], and near-duplicate descriptions.
Output a cluster map (keep it for the report):
textcluster: <theme> - file-a.md - file-b.md merge-default: yes|no — <one line>
Default bias: 2+ files in one theme → merge into one decision log, unless each holds a distinct durable decision that would become muddled if combined.
Score every non-index file. Calendar age is only one signal — a note can be days old and still SUPERSEDED or OPS-ONLY.
Verdicts: CONTRADICTED | SUPERSEDED | SUBSUMED | OPS-ONLY | ONE-SHOT | DORMANT | KEEP
Read references/staleness-examples.md for the full table, actions, and worked examples. Protect feedback_* preferences: incident dates do not make them OPS-ONLY.
For each project_* (and any reference_* that asserts repo-local facts):
VERIFIED | UPDATED | PRUNED | N/A (no repo) | UNVERIFIABLE (state why) plus tool-observed path → found|missingUse Grep, Read, and allowed Bash (find/ls/stat/diff/…) against the project — not arbitrary shell. Do not trust memory text over the tree.
feedback_* about user/process preferences: verify consistency with other feedback files and current plugin/skill code when they name a mechanism; do not invent user-preference changes. Prefer KEEP on preference files unless contradicted by the user's later explicit preference.
Mutation freeze lifts only after inventory + cluster map + staleness table exist and pre-mutation validate-consolidate.py --check=cluster,staleness exits 0. For claim edits, also hold the ground-truth table first.
[[name]] targets; rewrite links to survivors after renames/merges[[name]] cross-linksRun a second pass with clean context when any of: starting count ≥ 8 (excluding MEMORY.md); any cluster has 2+ files; ≥3 merges/deletes; user mentioned redundancy/stale memory; or you are unsure about keep-separate.
How: launch Agent (subagent_type general-purpose or Explore; fresh context). Pass inventory, cluster map, and paths only — not your keep-separate justifications. Ask it to propose merges, flag CONTRADICTED/SUPERSEDED/OPS-ONLY, list near-duplicate descriptions, and default to merge or prune when uncertain. Apply accepted findings; reject with one-line reasons in the report.
Zero findings despite multi-file clusters → re-check the largest cluster yourself. Skip only when count ≤ 5, all clusters size 1, no mutations, and ground-truth found nothing stale — state the skip. Never skip because the first pass "felt thorough".
Rewrite two indexes:
MEMORY.md — one line per surviving harness file; private lines end with (harness only).memory/MEMORY.md — one line per safe survivor only; strip every (harness only) lineNo version/date theater. Group by theme if helpful. Keep every required entry (no hard 50-line drop).
.memory/ (privacy fail-closed)For each file in harness memory:
.memory/.memory/; delete .memory/<same name> if it exists (stale public copy)*.md in .memory/ that are not among current safe harness files.memory/ readAfter sync: public safe sets match; no private bodies or private index lines remain under .memory/.
rm targets only paths under harness memory or .memory/.
text## Consolidate report - Inventory: harness N, .memory/ M, drift: … - Clusters: (map covering every non-index file) - Staleness: verdict counts - Ground truth: rows with path → found|missing + VERIFIED/UPDATED/PRUNED counts - Merged: (old files → survivor) × reasons - Pruned: (file × rubric verdict × reason) - Kept-separate: (cluster × justification) - Privacy: private files kept harness-only; public scrub actions - Adversarial pass: ran|skipped — findings applied/rejected - Index rebuilt: harness yes|no; .memory yes|no (safe-only) - Validator: pre exit=…; post exit=… (paste PASSED/FAILED summary) - Residual risks: anything still fuzzy - Gates: G1–G8 checklist
Write the report body to a temp file and run the post-sync validator (full checks). Exit non-zero → do not claim done. If nothing changed after a full gated run, still show inventory, cluster map, ground-truth, and validator output.
(harness only) lines) into both locations.memory/ after a later privacy classificationvalidate-consolidate.py| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 8,638 | 12,259 | +42% | 1 | 1 | 0% | 755 | 4,168 | +452% | 0 | 0 | — |
case-02 | fail→fail | 7,556 | 7,322 | -3% | 1 | 1 | 0% | 526 | 3,966 | +654% | 0 | 0 | — |
case-03 | fail→fail | 5,561 | 7,480 | +35% | 1 | 1 | 0% | 400 | 4,177 | +944% | 0 | 0 | — |
case-04 | fail→fail | 12,577 | 5,953 | -53% | 1 | 1 | 0% | 2,086 | 3,938 | +89% | 0 | 0 | — |
case-05 | pass→pass | 9,338 | 9,156 | -2% | 1 | 1 | 0% | 1,413 | 4,357 | +208% | 0 | 0 | — |
case-06 | pass→fail | 2,343 | 6,342 | +171% | 1 | 1 | 0% | 325 | 4,183 | +1187% | 0 | 0 | — |
case-07 | fail→fail | 7,011 | 6,188 | -12% | 1 | 1 | 0% | 1,128 | 3,891 | +245% | 0 | 0 | — |
case-08 | fail→fail | 4,231 | 7,627 | +80% | 1 | 1 | 0% | 691 | 4,013 | +481% | 0 | 0 | — |
case-09 | fail→fail | 2,116 | 8,974 | +324% | 1 | 1 | 0% | 305 | 4,102 | +1245% | 0 | 0 | — |
case-10 | fail→fail | 1,939 | 6,761 | +249% | 1 | 1 | 0% | 253 | 4,048 | +1500% | 0 | 0 | — |
case-11 | pass→fail | 3,455 | 9,568 | +177% | 1 | 1 | 0% | 533 | 4,284 | +704% | 0 | 0 | — |
case-12 | fail→pass | 11,642 | 7,566 | -35% | 1 | 1 | 0% | 1,722 | 4,800 | +179% | 0 | 0 | — |
case-13 | fail→fail | 5,843 | 5,707 | -2% | 1 | 1 | 0% | 925 | 4,575 | +395% | 0 | 0 | — |
case-14 | pass→fail | 10,631 | 5,525 | -48% | 1 | 1 | 0% | 1,621 | 4,446 | +174% | 0 | 0 | — |
case-15 | pass→pass | 9,739 | 4,983 | -49% | 1 | 1 | 0% | 1,680 | 4,473 | +166% | 0 | 0 | — |
case-16 | fail→pass | 8,270 | 4,598 | -44% | 1 | 1 | 0% | 1,479 | 4,375 | +196% | 0 | 0 | — |
case-17 | fail→pass | 14,531 | 5,088 | -65% | 1 | 1 | 0% | 1,988 | 4,470 | +125% | 0 | 0 | — |
case-18 | fail→fail | 14,497 | 5,616 | -61% | 1 | 1 | 0% | 2,170 | 4,479 | +106% | 0 | 0 | — |
case-19 | fail→pass | 9,942 | 4,817 | -52% | 1 | 1 | 0% | 1,738 | 4,634 | +167% | 0 | 0 | — |
case-20 | fail→pass | 10,058 | 3,194 | -68% | 1 | 1 | 0% | 1,650 | 4,143 | +151% | 0 | 0 | — |
case-21 | fail→pass | 6,096 | 4,516 | -26% | 1 | 1 | 0% | 869 | 4,272 | +392% | 0 | 0 | — |
case-22 | pass→pass | 9,802 | 2,150 | -78% | 1 | 1 | 0% | 1,492 | 3,828 | +157% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 12 counted toward the lift figure. The other 10 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +14 percentage points is the difference between those two pass rates over the 12 comparable cases. 5 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.