Memory Bank Defrag
Bring a project's memory bank back in sync with reality and remove cruft. The
memory bank is three things:
.assistant/ in the repo — the durable, version-controlled knowledge
base (project context, requirements, decisions/ADRs, scope, open questions,
stakeholders, glossary). Structure varies per project; operate on whatever
files exist, don't assume a fixed layout.
- Auto-memory — the cross-session memory at
~/.claude/projects/<slugified-cwd>/memory/ (an MEMORY.md index plus one
file per fact). Local only — absent on cloud/headless runs; skip silently
if the directory doesn't exist.
- Repo current-state docs — files that assert what is true now and drift
the same way:
README.md, AGENTS.md/CLAUDE.md, design specs, proposals.
Update these when a recent commit makes them stale. Do not touch raw
source records that capture what happened — meeting transcripts, chat
dumps, extracted board content — those are evidence; keep them verbatim
(translate only by adding a summary alongside, never by overwriting).
The job is defragmentation, not rewriting: same facts, fewer patches.
Amendment-on-amendment trails collapse into one current-state statement;
resolved questions get closed; stale facts get corrected against the actual
code/docs; new durable facts from recent commits get captured.
Non-negotiables
- Accuracy > speed. Memory is point-in-time and may be wrong. Before you
assert a fact as current, verify it against the live repo (file exists,
symbol not renamed, deadline not passed, stack matches code). If a memory
claim contradicts current code, trust the code and fix the memory.
- Fold, don't lose. When collapsing patches, preserve every substantive
fact. A reversal that carries decision-relevant context (why we moved off X)
stays as a short supersession note — that's history, not a patch.
- Only substantive facts — no process noise. The memory bank records the
project, not your maintenance of it. Do not add "actualized on ",
"switched to English", "synced the docs" timeline/history entries, and do not
repeat the same standing rule (e.g. "working language is English") across many
files — state it once in its authoritative home and link to it. When a sync
you just did makes an old "needs updating" caveat stale, delete the caveat;
don't leave a trail.
- Don't invent. No facts, paths, dates, or decisions that aren't in the
commits, the code, or the existing memory. Unknown stays unknown.
- Review before commit. Make the edits, then show the diff and a summary. Do
not auto-commit unless the user asks (interactive) or the unattended rules
below apply. Auto-memory files live outside the repo and are written directly
(not committed).
- Convert relative dates to absolute (use the current date from context).
Workflow
1. Locate the baseline (last defrag)
Find the commit of the previous defrag so you only process what changed since.
# Preferred: explicit trailer this skill writes on its own commits
git log -1 --grep='^Memory-Defrag:' --format='%H %cI %s'
# Fallback: the conventional subject line
git log -1 --grep='actualize memory bank' --format='%H %cI %s'
- If the user passed a
baseline-ref argument, use it instead.
- If neither is found (first ever run), say so and propose a sensible range
(e.g. last 20 commits, or since the memory bank dir was created:
git log --diff-filter=A -1 --format=%H -- .assistant). Confirm with the user
before processing a large history (interactive) or just use it (unattended).
2. Gather what changed since baseline — and bail early if nothing did
git log <baseline>..HEAD --format='%h %cI %s' # commit subjects
git diff <baseline>..HEAD --stat -- . ':(exclude).assistant' # what code/docs moved
No-op guard: if <baseline>..HEAD is empty (no commits since the last
defrag), there is nothing to do. Stop here — no edits, no commit, no PR. Report
"nothing changed since the last defrag" and exit. (This is what keeps a daily
schedule cheap and quiet.)
Otherwise read the actual diffs (or the changed files) for anything that
changes a fact the memory bank records: stack/dependency changes, renamed or
deleted files, new integrations, resolved decisions, changed config, new
constraints. Ignore pure formatting / lockfile churn — if the only changes are
cosmetic, treat it as a no-op too.
3. Read the current memory bank
ls and read every file under .assistant/.
- If auto-memory exists, read its
MEMORY.md and the referenced files. (On
cloud/headless runs this directory won't exist — skip it.)
4. Detect divergences
Build a list, each tagged with evidence (commit SHA, file:line):
- Stale facts — memory says X, current code/docs say Y.
- Patches to fold — multiple "Amendment " blocks on one decision,
superseded-but-still-described content, the same fact stated three ways across
files. Collapse to current state + one supersession note where a reversal
happened.
- Resolvable questions — open questions the recent commits actually answered.
- Dangling references — memory cites a file/symbol/flag that no longer exists.
- Uncaptured facts — durable decisions/constraints in recent commits not yet
in the memory bank.
If divergences are large or ambiguous in an interactive run, surface the list
and confirm direction before mass-editing. In an unattended run, proceed and let
the PR be the review surface.
5. Apply the edits
Edit .assistant/, the repo current-state docs (README and the like), and
auto-memory to current state:
- Rewrite patched sections as clean current-state; keep a one-line
"superseded by … because …" only where the reversal matters.
- Close resolved questions in place (mark resolved + the answer + the commit),
matching the file's existing convention.
- Correct stale facts; fix or drop dangling references.
- Capture new durable facts in the right file.
- Auto-memory (skip if absent): update the relevant fact file (don't duplicate —
edit the existing one), refresh its
MEMORY.md index line, delete memories
proven wrong. Link related memories with [[name]].
6. Verify
grep the memory bank for leftovers of anything you retired (old vendor,
old path, old decision) — catch half-updated mentions.
- Re-check internal consistency: a table row and the prose around it agree; a
closed question isn't still referenced as open elsewhere.
7. Report + commit
Summary: files touched, patches folded, questions closed, facts corrected,
anything you deliberately left alone.
- Interactive run: show the diff (
git diff); commit only when the user asks.
- Unattended run: see below.
Commit subject + trailer (the trailer is what the next run's baseline detection
finds — always include it):
docs(assistant): actualize memory bank — <one-line theme>
<what was folded/closed/corrected>
Memory-Defrag: <YYYY-MM-DD>
Unattended / scheduled runs (cloud routine)
When there is no human to review mid-run (a scheduled routine, CI, headless):
- Open a pull request, never push to
main. Create a branch
(memory-defrag/<YYYY-MM-DD>), commit with the trailer above, push it, and
open a PR whose body is the step-7 summary. A human merges. This keeps the
baseline-trailer in history once merged, and keeps the knowledge base reviewed.
- If the no-op guard fired (step 2), do nothing — no branch, no PR. A quiet
run is the correct outcome on a day with no relevant commits.
- Don't loop on yourself. Your own defrag commit carries the
Memory-Defrag:
trailer and becomes the next baseline, so the following run sees no new
substantive commits and no-ops. Never treat a prior defrag commit as a change
to process.
- Auto-memory is local-only and absent here — operate on the repo alone.
- Announce the PR in Mattermost (next section) once it's open, so a human
knows there's a knowledge-base diff to review. Skip entirely on a no-op run.
Notify Mattermost (after the PR is opened)
When a PR was opened, post a short report to Mattermost via incoming webhook so a
reviewer can open the PR and leave comments there. Only after a successful PR —
never on a no-op run (a quiet day stays quiet).
- Webhook: read it from env
MM_DEFRAG_WEBHOOK — never hardcode a hook URL in
the skill or the repo (it's a write credential). Skip the whole notify step if
the var is unset.
- Channel: whichever channel the run instructions name, else env
MM_DEFRAG_CHANNEL
(each project targets its own channel). Use the channel name/slug (the URL
handle), not the 26-char channel ID — the webhook override rejects IDs. If neither
is set, omit channel and the post lands in the webhook's default.
Language — write the message in the project's working language (the one
its CLAUDE.md/AGENTS.md mandates). Don't default to English on a project that
works in another language.
Message content — write it for a human skimming chat, not a changelog dump.
Keep it short and scannable:
- Title — the one-line run theme; links to the PR (
title_link).
- Folded / corrected — 2–4 grouped bullets of what you tidied (patches
folded, facts corrected, questions closed). Summarize, don't list every file.
- Heads-up — gaps you noticed that a human or the client should check or
confirm. Include only items that genuinely need someone — if there are none
this run, say so in one line rather than padding.
- Call to action (always last) — make clear you don't read replies in the
channel; you act on PR review comments and edit reactively, so they should
comment inline on the PR and keep the thread there.
Formatting — use a Mattermost attachment (colored bar + clickable PR title)
with markdown in the body. Build the JSON with jq so the summary text can't
break the payload — never hand-concatenate summary text into JSON:
PR_URL="<from gh pr create>"
TITLE="Memory bank defrag — <one-line theme>"
BODY=$(cat <<'EOF'
Tidied up the `.assistant/` knowledge base after <N> commits — docs only.
**Folded / corrected**
- <grouped bullet>
- <grouped bullet>
**Heads-up**
- <gap to check / confirm with client — or: "nothing needs input this run">
:point_right: **I don't read replies in this channel.** I act on the PR review
comments and make edits reactively — drop your notes inline on the PR and let's
keep the thread there.
EOF
)
curl -fsS -X POST -H 'Content-Type: application/json' \
--data "$(jq -n \
--arg ch "${MM_DEFRAG_CHANNEL:-}" --arg title "$TITLE" \
--arg link "$PR_URL" --arg body "$BODY" \
'{username:"Memory Bank",
attachments:[{color:"#2eb886", title:$title, title_link:$link, text:$body,
fallback:("Memory bank defrag — review the PR: "+$link)}]}
+ (if $ch=="" then {} else {channel:$ch} end)')" \
"$MM_DEFRAG_WEBHOOK"
The webhook post is best-effort: if it fails (non-2xx, network), log it in the
run summary but don't fail the run — the PR is the source of truth, the ping is
convenience.
Notes
- This skill changes documentation/knowledge only — it never edits product code.
- If the project's
CLAUDE.md/AGENTS.md defines a memory-update protocol or a
working-language rule, follow it.
- Keep the memory bank's existing tone and structure; match it, don't impose a
new one.
1---2name: memory-bank-defrag3description: Defragment and re-actualize a project memory bank (the .assistant/ knowledge base + the project's auto-memory). Reads everything that changed in the repo since the last defrag, finds where the memory bank has gone stale or accumulated amendment-on-amendment "patches", folds the patches into clean current-state docs, closes resolved questions, and updates the auto-memory — then shows a diff for review. Use when the user says "defrag the memory bank", "привести memory bank в порядок", "дефрагментация памяти", "актуализируй .assistant", "причеши базу знаний", or runs /memory-bank-defrag.4---56# Memory Bank Defrag78Bring a project's memory bank back in sync with reality and remove cruft. The9memory bank is three things:10111. **`.assistant/`** in the repo — the durable, version-controlled knowledge12 base (project context, requirements, decisions/ADRs, scope, open questions,13 stakeholders, glossary). Structure varies per project; operate on whatever14 files exist, don't assume a fixed layout.152. **Auto-memory** — the cross-session memory at16 `~/.claude/projects/<slugified-cwd>/memory/` (an `MEMORY.md` index plus one17 file per fact). Local only — **absent on cloud/headless runs**; skip silently18 if the directory doesn't exist.193. **Repo current-state docs** — files that assert *what is true now* and drift20 the same way: `README.md`, `AGENTS.md`/`CLAUDE.md`, design specs, proposals.21 Update these when a recent commit makes them stale. Do **not** touch raw22 source records that capture *what happened* — meeting transcripts, chat23 dumps, extracted board content — those are evidence; keep them verbatim24 (translate only by adding a summary alongside, never by overwriting).2526The job is **defragmentation**, not rewriting: same facts, fewer patches.27Amendment-on-amendment trails collapse into one current-state statement;28resolved questions get closed; stale facts get corrected against the actual29code/docs; new durable facts from recent commits get captured.3031## Non-negotiables3233- **Accuracy > speed.** Memory is point-in-time and may be wrong. Before you34 assert a fact as current, **verify it against the live repo** (file exists,35 symbol not renamed, deadline not passed, stack matches code). If a memory36 claim contradicts current code, trust the code and fix the memory.37- **Fold, don't lose.** When collapsing patches, preserve every substantive38 fact. A reversal that carries decision-relevant context (why we moved off X)39 stays as a short supersession note — that's history, not a patch.40- **Only substantive facts — no process noise.** The memory bank records the41 *project*, not your maintenance of it. Do **not** add "actualized on <date>",42 "switched to English", "synced the docs" timeline/history entries, and do not43 repeat the same standing rule (e.g. "working language is English") across many44 files — state it once in its authoritative home and link to it. When a sync45 you just did makes an old "needs updating" caveat stale, delete the caveat;46 don't leave a trail.47- **Don't invent.** No facts, paths, dates, or decisions that aren't in the48 commits, the code, or the existing memory. Unknown stays unknown.49- **Review before commit.** Make the edits, then show the diff and a summary. Do50 **not** auto-commit unless the user asks (interactive) or the unattended rules51 below apply. Auto-memory files live outside the repo and are written directly52 (not committed).53- **Convert relative dates to absolute** (use the current date from context).5455## Workflow5657### 1. Locate the baseline (last defrag)5859Find the commit of the previous defrag so you only process what changed since.6061```bash62# Preferred: explicit trailer this skill writes on its own commits63git log -1 --grep='^Memory-Defrag:' --format='%H %cI %s'64# Fallback: the conventional subject line65git log -1 --grep='actualize memory bank' --format='%H %cI %s'66```6768- If the user passed a `baseline-ref` argument, use it instead.69- If neither is found (first ever run), say so and propose a sensible range70 (e.g. last 20 commits, or since the memory bank dir was created:71 `git log --diff-filter=A -1 --format=%H -- .assistant`). Confirm with the user72 before processing a large history (interactive) or just use it (unattended).7374### 2. Gather what changed since baseline — and bail early if nothing did7576```bash77git log <baseline>..HEAD --format='%h %cI %s' # commit subjects78git diff <baseline>..HEAD --stat -- . ':(exclude).assistant' # what code/docs moved79```8081**No-op guard:** if `<baseline>..HEAD` is empty (no commits since the last82defrag), there is nothing to do. Stop here — no edits, no commit, no PR. Report83"nothing changed since the last defrag" and exit. (This is what keeps a daily84schedule cheap and quiet.)8586Otherwise read the actual diffs (or the changed files) for anything that87changes a fact the memory bank records: stack/dependency changes, renamed or88deleted files, new integrations, resolved decisions, changed config, new89constraints. Ignore pure formatting / lockfile churn — if the only changes are90cosmetic, treat it as a no-op too.9192### 3. Read the current memory bank9394- `ls` and read every file under `.assistant/`.95- If auto-memory exists, read its `MEMORY.md` and the referenced files. (On96 cloud/headless runs this directory won't exist — skip it.)9798### 4. Detect divergences99100Build a list, each tagged with evidence (commit SHA, file:line):101102- **Stale facts** — memory says X, current code/docs say Y.103- **Patches to fold** — multiple "Amendment <date>" blocks on one decision,104 superseded-but-still-described content, the same fact stated three ways across105 files. Collapse to current state + one supersession note where a reversal106 happened.107- **Resolvable questions** — open questions the recent commits actually answered.108- **Dangling references** — memory cites a file/symbol/flag that no longer exists.109- **Uncaptured facts** — durable decisions/constraints in recent commits not yet110 in the memory bank.111112If divergences are large or ambiguous in an interactive run, surface the list113and confirm direction before mass-editing. In an unattended run, proceed and let114the PR be the review surface.115116### 5. Apply the edits117118Edit `.assistant/`, the repo current-state docs (README and the like), and119auto-memory to current state:120121- Rewrite patched sections as clean current-state; keep a one-line122 "superseded by … because …" only where the reversal matters.123- Close resolved questions in place (mark resolved + the answer + the commit),124 matching the file's existing convention.125- Correct stale facts; fix or drop dangling references.126- Capture new durable facts in the right file.127- Auto-memory (skip if absent): update the relevant fact file (don't duplicate —128 edit the existing one), refresh its `MEMORY.md` index line, delete memories129 proven wrong. Link related memories with `[[name]]`.130131### 6. Verify132133- `grep` the memory bank for leftovers of anything you retired (old vendor,134 old path, old decision) — catch half-updated mentions.135- Re-check internal consistency: a table row and the prose around it agree; a136 closed question isn't still referenced as open elsewhere.137138### 7. Report + commit139140Summary: files touched, patches folded, questions closed, facts corrected,141anything you deliberately left alone.142143- **Interactive run:** show the diff (`git diff`); commit only when the user asks.144- **Unattended run:** see below.145146Commit subject + trailer (the trailer is what the next run's baseline detection147finds — always include it):148149```150docs(assistant): actualize memory bank — <one-line theme>151152<what was folded/closed/corrected>153154Memory-Defrag: <YYYY-MM-DD>155```156157## Unattended / scheduled runs (cloud routine)158159When there is no human to review mid-run (a scheduled routine, CI, headless):160161- **Open a pull request, never push to `main`.** Create a branch162 (`memory-defrag/<YYYY-MM-DD>`), commit with the trailer above, push it, and163 open a PR whose body is the step-7 summary. A human merges. This keeps the164 baseline-trailer in history once merged, and keeps the knowledge base reviewed.165- **If the no-op guard fired (step 2), do nothing** — no branch, no PR. A quiet166 run is the correct outcome on a day with no relevant commits.167- **Don't loop on yourself.** Your own defrag commit carries the `Memory-Defrag:`168 trailer and becomes the next baseline, so the following run sees no new169 substantive commits and no-ops. Never treat a prior defrag commit as a change170 to process.171- Auto-memory is local-only and absent here — operate on the repo alone.172- **Announce the PR in Mattermost** (next section) once it's open, so a human173 knows there's a knowledge-base diff to review. Skip entirely on a no-op run.174175### Notify Mattermost (after the PR is opened)176177When a PR was opened, post a short report to Mattermost via incoming webhook so a178reviewer can open the PR and leave comments there. **Only after a successful PR —179never on a no-op run** (a quiet day stays quiet).180181- Webhook: read it from env `MM_DEFRAG_WEBHOOK` — never hardcode a hook URL in182 the skill or the repo (it's a write credential). Skip the whole notify step if183 the var is unset.184- Channel: whichever channel the run instructions name, else env `MM_DEFRAG_CHANNEL`185 (each project targets its own channel). Use the channel **name/slug** (the URL186 handle), not the 26-char channel ID — the webhook override rejects IDs. If neither187 is set, omit `channel` and the post lands in the webhook's default.188189**Language** — write the message in the **project's working language** (the one190its `CLAUDE.md`/`AGENTS.md` mandates). Don't default to English on a project that191works in another language.192193**Message content** — write it for a human skimming chat, not a changelog dump.194Keep it short and scannable:1951961. **Title** — the one-line run theme; links to the PR (`title_link`).1972. **Folded / corrected** — 2–4 grouped bullets of what you tidied (patches198 folded, facts corrected, questions closed). Summarize, don't list every file.1993. **Heads-up** — gaps you noticed that a human or the client should check or200 confirm. Include only items that genuinely need someone — if there are none201 this run, say so in one line rather than padding.2024. **Call to action** (always last) — make clear you don't read replies in the203 channel; you act on PR review comments and edit reactively, so they should204 comment inline on the PR and keep the thread there.205206**Formatting** — use a Mattermost attachment (colored bar + clickable PR title)207with markdown in the body. Build the JSON with `jq` so the summary text can't208break the payload — never hand-concatenate summary text into JSON:209210```bash211PR_URL="<from gh pr create>"212TITLE="Memory bank defrag — <one-line theme>"213BODY=$(cat <<'EOF'214Tidied up the `.assistant/` knowledge base after <N> commits — docs only.215216**Folded / corrected**217- <grouped bullet>218- <grouped bullet>219220**Heads-up**221- <gap to check / confirm with client — or: "nothing needs input this run">222223:point_right: **I don't read replies in this channel.** I act on the PR review224comments and make edits reactively — drop your notes inline on the PR and let's225keep the thread there.226EOF227)228229curl -fsS -X POST -H 'Content-Type: application/json' \230 --data "$(jq -n \231 --arg ch "${MM_DEFRAG_CHANNEL:-}" --arg title "$TITLE" \232 --arg link "$PR_URL" --arg body "$BODY" \233 '{username:"Memory Bank",234 attachments:[{color:"#2eb886", title:$title, title_link:$link, text:$body,235 fallback:("Memory bank defrag — review the PR: "+$link)}]}236 + (if $ch=="" then {} else {channel:$ch} end)')" \237 "$MM_DEFRAG_WEBHOOK"238```239240The webhook post is best-effort: if it fails (non-2xx, network), log it in the241run summary but don't fail the run — the PR is the source of truth, the ping is242convenience.243244## Notes245246- This skill changes documentation/knowledge only — it never edits product code.247- If the project's `CLAUDE.md`/`AGENTS.md` defines a memory-update protocol or a248 working-language rule, follow it.249- Keep the memory bank's existing tone and structure; match it, don't impose a250 new one.