Capture a memory (lesson / decision / rule / preference / feedback / project fact / reference) via propose-confirm-commit. Use on /capture, \"remember that\" / \"save this\", or skill invocation. Auto-proposes (never silent-writes; accept/edit/reject gates) only when high-stakes: a reversal of a shipped decision, a correction phrased with finality, or a warning-shaped \"X broke Z, hard to undo\". Otherwise never auto-fires.
Installs into .claude/skills of the current project.
Are you the author of Capture?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/ackeskin-capture)
---
name: capture
description: "Capture a memory (lesson / decision / rule / preference / feedback / project fact / reference) via propose-confirm-commit. Use on /capture, \"remember that\" / \"save this\", or skill invocation. Auto-proposes (never silent-writes; accept/edit/reject gates) only when high-stakes: a reversal of a shipped decision, a correction phrased with finality, or a warning-shaped \"X broke Z, hard to undo\". Otherwise never auto-fires."
---
# capture
The capture organ. Writes stored memory for future discovery; the memory format and kind taxonomy live in the format spec below.
## Prerequisite — load the format spec
Before classifying or writing anything, **Read `~/.claude/claude-md/memory-capture.md`** (the frontmatter template, kind table, folder layout, and capture rules). It is not always-on context — it loads here, on demand, only when capture actually runs. Every reference to `claude-md/memory-capture.md` below assumes you have read it this turn.
## When to run
- User types `/capture [content]` (explicit trigger).
- User says "remember that…", "save this as a memory," "capture this lesson."
- Another skill invokes capture programmatically (e.g. review's user-correction feedback loop).
- **High-stakes auto-propose.** Claude *proposes* a capture unprompted — **at the moment it happens, mid-session** — when a genuinely high-stakes item surfaces: (a) a decision that reverses or supersedes a prior shipped decision; (b) a correction the user phrases with finality ("never do X again", "this is the rule now"); (c) a `kind: warning`-shaped event ("I did X, it broke Z, hard to undo"). This is auto-***propose***, never auto-***write*** — the standard accept/edit/reject (§7) still gates every write, so the collaborator principle holds. Fire **on the event, not at session close** — deferring to `/recap` loses it when no recap runs. Keep it **rare**: only the three shapes above; never auto-propose ordinary lessons.
- **Otherwise do not auto-fire.** No session-start or per-turn triggers. Mode B (silent episodic auto-*write*) stays parked — see the auto-propose bullet above.
## Inputs
1. **Content source.**
- Text passed after `/capture` → that text is the candidate rule / lesson / fact.
- No arguments → infer content from the most recent user/assistant turns. If multiple recent turns could be the capture target, ask one clarifying question rather than guessing.
2. **Triggering context.** What in the session made this worth capturing — a correction, a decision, a surprise, a pattern. Use this to decide `kind` and to write the **Why:** line.
3. **Target project.** `$CLAUDE_PROJECT_DIR` if set, otherwise current working directory. Universal-applicable content may override to the global tree (see §"Universal vs project" below).
## Procedure
### 1. Classify `type`
From the content and how the user framed it:
| Trigger shape | `type` |
| --- | --- |
| User-given rule or directive ("always do X", "never do Y") | `feedback` |
| Fact about the ongoing work (who / when / why) | `project` |
| Pointer to an external system | `reference` |
| User profile detail (role, preference, expertise) | `user` |
`type` picks between flat top-level folders (`feedback/`, `project/`, `reference/`, `user/`) vs. the `kind`-keyed folders used for the others. See `claude-md/memory-capture.md` for the exact folder layout.
### 2. Classify `kind`
When `type` is not `feedback` / `project`, pick `kind` per `memory-capture.md`'s kind table. Additional kind owned here: `canonical-command` — a verb→exact-command pin, a specialization of `architectural-rule`, routes per §5b. `canonical-command` never lands as file frontmatter — §5b writes sections into the canonical-commands rule file, so the kind enums of memory-audit and the engine are unaffected. If the body has the shape "I did X on Y and it broke Z, hard to undo," it's a `warning`, not a `lesson`. Omit `kind` when default (`lesson`).
### 3. Classify `scope`
- Task-specific → match current working scope (language, module, domain).
- Universal → `[global]` or a named universal tag (e.g. `[universal, naming]`).
- Project-specific → include `[project-<slug>]` so discovery can filter.
- Multiple scopes allowed: `[auth, billing]` means relevant for either.
### 4. Classify `relevance`
- `always` — load-bearing rule that applies every session.
- `when-touching-<X>` — scoped to a domain / module / language.
- `when-language-<lang>` — language-gated (architectural-rule convention).
- `when-domain-<domain>` — domain-gated (architectural-rule convention).
- `during-planning` / `during-review` / `during-debug` — phase-specific.
- Multiple allowed, comma-separated.
### 5. Universal vs project
If the content is plainly universal (applies across every project, every language), prefer the global tree over per-project memory:
- `kind: architectural-rule` + universal/language/domain → the architectural-rules tree (but pick the **overlay tier** per step 5a — never write into the shipped tree directly).
- Everything else universal → `~/.claude/memory/<folder>/<topic>.md` if the user has a global memory tree; otherwise project memory with `scope: [global]` and a note that this may want promoting later.
If ambiguous (feels universal but might be project-specific), ask the user before writing.
### 5a. Overlay tier (architectural-rule only)
When `kind: architectural-rule`, the rule must land in an **overlay tier**, not the shipped tree (the shipped tree is synced + symlinked; hand-edits there are overwritten on the next sync). Ask the target tier:
| Tier | Path | When |
| --- | --- | --- |
| **user** (default) | `~/.claude/architectural-rules-local/<scope>/<name>.md` | personal rule, this machine / the user's own sync |
| **company** | `~/.claude/architectural-rules-company/<scope>/<name>.md` | a team rule — only when the user maintains the company repo locally; remind them to commit + push it |
| **project** | `<project-root>/.claude/rules/<scope>/<name>.md` | rule specific to this codebase, committed with it |
Write `origin: <tier>` into the frontmatter (`origin: user | company | project`). Default to **user** unless the user says otherwise.
**Override vs new rule.** If a file with the same `<scope>/<name>` key already exists in a lower tier (shipped or company), the user is *overriding* it. Offer the choice:
- **whole-file** — the new file replaces the lower one entirely (seed the draft from the lower-tier body so they start from the real baseline).
- **field patch** — set `override: <scope>/<name>` + `mode: patch` and capture only the deltas (`## remove` / `## replace` / `## add`), anchored on the lower file's bullet ids. Lighter; survives upstream evolution.
Surface which lower-tier file is being shadowed/patched so the user confirms intent. For a genuinely new key (no lower-tier file), it's a plain add — no override frontmatter.
This step routes only `kind: architectural-rule`. All other kinds follow steps 5 / 8 unchanged. The `rules` skill is the richer front door for managing existing overlay rules; capture is where a *new* rule is born mid-conversation.
### 5b. Canonical-command route
A `canonical-command` pin lives in **`canonical-commands.md`**, not its own file. Fires most often when the user *corrects an improvised command* ("no — use `gh api …/comments --paginate`, you missed comments again"): that correction is a candidate pin.
Route it through §5a's overlay-tier choice, but the destination is a **section appended to the tier's `canonical-commands.md`**, not a new per-rule file:
| Tier | Destination |
| --- | --- |
| user / company | `…/architectural-rules-{local,company}/universal/canonical-commands.md` |
| project | `<repo>/CLAUDE.md` `## Canonical commands` section, or `<repo>/.claude/canonical-commands.md` — project pins override universal ones |
Draft the entry in that file's strict shape — `### <verb>`, a fenced command block, then a **mandatory** `**Why:**` line (the rationale is what lets the agent adapt; a pin without it is the slop case). Before writing, **redact** per §-redaction: a pinned command must be safe to commit, so a token / API key / internal URL in the command is rejected — force an env var or placeholder. If the verb already has a pin, this is an *override*: surface the existing entry and confirm intent (same as §5a's override flow).
### 6. Draft
Build the frontmatter per `claude-md/memory-capture.md`'s template exactly — `relations` (if any) is populated by step 6a.
**Always quote the `description` value** (and any frontmatter scalar that may contain `:` `#` `[` `]` `{` `}` `&` `*`). An unquoted `description:` with a `: ` inside parses as a nested YAML map, fails, and the loader **silently drops the whole memory** from the corpus. Default to double-quoting descriptions; it is never wrong to quote.
Body: **rule + why + scope**, in the **model-optimized compressed form** — memory bodies are read by models, not humans. Follow [`docs/memory-compression-spec.md`](../../docs/memory-compression-spec.md): hybrid by field (`description` + MEMORY.md hook stay human-legible — they are the discovery signal; the body goes terse shorthand), and the **misapplication test** for the why-line:
> Keep the why iff removing it would let a future model misapply the rule. Drop it when the rule self-enforces.
For `type: feedback` and `type: project` bodies, the `**Why:**` / `**How to apply:**` lines apply *through that test* — keep the why compressed (`why: <terse cause>`) when load-bearing, fold/drop `how-to-apply` unless it adds a firing condition the rule doesn't already imply. `kind: warning` bodies always keep their (compressed) incident anchor — it is what makes a warning a warning.
### 6a. Cross-memory relations
Before writing, find existing memories whose territory overlaps the new one. **Primary path:** query the engine — `mcp__project-memory__discover({ cwd, task_keywords: [<new memory's key terms>], scopes: [<new memory's scopes>], top_n: 8 })` — and treat its ranked hits as the relation candidates (out of context, and it already surfaces `⚡ contradicts` / `[related_to]` flags on neighbours). **Fallback** (MCP down): scan MEMORY.md in-context for entries whose title or hook overlaps the new memory's title / description / scope. For each plausible match:
- **Read the candidate's body.** Decide whether the new memory replaces it (`supersedes`), disagrees with it (`contradicts`), reinforces it (`supports`), or is a sibling in territory (`related_to`).
- **For `supersedes`:** confirm with the user before writing. Supersedes is the load-bearing relation — it hides the target from normal discovery. The prompt:
> Capturing this memory looks like it would supersede `<target-path>`. Confirm? (y / n / show target)
>
> - y → write `supersedes: <target>` on new memory; write `superseded_by: <new-path>` on target in the same step.
> - n → no relation written; new memory and target both stay active.
> - show target → display the target's body for the user to compare, then re-ask.
- **For `contradicts`:** propose the link + `note:` capturing *why* they disagree; the user accepts/edits.
- **For `supports` / `related_to`:** propose, accept-with-no-friction — low retrieval impact, cheap to roll back.
Bidirectional integrity (mandatory for `supersedes`):
- Writing `supersedes: target.md` on the new memory **always** also writes `superseded_by: <new-path>` on the target. Two file writes in one step. If the target write fails for any reason, abort the whole capture — don't leave a one-way relation in place.
`related_to` and `supports` are bidirectional in spirit but not enforced — discovery treats them as soft hints either way.
Skip 6a entirely when no plausible candidates surface — most captures have no cross-memory link; don't force a relation.
### 6b. Bloat budget guard (write-time)
Before proposing, run two checks so bloat cannot enter the corpus at the source (see `docs/memory-compression-spec.md` and `/memory-audit` dim 10):
**Body-size budget.** Measure the drafted body (bytes, excluding frontmatter):
- **≤ ~600 B** → fine, proceed.
- **600–1000 B** → acceptable only if the content genuinely needs it (a table, a multi-case rule, an incident anchor). If it's prose that could compress, compress it first.
- **> ~1000 B** → over budget. Re-compress per the spec before proposing. If it still exceeds after honest compression, the memory is probably **two memories** — split it. Never propose an >1000 B body without either compressing or flagging *why* it must be large in the proposal.
**`relevance: always` challenge.** If the draft sets `relevance: always`, justify it explicitly: *"does this genuinely apply every single session, or only when touching a domain/phase?"* — every `always` entry is paid every session. Default to a scoped/phase relevance (`when-touching-X`, `during-review`) unless the rule is a load-bearing discipline that fires regardless of task. Finished-work facts, tool-specific gotchas, and domain rules are **never** `always`. State the justification in the proposal so the user can veto.
Guards, not gates — the user can override in §7, but the check must *run* and its result must appear in the proposal.
### 7. Propose
Show the user:
- **Target path** — where the file will land (absolute or `~/`-relative).
- **Full frontmatter and body** — no placeholders.
- **MEMORY.md index line** that will be appended (if applicable — see §8).
- **Budget line** (§6b) — body size vs budget, and the `relevance: always` justification if applicable.
Ask: *accept / edit / reject*.
### 7a. Pre-write secret scan
After the user accepts in step 7 but **before** the disk write in step 8, run the secret-pattern set from [`secret-patterns.md`](secret-patterns.md) over the proposed body and over every frontmatter value (description, name, scope tags, relations notes, etc.). Frontmatter keys themselves are not scanned — they're a fixed schema.
For each pattern in the set, walk the text and collect matches as `(pattern_name, type_label, matched_text, line_offset)`.
**Zero matches** → continue to step 8.
**Matches found** → halt the write. For each match, surface to the user:
```
Capture paused — possible secret detected.
Pattern: <type_label> (matches /<regex>/)
Line <N>: "<matched_text>"
(r)edact and continue — replace with <REDACTED:<pattern_name>>
(e)dit body manually before retrying
(a)bort — don't write this memory
(i)gnore — pattern misfire, write this match as-is (logs to false-positive table)
```
Process matches one at a time (not all at once). The user picks per match:
- **r → redact and continue.** Substitute the matched text with `<REDACTED:<pattern_name>>` in the body / frontmatter value. Re-run the entire pattern set against the (now-modified) text — there may be more matches the first didn't expose, or new ones overlapping. Keep redacting until zero matches remain, then continue to step 8.
- **e → edit body.** Drop back into step 7 with the current draft for the user to edit. Loop. (The user's edit may resolve all matches manually, in which case the next pass through 7a finds zero.)
- **a → abort.** Discard the capture cleanly. No file, no MEMORY.md change, no residue. Same as step 8 reject.
- **i → ignore (false positive).** Write the match as-is. Append a row to the false-positive table in `secret-patterns.md` capturing the pattern name, a *sanitized* version of the match (replace the secret-like token with `<example>`), and the user's one-line reason. Re-run the rest of the pattern set on the body to catch any unrelated matches; ignore is per-match, not per-capture.
Per-match ignore avoids edit-around friction; the false-positive log feeds pattern refinement.
The body that lands on disk is the post-redaction version; the original (with secrets) is never written anywhere — not to disk, not to the false-positive log, not to anywhere.
### 8. Commit
**On accept:**
1. Create the target folder lazily if absent.
2. Write the file.
3. If the file is under a `MEMORY.md`-indexed tree (per-project memory root, not any architectural-rules tier — overlay rule files are discovered by folder scan, not MEMORY.md), append one line to `MEMORY.md`:
```
- [Short title](path/including/subfolder.md) — terse one-line hook
```
Path includes the subfolder. Hook ≤ ~150 chars.
4. Report the written path and index line.
**On edit:**
1. Take the user's edit as the new draft.
2. Loop back to step 7.
**On reject:**
1. Discard. No file, no index change, no residue.
2. Acknowledge and move on.
## Failure modes
- **Cannot classify `kind` confidently** — propose best guess, flag the uncertainty in the proposal message, let the user override. Do not silently commit a fuzzy classification.
- **Ambiguous target folder** (e.g. "is this a universal rule or a C++-specific rule?") — ask one clarifying question. Do not write to both locations.
- **Project memory root missing (first creation)** — if no `~/.claude/projects/*/memory/` folder matches the project root, derive the slug from the project directory path with the platform's path-to-slug encoding (the same encoding visible in existing sibling project folders), create the tree, and note the creation in the commit summary. Seed `MEMORY.md` with a header line before writing the first file.
- **`MEMORY.md` write race** (rare, but possible if user has another session open) — if the file has changed since read, re-read, reconcile, re-append. Never blind-overwrite.
- **Content plainly duplicates an existing memory** — surface the duplicate in the proposal, ask whether to replace / merge / reject. Never silently overwrite.
## What capture does NOT do
- **Does not auto-fire.** Mode B parked.
- **Does not decide when to capture.** Triggers live in calling context (user says "remember", review surfaces a correction). This skill owns the *how*, not the *when*.
- **Does not modify existing memories for reasons beyond the specific capture.** Lazy migration of old files happens only when the user edits them for another reason (per `memory-capture.md`).
- **Does not read stored memory.** That's discovery's job (`skills/discover`).
- **Does not silent-redact.** Step 7a always halts and surfaces matches; never substitutes without user confirmation — the threat model is "I forgot the stack trace contained a key," not an adversary, so the user is in the loop on every match.
- **Does not retroactively scan existing memories.** Step 7a runs at capture time only. Retrospective scanning of already-written memories lives in `/memory-audit` dimension 9.
See [`docs/capture-organ.md`](../../docs/capture-organ.md) for the organ's scope map and relationship to the rest of the context family.