# Memory Gardener

> Run a periodic hygiene ceremony over whatever persistent memory the agent can reach — vector memory stores, knowledge graphs, diaries. Dedup near-duplicates, decay stale items, split bloated ones, invalidate superseded facts, reconnect orphans, write an interval summary, and report. Use when asked to garden/tend/clean agent memory, run memory hygiene, or as a scheduled unattended task. Storage-agnostic — discovers capabilities from the tool surface, never assumes specific tool names.

- Skill: `thorinside/memory-gardener` (Agent Skill, multi-file: 7 files)
- Install (CLI): `npx skillmds@latest add thorinside/memory-gardener`
- Raw SKILL.md: https://api.skillmd.com/api/skills/thorinside/memory-gardener/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: thorinside (https://skillmd.com/u/thorinside)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/thorinside/memory-gardener

---


# Memory gardener

Persistent agent memory rots by default. Duplicates accumulate, facts go stale
without anyone noticing, single items bloat into junk drawers, graph nodes orphan,
and nothing ever gets summarized. Most memory systems ship the *primitives* for
hygiene (duplicate checks, invalidation, re-linking) but nothing *runs* them.

This skill is the gardener: a budgeted, idempotent ceremony you run periodically
over whatever memory stores the current environment exposes. It is deliberately
**storage-agnostic** — you discover capabilities from the tool surface at the start
of every run, tend what exists, skip what doesn't, and report everything. The
judgment prompts for the risky calls — dedup and bloat-splitting — ship in
[`prompts/`](prompts/README.md), carried from the battle-tested nightly ceremony
in EI by Jeremy Scherer (MIT).

## Ground rules — the safety contract

These override anything else in this document.

1. **Mutation mode.** The default is **tend-and-propose**: deletions and uncertain
   merges are *proposed* in the report, not executed. If the operator's run
   instructions explicitly grant **autonomous mode**, clear-case deletions and
   merges execute directly — this matches upstream EI's own ceremony, and the
   prompts' conservatism (merge only at 85%+ core meaning; *default to keeping
   both*) is the load-bearing safeguard. In either mode: prefer invalidate /
   archive / expire over delete where the store distinguishes them, and anything
   the prompts mark *uncertain* is never executed — propose it (tend-and-propose)
   or defer it with a note (autonomous).
2. **Budget every run.** Defaults: **25 mutations** and **50 judgment comparisons**
   per run. When the budget is spent, stop mid-phase, record the resume point in
   the report, and let the next run continue. Working oldest-or-dirtiest-first
   makes this naturally resumable.
3. **Idempotent.** Re-running immediately after a clean run should find almost
   nothing to do. If a second pass keeps finding the same "problem", the fix is
   wrong — stop and propose instead.
4. **Snapshot first.** If the environment offers a backup capability: in
   tend-and-propose mode, back up before the first run; in autonomous mode, back
   up before **every** run — it is the undo button for unattended pruning.
5. **Missing capability ⇒ skip the phase and log it.** Never approximate a missing
   primitive with a destructive workaround (e.g., delete-and-recreate to fake an
   update).

## Phase 0 — survey

Do not assume tool names; environments differ. Scan the available tools and map
them to capabilities:

