Skip to content
Back to skills

Memory Factcheck

BSecurity

Fact-check an agent's persistent memory against ground truth — verify each memory's load-bearing claims against code, database, issue tracker and filesystem, correct the stale ones, and report dead ones as archive candidates. Use when memory grows past ~30 files, right after a big stack/infra change (library swap, version upgrade, server migration, schema drop), when two memories seem to contradict each other, or on a "clean up / audit my memory" request.

  • 35 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 5, 2026
ai-agentsgojavashellgitapidatabase

Works with

  • cli
  • api

Security analysis

B85/100
  • highCreates or modifies cron jobs for persistent execution

Pro shows the line behind each finding and how to fix it

Scanned September 5, 2026

npx -y skills add LeeYudok/agents-scaffold --skill memory-factcheck --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Memory Factcheck?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Memory Factcheck
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/leeyudok-memory-factcheck-agents-scaffold/badge)](https://www.skillsdirectory.com/skills/leeyudok-memory-factcheck-agents-scaffold)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: memory-factcheck
description: Fact-check an agent's persistent memory against ground truth — verify each memory's load-bearing claims against code, database, issue tracker and filesystem, correct the stale ones, and report dead ones as archive candidates. Use when memory grows past ~30 files, right after a big stack/infra change (library swap, version upgrade, server migration, schema drop), when two memories seem to contradict each other, or on a "clean up / audit my memory" request.
---

# Memory Fact-Check

> Origin: the skill of the same name in [leeyudok/doksam-skills](https://github.com/leeyudok/doksam-skills).
> **Deliberately forked** for template bundling — not auto-synced; good improvements are cherry-picked manually.

Memory decays. A fact that was true when written becomes false as code, schema and
infrastructure move on. **Stale memory is worse than no memory** — an agent reads it and
confidently does the wrong thing.

This is **not** a structural hygiene pass. Orphan files, duplicate entries, index bloat,
broken internal links — those checks compare memory files *to each other*. This skill
compares each memory **to the world it describes**: the code, the database, the issue
tracker, the filesystem. A memory can be perfectly well-formed, correctly indexed, recently
touched — and completely false.

Automatic deletion is forbidden. Only a source-of-truth comparison can tell what is dead,
and losing an incident lesson is expensive. Hence semi-automatic: **correct freely, archive
only with approval, never delete.**

## 1. Locate and inventory

Find the memory set. Common locations, in order:

- a path declared by the project's agent instructions (`AGENTS.md` / `CLAUDE.md` — e.g.
  "memory SSOT is `.claude/memory/`"). A declared path wins over every default.
- `.claude/memory/` in the repo (team-shared, committed)
- the host's per-project memory dir (e.g. `~/.claude/projects/<slug>/memory/`)

Collect for each file: frontmatter (`name`/`description`/`type`), and last modification date
(`git log -1 --format=%cs -- <file>` for committed memory, `stat` otherwise). Note the index
file (`MEMORY.md`) if one exists — it is audited too, but it is an index, not a memory.

**Personal files are out of scope** — anything the project marks personal (`user_*.md` or
equivalent) belongs to its owner. Leave untouched.

**Reading many files**: dumping 50 memories into context at once blows the tool output cap
and wastes the budget. Concatenate to one scratch file with `########## <filename>` headers,
then page through it. Do not skim — a stale claim is usually one clause inside an otherwise
correct paragraph.

## 2. Extract load-bearing claims

Per file, pick the **1–3 claims that change what an agent would do**. Ignore prose, rationale
and background; a memory is only as stale as its actionable assertions.

Load-bearing looks like: "X lives at path P" · "table T has N rows" · "issue #N is still open"
· "feature F does not exist yet" · "library L is not installed" · "the fix for this is still
pending" · "run command C to verify".

## 3. Verify against the source of truth

Work **cheapest-and-highest-yield first**. In practice the ranking below holds: issue status
is one API call and catches the largest share of stale claims, because memories are written
mid-work and the work then finishes without anyone going back to edit the memory.

| Order | Claim type | How to verify |
| --- | --- | --- |
| 1 | **Issue/PR state** ("#N open", "waiting on #N", "decision pending") | forge CLI/API — `gh issue view N --json state` / `glab api projects/<enc>/issues/N`. Batch them in one loop |
| 2 | **Path/URL** (script locations, deploy paths, endpoints) | `ls`, `test -f`, `curl -s -o /dev/null -w '%{http_code}'` |
| 3 | **Code** (file/class/config exists, behaves a certain way) | `grep`/`Read` the current tree — *the code is the SoT, not the memory* |
| 4 | **Data/schema** (tables, columns, row counts) | read-only queries via the project's DB tool. Prefer catalog estimates first (`pg_class.reltuples`, `information_schema.columns`), exact `count(*)` only when the estimate is the disputed claim |
| 5 | **Runtime/host** (cron jobs, services, logs) | `ssh <host> 'ls …; crontab -l; tail <log>'` — a job's last log line dates the claim precisely |

Parallelize independent verifications. If a source is unreachable, say so explicitly in the
report — never silently downgrade "couldn't check" to "checked".

## 4. Classify

- **fresh** — every claim holds. Don't touch it.
- **stale** — some claim is outdated (moved path, changed number, closed issue, implemented
  gap). → **Correct the body now, with the measured value and the date.** Corrections are
  within autonomous scope; they are additive truth, not deletion.
- **dead** — the core premise is gone (library removed, feature retired, wholly superseded).
  → Mark as an archive **candidate** only.

## 5. Stale patterns worth hunting

Beyond "the number changed", these recur and are easy to miss:

- **Fixed-gap drift** — the memory documents a missing capability ("there is no startup
  reconcile", "no rate limiting yet") and it has since been built. This is the most dangerous
  class: the agent re-implements or re-reports work that already shipped. Check the linked
  issue *and* grep for the symbol.
- **Cross-memory contradiction** — two memories disagree (one says a script is the way to do
  X, another says that script was retired). At least one is stale by definition. Compare
  claims across files, not only file-by-file.
- **Scale drift** — "table T has ~8M rows" written months ago now off by 25%. Harmless as
  trivia, harmful when the memory derives advice from it (batch sizes, timeout budgets,
  "this query takes 19s").
- **Recipe rot** — the memory stores a command/query as a verified recipe, and the recipe no
  longer works at today's data volume or API version. **Re-run stored recipes**; a recipe you
  did not execute is unverified.
- **Progress-state drift** — a long-running job/backfill memory whose "current status" section
  is weeks behind, sometimes with two internally contradictory status sections stacked up.
  Date each section, keep the newest, mark superseded ones.
- **Identity mismatch** — `name`/`description` says one thing, the body says the opposite
  (e.g. a file named `*-via-toolX` whose body records that toolX was abandoned). Recall
  matches on description, so the file gets loaded for the wrong reason, or missed entirely.

## 6. Report, then apply

Report as a table before changing anything — file · class · one-line evidence · action:

| File | Class | Evidence | Action |
| --- | --- | --- | --- |
| `reference_x.md` | stale | script moved `scripts/` → `data/` | path corrected |
| `project_y.md` | stale | claims "#302 reconcile missing"; `JobRunHistoryReconciler` exists, #302 closed | rewritten as done |
| `project_z.md` | dead candidate | feature from #N removed in #M | awaiting approval |

Then:

1. **Apply corrections** to stale bodies — measured value + date, keeping the original
   observation where it still carries a lesson ("was 8M as of <date>, 9.9M as of <today>").
2. **Archive dead candidates only after explicit user approval**: `git mv` into
   `<memory>/archive/` and add `archived: <date> <reason>` to the frontmatter. Never `rm`.
3. **Sync the index** — reflect corrections, drop archived entries from `MEMORY.md`.
4. **Commit through the project's normal workflow** (issue → branch/worktree → PR/MR). Memory
   is team-shared content; it does not get direct-to-main commits.

## Judgment rules — stay conservative

- **Unverifiable ⇒ fresh.** If the source of truth is unreachable, leave the memory alone and
  say the check was skipped. Unknown is not dead.
- **Incident lessons stay fresh even when the code moves.** A memory recording *why something
  broke* exists to prevent recurrence, not to snapshot a call site. Correct its stale path
  references; do not retire the lesson because the file was renamed.
- **Report the drift, don't invent the cause.** If a count inverted or a number moved
  inexplicably, record the measurement and flag it as unexplained. A plausible story written
  into memory becomes tomorrow's false fact.
- **Prefer merge over create.** Two memories on one topic → propose merging into the existing
  one.
- **Non-obvious facts discovered by the audit itself become new memories** — the audit is
  itself a source of ground truth.

## Field notes

- Fresh timestamps prove nothing. A file committed this week can carry a claim that was
  already false when written; a file untouched for months can be perfectly true. Verify
  claims, never sort by date and trim the tail.
- `count(*)` on a large table may exceed the DB tool's statement timeout — that failure *is*
  a finding when the memory claims the query is fast.
- Quote globs when shelling out from zsh (`grep --include="*.java"`), or the shell eats them
  and the check silently returns nothing — a false "fresh".
- An issue being closed does not by itself prove the described work shipped. For fixed-gap
  claims, confirm with a grep for the symbol as well.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.