Skip to content

Correcting the vault

Knowledge changes. A domain definition shifts after a refactor. An ADR turns out to be wrong. A lesson misremembered the conclusion.

Strata has four ways to handle it. Each carries a different blast radius.

OperationWhenSkill
CorrectOne paragraph is wrong; note overall stays useful/strata:correct
InvalidateThe whole note is no longer current; keep history visible/strata:correct (invalidate path)
Supersede (decisions only)A new ADR replaces an older one with a bidirectional link/strata:decide --supersedes <old>
ForgetThe note must disappear entirely (PHI, secrets, mistake)/strata:forget

All four leave a different audit trail. Correct keeps the note and records what changed. Invalidate keeps the note and marks it dead. Supersede keeps both notes and links them. Forget moves the note to .trash/ with a reason logged.

Use for focused edits where the note’s overall shape stays intact.

Terminal window
# Replace body
cat <<'EOF' | /strata:correct domain/order-aggregate.md \
--reason "OrderPriced now emitted before OrderConfirmed, not after"
# Order Aggregate
<new body>
EOF
# Update one frontmatter field
/strata:correct domain/order-aggregate.md \
--set status=stable \
--reason "Adopted in production after two-week rollout"

The script:

  1. Reads the note and frontmatter.
  2. Applies --set updates and optional body replacement.
  3. Appends {at, by, reason} to a corrections: list.
  4. Bumps updated: to today.
  5. Refreshes the FTS index.

--set is repeatable. --reason is strongly recommended. It’s the durable audit trail.

Fires on “fix the note about X”, “the part about Y should say Z”, or “update the status of <slug>. Claude proposes the edit, you approve, the script writes.

Use when a whole note is no longer current but historical context still matters. This is the invalidate path of the unified /strata:correct skill — say “stop using this”, “this is no longer true”, or “deprecate this note” and Claude routes to the invalidate flow with required --reason and optional --replaced-by.

Terminal window
# The script under the hood (auto-invoked by `/strata:correct`):
"${CLAUDE_PLUGIN_ROOT}/bin/run-python.sh" \
"${CLAUDE_PLUGIN_ROOT}/scripts/invalidate-note.py" \
domain/old-aggregate-pattern.md \
--reason "Aggregate split into Order + Payment in 2026-05-24 refactor" \
--replaced-by "domain/order-aggregate.md"

The note stays in the vault. Readable in Obsidian, in git log, in audit reviews. But:

  • status: invalidated joins the frontmatter.
  • invalidated_at, invalidated_by, invalidation_reason are recorded.
  • Optional replaced_by: link points to the successor.
  • Optional --invalid-since YYYY-MM-DD records when the fact stopped being true — separate from invalidated_at, which is just when you recorded it. Keeping both answers “how long were we acting on a wrong assumption?”, which a single timestamp can’t.
  • Default search filters it out. recall and find skip invalidated notes.
  • Opt back in with include_invalidated=true when you need history.

--reason is required. Invalidation without an explanation is just deletion in disguise.

Fires on “that’s no longer true”, “stop using <note>”, “this is outdated”, “deprecate <note>”.

Decisions get special treatment. A new ADR can formally retire an older one with bidirectional linking.

Terminal window
/strata:decide "Use Postgres with pgvector" \
--supersedes "2026-03-15-pinecone-for-embeddings"

The new ADR records supersedes: [2026-03-15-pinecone-for-embeddings]. The old ADR gets superseded_by: [<new-path>] written back into its frontmatter, and its status flips to superseded. The chain survives re-indexing on any machine.

Why not invalidate? Because supersession captures the reasoning through the new ADR’s body. The whole point of an ADR is the reasoning, so the successor carries that load. A one-line --reason on invalidate can’t.

Strata never deletes a superseded decision — it expires and rewrites. The old file stays as history; supersession just flips its status and drops it from the live list. One refinement worth the keystrokes: when you supersede, prepend a past-tense line to the old ADR (“Superseded by <new> — we used to do X”) via /strata:correct, so a reader who lands on it directly isn’t misled into thinking it’s current. Leave the rest of the body alone; the original reasoning is exactly what you’re keeping.

The hard delete. Use only when the note shouldn’t exist at all.

Terminal window
/strata:forget pr-context/feat-x/2026-05-24-secrets-leaked.md \
--reason "Contained API key from .env, removed per GDPR request"

The note moves to <vault>/<repo>/.trash/ with a JSONL audit log entry recording who, when, why, and SHA256. Recoverable. .trash/ is preserved unless you explicitly clean it.

--reason is required. Never auto-invokes. Forget only fires on explicit user request. The audit trail must reflect intent.

Three signals flag notes for likely correction.

code_refs that no longer resolve. When a note’s frontmatter lists code_refs: [Foo, Bar] and one of those symbols doesn’t exist in the current graph.json, /strata:review surfaces the note.

Path claims that broke. For ADRs and lessons extracted via bootstrap, plan_correlate re-runs to detect when previously-resolved paths no longer exist.

Staleness by usage. /strata:review also scores durable notes on an importance-weighted decay curve: a decision that’s old (by its own frontmatter date) and rarely recalled surfaces as a candidate to refresh, supersede, or invalidate. Recalling a note resets its clock, so notes the team actually uses don’t get flagged.

None of these auto-correct. All surface in review for you to decide between correct, invalidate, supersede, or forget.


Next: MCP tools. Every tool exposed to Claude, every argument.