| Capability | Look for tools that… |
|---|---|
| Memory search | search or list stored memories by similarity, text, or tag |
| Memory mutation | create / update memories (delete may exist; you won't call it) |
| Duplicate check | given text, return near-duplicates — or emulate via similarity search with scores |
| Knowledge graph | add / query facts; retire/invalidate links or facts; timeline or history views |
| Graph structure | list containers (collections, rooms, drawers), links/edges, traverse, reconnect |
| Diary / journal | append a dated free-text entry |
| Stats | item counts, graph stats — take these before and after |
| Backup | snapshot the store |
| Artifact / report storage | persist a text document |

Note which **stores** exist. Many environments run a two-tier stack — a fast
vector store *and* a structured knowledge graph; tend both. Take baseline stats.
Establish "since when": look for the previous gardening report or diary entry and
garden the interval since it; on a first run, limit yourself to the most recent
~200 items rather than all of history.

When the environment exposes mapped tool discovery, discover and inspect the
actual mutation surface before deciding a capability is missing. Search for the
intent ("invalidate knowledge relationship", "delete knowledge link",
"archive memory", etc.), inspect the returned schema, and call the mapped tool
directly. Do not require a tool to be literally named `invalidate`; many systems
expose graph invalidation as a provider-neutral delete/retire operation on a link
resource. Treat backing-provider tool names as implementation details unless the
environment explicitly exposes only those raw tools.

## Phase 1 — dedup

**First, close the loop**: if prior reports carry outstanding *Proposed* entries
that are cleared to run — approved by the human in tend-and-propose mode, or
simply outstanding in autonomous mode — execute those merges now using
[`prompts/dedup-confirmed.md`](prompts/dedup-confirmed.md), then mark those
reports executed so they never run twice.

Then, for items added or updated since the last run, find near-duplicates using
the duplicate-check capability (or similarity search). Treat **≥ 0.90 similarity**
as candidates, and judge them with the shipped ceremony prompts:

- A cluster of 2+ candidates → [`prompts/dedup-cluster.md`](prompts/dedup-cluster.md) —
  the curator: merges at 85%+ core-meaning overlap, prime directive **lose NO data**.
- Exactly two records, newcomer vs established → [`prompts/dedup-validate.md`](prompts/dedup-validate.md) —
  the gate: **default to keeping both** ("a false merge destroys information
  permanently; a false keep is harmless").

Apply each prompt's `update` output. Its `remove` output follows the mutation
mode: tend-and-propose routes it to the report's *Proposed* queue; autonomous
mode executes it (prefer archive/invalidate over hard delete) and logs each
removal in the report (see [`prompts/README.md`](prompts/README.md)). Merge-rule
cheatsheet: HIGHER for strength/confidence/exposure-like fields, AVERAGE for
sentiment-like, union of unique description details.

## Phase 2 — decay

Stale means not mentioned, retrieved, or updated in **30 days** (adjust to the
store's tempo). For stale items: lower importance/priority/exposure by one notch,
never below the floor. If the store supports expiry, set an expiry on trivia-grade
items instead of proposing deletion. Never decay items the store marks pinned,
critical, or protected.

## Phase 3 — split bloat

An item whose text exceeds **~750 characters** is usually several facts wearing
one id. Worst offenders first, **max 5 splits per run**:

1. Extract the distinct facts into new focused items.
2. Link children to the parent using whatever the store has — edges, tunnels,
   tags, or a `parent:` reference in metadata.
3. Rewrite the parent down to its actual core.

Run it as EI does, in two steps with the shipped prompts:
[`prompts/bloat-scan.md`](prompts/bloat-scan.md) extracts the buried subjects as
search phrases (the scan mutates nothing; person-like records get the coffee-shop
test — *would this detail still matter at a chance meeting in six months?*);
search your store for each phrase; then
[`prompts/bloat-split.md`](prompts/bloat-split.md) slims the original and
redistributes the content into matching or new records.

This is the most expensive phase; it is always acceptable to do fewer, better
splits.

## Phase 4 — invalidate the superseded

Knowledge-graph stores only. Query facts touched since the last run and look for
contradictions: same subject and predicate, different object, different times. The
older fact is superseded — **invalidate** it (which preserves the timeline), never
hard-delete it.

Stay storage-agnostic, but be precise about the operation class:

1. Prefer a native invalidate/archive/retire operation when the inspected schema
   offers one.
2. If the schema models invalidation as deleting a link or fact resource, that is
   still the correct invalidation capability. Use it when the tool description or
   provider behavior says it retires the graph assertion rather than destroying
   all history.
3. If the stale assertion is identified as subject + predicate + object but the
   mutation schema requires an opaque link/fact ID, first use the graph query,
   neighbor, path, timeline, or search capabilities to recover the exact ID. Do
   not claim invalidation is unavailable just because you started from the triple.
4. In tend-and-propose mode, propose the invalidation unless the operator has
   already approved that stale assertion. In autonomous mode, execute only
   clear-cut supersession and log the exact retired assertion.

If the store truly has no invalidation, add the correction as a new fact and list
the stale one under *Proposed*.

## Phase 5 — reconnect orphans

Graph stores: find nodes with no links, or items filed in an obviously wrong
container. Re-file them, add the one missing link that's unambiguous, and use the
store's reconnect helper if it has one. **Max 10 reconnects per run.** If the right
home for an item isn't obvious, leave it and note it — a wrong link is worse than
a missing one.

## Phase 6 — summarize the interval

Write one diary/journal entry: 2–6 sentences on notable knowledge added since the
last run, plus one line on garden health (e.g., "dup pressure rising in topic X").
No diary capability → write a memory item tagged `garden-summary` instead.

## Phase 7 — report (never skipped)

Even when every other phase was skipped, produce the report. Persist it via the
artifact/report capability if one exists; always also emit it as your final output.

```
# Gardening report — <ISO timestamp>
Stores tended: <store: capability summary, per store>
Stats: <counts before → after, per store>
Actions: merges N · decays N · splits N · invalidations N · reconnects N
Destructive ops (mode: tend-and-propose | autonomous):
  - PROPOSED DELETE <id> "<short preview>" — duplicate of <id>   (tend-and-propose)
  - EXECUTED MERGE <id> ← <id> — "<short preview>"               (autonomous)
Deferred (uncertain): <pairs the prompts declined to merge, one line each>
Skipped: <phase> — <missing capability>
Budget: <used>/<max> mutations · resume point: <store / cursor or "clean">
```

In tend-and-propose mode the destructive-ops section is the human review queue —
**the gardener tends; the human prunes.** In autonomous mode the gardener prunes
too, and the report is the audit log: every removal traceable, every uncertainty
deferred rather than forced.

### Substrate semantic completion

When this skill runs as a Substrate runner or workflow job, the gardening report
remains the human-readable output, but it is not the machine success signal.
After the report and any workflow-specific status fields, make the final
non-empty assistant line exactly one `SUBSTRATE_OUTCOME_V1=` declaration.

Declare `outcome: "succeeded"` only when the ceremony completed within its
budget, every unavailable phase was honestly recorded as skipped, every executed
mutation was audited, and the report was persisted and read back when an artifact
capability exists. A required backup failure in autonomous mode, an unreported
partial mutation, or failure to persist the required report is `outcome:
"failed"`; blocked or needs-human work is also failed. Memory and knowledge
mutations are not Git changes, so use `changes.status: "notApplicable"`.

Example success line (replace placeholders with real evidence):

```text
SUBSTRATE_OUTCOME_V1={"version":1,"outcome":"succeeded","summary":"Completed memory gardening and persisted report <artifact-id>","evidence":{"changes":{"status":"notApplicable","reason":"Memory-store mutations are not Git changes"},"verification":{"status":"passed","commands":["read back report artifact <artifact-id>"]}}}
```

Emit no second declaration and nothing after it. Outside a Substrate job, do not
add this platform-specific line.

## Running periodically

Designed for daily-ish unattended runs by any host that can invoke an agent with
this skill — a scheduler that launches an agent job, an agent harness's own cron,
or a plain crontab entry. Daily suits active stores; weekly suits quiet ones.
Every run is budget-capped, so an aggressive cadence is safe — the worst case is a
report that says "clean".

## Composes with memory-collector

[memory-collector](../memory-collector/SKILL.md) is the other half of the pair:
it harvests coding-agent session transcripts (Claude Code, Codex, OpenCode,
Cursor, Pi) and plants topics, people, events, and quotes into the same stores
this skill tends. Collection deliberately tolerates near-duplicates and
overgrowth — the validate gate, dedup curator, and bloat-split exist for exactly
that inflow. The collector plants; the gardener prunes. Good rhythm: collector
daily, gardener nightly after it.

## Provenance & credit

The ceremony structure (dedup → decay → rewrite → reflect), the working
thresholds (0.90 duplicate candidates, 85%+ core-meaning merges, ~750-character
bloat line), and the judgment prompts in [`prompts/`](prompts/README.md) come from
the nightly ceremony in [Flare576/ei](https://github.com/Flare576/ei) by
**Jeremy Scherer** (MIT, © 2026 Jeremy Scherer). Jeremy wrote and tuned those
prompts against real personal memory; this skill only generalizes the plumbing
around them to be storage-agnostic. If the gardener earns its keep, the upstream
project — a local-first AI memory layer with a persona companion system on top —
deserves a look.

