examples/before-after.md
# refactor-memory — worked example: recalling a stale note safely
## Setup
A new session opens on `~/projects/storefront`. Boot injects:
> refactor-chain memory: last refactor-chain run: web lane, done; 2 parked scope-drift item(s)
> awaiting attention (verify against the live repo before relying on it).
`sessions.jsonl` (latest line):
```json
{"at":"2026-06-20T18:04:11Z","lastRun":{"lane":"web","outcome":"done","at":"2026-06-20T17:58:02Z"},"flagged":["scope: checkout modal rewrite parked — out of lane","scope: dead CSS in legacy/ parked"]}
```
## Before (what NOT to do — memory treated as truth)
> "Welcome back! Last time we finished the web-lane run and you still have two parked items:
> the checkout modal rewrite and the dead CSS in `legacy/`. Shall I start on the modal rewrite?"
Three faults: it asserts two-week-old notes as current facts, it proposes work from a *parked
scope-drift flag* (the flag meant "explicitly out of lane", not "to-do"), and it never looked at
the repo. If a teammate already deleted `legacy/`, the session opens with a false claim.
## After (memory verified against the live repo first)
1. **Verify the outcome.** Read `.refactor-chain/history.jsonl` — last line confirms
`{"lane":"web","outcome":"done"}`. ✓ memory matches the record.
2. **Verify parked item 1.** The checkout modal still exists (`src/checkout/Modal.jsx`, unchanged
since the note). Still parked, still real — and still out of lane unless the human widens scope.
3. **Verify parked item 2.** `legacy/` was deleted three days ago (`git log --oneline -- legacy/`
shows the removal). The note is stale.
What the session actually says:
> "Picking up where the chain left off here: the last web-lane run finished clean — confirmed in
> the run history, not just from memory. Of the two parked items, one is still real (the checkout
> modal rewrite — parked because it was out of scope, so it stays parked unless you want it), and
> one is already resolved: `legacy/` was deleted since, so the dead-CSS note is closed. Nothing
> is in flight; we can start fresh whenever you like."
## Why this is the pattern
- Every recalled claim got a named live check (history file, source path, git log).
- A stale note was **closed with evidence**, not silently repeated.
- A scope-drift flag was reported as parked — not promoted into work.
- The memory file itself was left untouched; next SessionEnd will capture the new reality.
references/method.md
# refactor-memory — full method: the per-project memory discipline
## Where memory lives
`<project>/.refactor-chain/memory/sessions.jsonl` — append-only, one JSON note per session,
written by the SessionEnd hook `~/.claude/skills/refactor-chain/scripts/memory-capture.mjs`.
Recall is done by the SessionStart hook `boot.mjs`, which reads only the **last** line and injects
a one-line plain-language summary into the new session's context.
Both hooks are deliberately boring and safe:
- **Dormant unless relevant.** No `state.json` and no `history.jsonl` → no capture, no banner.
- **Never fatal.** Both wrap everything in try/catch and exit 0; a broken memory file is skipped
silently rather than breaking session start or end.
## The note schema (what capture actually writes)
```json
{
"at": "ISO-8601",
"activeRun": { "lane": "web", "phase": "do-the-work", "step": "3/7", "health": "ok" },
"flagged": ["scope: sidebar rewrite parked — out of lane"],
"lastRun": { "lane": "web", "outcome": "done", "at": "ISO-8601" }
}
```
- `activeRun` — present only when a chain is mid-flight (from `state.json`).
- `flagged` — up to 3 scope-drift notes harvested from step notes.
- `lastRun` — present only when there is no active run (from the last `history.jsonl` line).
- If none of the three exist, **nothing is written** — an empty session leaves no residue.
## The persistence filter
Ask of any candidate fact: *will this matter to a future session, and is it safe to write down?*
| Persist | Never persist |
|---|---|
| durable decisions ("kept the legacy adapter — X depends on it") | conversation transcripts, chat excerpts |
| parked items / scope-drift flags | secrets, tokens, credentials, keys |
| run outcomes and active-run position | personal data of any kind |
| recurring environmental facts the chain tripped on | transient debugging chatter, one-off errors |
The hook enforces the shape; the skill enforces the judgment. If a note only makes sense with the
transcript open, it fails the filter.
## Recall: the live-truth rule
Memory answers "what did we think last time?" — never "what is true now?". Before acting on any
recalled fact:
1. **Paused run remembered** → read `.refactor-chain/state.json`. It is the authority on phase,
cursor, and step list. If it is gone or disagrees, the memory is stale; say so and follow state.
2. **Outcome remembered** → read the last `history.jsonl` line. Cite that, not the memory.
3. **Parked item remembered** → check the referenced code. It may have been fixed, deleted, or
rewritten since; a parked item that no longer exists is closed, not re-raised.
The boot banner itself says "(verify against the live repo before relying on it)" — that clause is
the contract. The repo now outranks the note then, every time. This is the same discipline the
`live-state-truth` skill applies to any remembered claim: recall proposes, the live system decides.
## Inspection and pruning
- `node scripts/checklist.mjs --recall <target>` → note count + latest note, as JSON, for showing
the human exactly what is remembered.
- The hook only appends. Pruning is a human act: truncate old lines or delete `sessions.jsonl`
entirely — the chain degrades gracefully to a fresh start. Offer pruning when notes are stale,
when the project changed hands, or on request ("forget this project").
- Never edit individual lines in place; if a note is wrong, the fix is a live-repo verification in
the session (and, if needed, deletion), not historical revisionism.
## Failure modes to name plainly
- **Stale resume:** memory says "paused at step 3/7", state file says the run was reset. Trust state.
- **Ghost parked item:** flagged code no longer exists. Close it with one line of evidence.
- **Unreadable memory:** boot skips it silently by design; mention it only if the human asks why
there was no banner.
scripts/checklist.mjs
#!/usr/bin/env node
/**
* refactor-memory — checklist + memory reader. Zero deps.
* node checklist.mjs -> prints the step checklist as JSON
* node checklist.mjs --recall <dir> -> reads <dir>/.refactor-chain/memory/sessions.jsonl and
* prints note count + the latest note (what boot would use)
*/
import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
const SKILL = "refactor-memory";
const PHASE = "improve/understand";
const STEPS = [
{ id: 1, step: "know-mechanics", detail: "Capture: memory-capture.mjs (SessionEnd) appends to .refactor-chain/memory/sessions.jsonl. Recall: boot.mjs (SessionStart) injects the last note's summary." },
{ id: 2, step: "persistence-filter", detail: "Persist durable decisions, parked items, run outcomes/position. Never transcripts, secrets, personal data, transient chatter." },
{ id: 3, step: "recall-safely", detail: "Every remembered fact is a hint: verify against state.json / history.jsonl / the code before acting (live-truth rule)." },
{ id: 4, step: "inspect", detail: "Use --recall <target> to show the human exactly what is remembered." },
{ id: 5, step: "prune", detail: "Append-only by hook; humans may truncate/delete sessions.jsonl. Offer when stale or on request." },
{ id: 6, step: "report", detail: "Fill templates/output.md (memory audit) when reporting on memory." },
];
function recall(dir) {
const mf = join(dir, ".refactor-chain", "memory", "sessions.jsonl");
if (!existsSync(mf)) return { notes: 0, note: "no memory file — the chain has nothing persisted here" };
const lines = readFileSync(mf, "utf8").trim().split("\n").filter(Boolean);
let latest = null;
try { latest = JSON.parse(lines[lines.length - 1]); } catch { latest = { unreadable: true }; }
return { notes: lines.length, latest, reminder: "verify every recalled fact against the live repo before acting on it" };
}
const argv = process.argv.slice(2);
const ri = argv.indexOf("--recall");
if (ri >= 0) {
process.stdout.write(JSON.stringify({ skill: SKILL, ...recall(argv[ri + 1] || process.cwd()) }, null, 2) + "\n");
} else {
process.stdout.write(JSON.stringify({ skill: SKILL, phase: PHASE, steps: STEPS }, null, 2) + "\n");
}
SKILL.md
---
name: refactor-memory
description: "Use this skill when a question touches the refactor-chain's per-project persistent memory — what the pipeline remembers about this repo between sessions, where that memory lives, what belongs in it, and how to use a recalled fact safely. Trigger phrases include \"what do you remember about this repo\", \"didn't we park something last time\", \"where did that note go\", \"should this be remembered\", \"clear the chain's memory\", or a session starting with a memory banner injected at boot. Spans the improve phase (memory is written at session end) and the understand phase (memory is recalled at session start). Advisory about memory content; the hooks that write and read it already exist — this skill governs them, it does not reinvent them."
---
# Project Memory — refactor-chain · improve/understand
**Bundle:** refactor-chain (self-diagnosing, self-healing fix-it pipeline).
**Phase:** improve (capture, at session end) and understand (recall, at session start) · **Prerequisite:** none — the hooks are dormant until the project has chain state or history · **Next:** whatever phase the recalled context feeds.
**Adaptivity / conditional:** conditional on the project having `.refactor-chain/` state, history, or memory; on a repo the chain has never touched, both hooks stay silent.
## Purpose
The pipeline keeps a small, durable memory per project so the next session does not start blind:
which run was in flight, how the last run ended, what got parked. This skill defines the memory
discipline — what deserves persisting, what never does, how notes get written and recalled, and
the one safety rule that makes recall trustworthy: **a remembered fact is a hint until the live
repo confirms it.**
## When to use
- A session opens with the boot banner ("refactor-chain memory: last run…") and you must decide what to do with it.
- Someone asks what the chain remembers here, or why it remembered (or forgot) something.
- Deciding mid-run whether a decision or parked item is memory-worthy.
- Auditing or pruning `.refactor-chain/memory/sessions.jsonl`.
## What I'll tell you (plain-language / ADHD-friendly)
- "Last time we were here, the web-lane run finished clean and two scope-drift items got parked. I'll check the repo to confirm that's still true before acting on it."
- "I only remember the durable stuff — where the run stood, how it ended, what was parked. Never your conversation, never anything secret."
- "That note says a run was paused at step 3 of 7 — but memory can be stale. Give me a second to verify against the actual state file."
- "This decision is worth remembering; it'll be captured automatically when the session ends. You don't have to do anything."
- "Want the memory wiped for this project? That's one file, and deleting it is safe — the chain just starts fresh. Say 'show technical details' for the file layout."
## Method
1. **Know the mechanics** (full detail in `references/method.md`). Capture: the SessionEnd hook
`memory-capture.mjs` appends one compact note per session to
`<project>/.refactor-chain/memory/sessions.jsonl` — active-run position (`lane`, `phase`,
`step`, `health`), flagged scope-drift notes, or the last run's outcome from `history.jsonl`.
It is dormant unless the project has chain state or history, and it exits 0 no matter what.
Recall: the SessionStart hook `boot.mjs` reads the **last** line and injects a one-line summary.
2. **Apply the persistence filter** when deciding what is memory-worthy: durable decisions,
parked/scope-drift items, run outcomes and position — yes. Transcripts, secrets, tokens,
personal data, transient debugging chatter — never. If it would not matter next week, it is
not memory.
3. **Recall safely.** On seeing an injected memory line, treat every remembered fact as a claim to
verify: check `state.json` before resuming a "paused run", check `history.jsonl` before citing
an outcome, check the repo before acting on a parked item — the code may have moved on. This is
the live-truth rule (the same discipline the `live-state-truth` skill teaches): the repo as it
exists now always outranks what memory says about it.
4. **Inspect on demand.** `node scripts/checklist.mjs --recall <target>` prints the note count and
the latest note so you can show the human exactly what is remembered.
5. **Prune deliberately.** Memory is append-only by the hook; humans may truncate or delete
`sessions.jsonl` at will. Offer this whenever memory is stale or the human asks to forget.
6. When reporting on memory, fill `templates/output.md` (the memory audit scaffold).
## Guardrails
- **Advisory-only: this skill never edits product code, and it never hand-writes memory entries — capture belongs to the SessionEnd hook so the format stays uniform.**
- Never persist transcripts, secrets, credentials, or personal data; a note that needs any of those is not written.
- **Never act on a remembered fact without verifying it against the live repo first.** Stale memory presented as truth is worse than no memory.
- Do not reinvent the mechanism: `memory-capture.mjs` and `boot.mjs` already exist — govern them, reference them, do not fork them.
## Verify
- Plain: "I can say exactly what this project's memory contains, why each note qualified, and I checked the repo before trusting any of it."
- Technical: `sessions.jsonl` parses line-by-line; every note carries only durable fields
(`activeRun` / `lastRun` / `flagged` / `at`); no secrets or prose transcripts present; any
remembered claim used this session has a named live-repo confirmation (file + what was checked).
## Resources
- `references/method.md` — the note schema, persistence filter, recall-verification protocol, and pruning rules.
- `examples/before-after.md` — worked example: a stale "paused run" memory, recalled and verified.
- `scripts/checklist.mjs` — step checklist as JSON; `--recall <target>` prints the project's memory summary.
- `templates/output.md` — the memory audit scaffold.
## Chain position
Bridges the end of one run to the start of the next. The **improve** phase closes with capture
(SessionEnd → `sessions.jsonl`, alongside the self-improvement history that `refactor-improve` reads); the
next session's **understand** phase opens with recall (SessionStart → injected summary), which
feeds diagnosis with context — always filtered through live-repo verification before anything
downstream trusts it. `refactor-code-principles` and the review gate still close every lane; memory
never bypasses them.
templates/output.md
# Project Memory Audit — <project name>
> Refactor-chain · memory (improve/understand) · <date>
> Memory proposes; the live repo decides. Every claim below was verified before being stated.
## Memory file
- Path: `<project>/.refactor-chain/memory/sessions.jsonl`
- Notes: `<N>` · oldest: `<date>` · newest: `<date>`
- Hook status: capture <active/dormant — why> · recall banner shown this session: <yes/no>
## Latest note, verified
| Remembered claim | Live check performed | Verdict |
|---|---|---|
| <e.g. "paused run: web lane, step 3/7"> | read `.refactor-chain/state.json` | <confirmed / stale — state says …> |
| <e.g. "last run: done"> | last line of `history.jsonl` | <confirmed / differs> |
| <e.g. parked item "…"> | checked `<path>` / `git log` | <still real / already resolved — closed> |
## Persistence-filter review
- Durable-only: <yes / violations found> · Secrets/transcripts/personal data: <none found / FLAG>
- Notes that no longer earn their place: <list, or "none">
## Actions
- Stale notes closed with evidence: <list, or "none">
- Pruning: <not needed / offered / performed by human — what was removed>
- Items carried forward into this session: <list, or "none">
## For next session
<one line on what SessionEnd capture is expected to write when this session closes>