A research note on how to manage the plans and specs that agent-driven
development produces: when to keep them, when to delete them, and the
conventions that make either choice safe. Written after a real cleanup pass on
the transport-manager repo (54 plan/spec files, ~38k lines, all shipped),
where the first attempt over-deleted and had to be revised. Expanded after a
second research round that surfaced the OpenSpec/SpecD ecosystem, the ADR
"never-delete" school, and a whole category of spec-drift detection tooling.
The lessons are generalized here for other projects and for discussion with
other engineers.
- Plans (execution checklists: "run this command, edit this file, commit with this message") and specs (design docs: data models, architecture rationale, decision records) have different durable value and deserve different lifecycle treatment. Don't bundle them.
- Plans → delete on ship. Once the feature ships, a plan describes how it was built and has no ongoing reference value. Git history is the archive. Keep a ledger so old file names/dates stay resolvable.
- Specs → retain in-tree as institutional knowledge, with an explicit
drift caveat:
docs/+ code are the source of truth when a spec and the implementation disagree. If a spec becomes stale, annotate it (Superseded / Deprecated) rather than silently deleting it — this is the ADR "never-delete" rule, and it generalizes to specs. - If you do delete a spec, the delete-on-ship convention requires promoting its durable content into a permanent doc first — deletion without promotion is the most common mistake (and the one made in the first pass of the cleanup that motivated this note). OpenSpec makes this mechanical: archiving folds a change's delta specs into the living spec.
- Drift is detectable. A whole tooling category now exists
(
staledocs,spec-drift,spec-sync,SpecKit sync) that pairs specs to code and flags one-sided changes deterministically. "We can't detect spec drift automatically" is no longer true.
| Term | What it is | Durable value after ship |
|---|---|---|
| Plan | Execution checklist. - [ ] steps, exact commands, file edits, commit messages. OpenSpec calls this tasks.md. |
None — describes how it was built. Recoverable from git history. |
| Spec | Design document. Data models, architecture rationale, trade-off records, "why this shape". OpenSpec's specs/ + design.md. |
High — captures why the system is shaped this way. Not obvious from code. |
| Living spec | OpenSpec term: openspec/specs/<capability>/spec.md — the current-state behavior contract, produced by folding archived change deltas. |
The durable home. This is where promoted content lands. |
| Delta | OpenSpec term: a structured spec change (ADDED/MODIFIED/REMOVED requirements) inside a change folder. The event log of the living spec. |
Folded into the living spec on archive; the change folder is the transient part. |
| Ledger | A permanent index (usually README.md) listing deleted docs: date, title, ship commit. |
Required if you delete-on-ship, so old names/dates stay resolvable. |
The single most important distinction: plans answer "how", specs answer "why". Code preserves "how" well; it preserves "why" poorly. That asymmetry drives the whole policy.
There are three schools, plus a tooling layer that changes the calculus.
Fully-shipped specs are promoted into
docs/and deleted in the same PR. Git history is the archive — nospecs/completed/graveyards. Aspecs/README.mdledger keeps IDs resolvable after deletion. Partially shipped specs get trimmed to open work. Precedence rule:docs/+ code are current truth; specs never outrank them.
Key detail often missed: the convention has two steps — promote, then delete. Skipping the promotion step and just deleting is the failure mode. The ledger is mandatory, not optional: without it, deleted names become unresolvable and future readers can't find what existed.
Source: brickhouse-tech/sync-agents PR #78 / commit 324e6c1
Plans/specs move through
drafts/ → active/ → completed/ → abandoned/. Completed and abandoned are kept permanently as institutional knowledge.
- TIM —
plans/{drafts,active,completed,abandoned}/; completed/abandoned are permanent. Abandoned plans are kept explicitly to "preserve learnings". - Agent Smith —
phases/{planned,active,done}/;done/documents stay as historical reference. Acontext.yamlledger tracks every phase by ID. - SpecMem — classifies specs as
healthy / orphaned / stale(90-day default). Recommends archiving before deleting and running health checks to catch drift early.
Sources:
The ADR (Architecture Decision Record) community has thought hardest about retiring docs and converged on a striking conclusion: almost never delete. This generalizes directly to specs.
Three paths for an obsolete ADR: supersede (decision replaced by a named new ADR — two-file atomic PR, bidirectional pointers), deprecate (decision no longer operative, no single successor — one file, one-sentence reason), or delete (almost never right — only justified for a Proposed ADR whose PR was closed without merging). In all other cases, the ADR stays — stale archaeology is exactly what the practice is for.
The core distinction that drives the choice:
- Superseded = the Decision section is now false (team chose A, now
chooses B, and a new ADR naming B exists). Two files, one atomic PR,
bidirectional
Supersedes:/Superseded-by:pointers, CI integrity job verifies both pointers resolve. - Deprecated = the Context section is now void (the system the ADR
governed is gone, no replacement decision). One file:
Status: Deprecated, aDeprecated YYYY-MM-DD: [reason]note, optionalDeprecated-by:pointer. The original Context/Decision/Consequences sections are left untouched — they're permanent history. - Deleted = only for ADRs that never reached
Accepted(unmerged drafts).
Five canonical statuses the tooling knows: Proposed, Accepted,
Superseded, Deprecated, Rejected. Non-canonical variants
(Obsolete, Retired, Archived, Inactive) break the index.
Why this matters for specs: the ADR practice treats deletion as corruption
of the audit trail — "it removes the evidence that the decision existed, which
is precisely what the practice is designed to preserve." The same is true of
design specs: a future reader who finds a SUPERSEDED note knows the design
existed and was replaced; a reader who finds nothing doesn't know either way.
Sources:
- WhyChose — When to Retire an ADR: Deprecation, Supersession, and the Never-Delete Rule
- WhyChose — ADR Supersession Pattern
- AWS Prescriptive Guidance — Using ADRs to streamline decision-making (immutability + supersession)
- Eden Technologies — ADRs: How to Document Decisions That Last
- mathews-tom/armory — adr-writer status lifecycle
The SDD (spec-driven development) tooling ecosystem has a fourth answer that sidesteps the keep-vs-delete debate: specs are not retired, they're folded. A change carries a delta; on archive, the delta merges into a living spec that always describes current behavior. The change folder is transient; the living spec is permanent.
OpenSpec (Fission-AI; openspec.dev; 66.7k stars, ~265k devs/month) is the
dominant framework. The mental model in one line: two folders — specs/ is
what's true, changes/ is what you're proposing; archiving moves a proposal
into truth.
- A change = one unit of work = one folder under
openspec/changes/<name>/holdingproposal.md(why),specs/(behavior delta),design.md(how to build),tasks.md(implementation checklist). - A delta describes what's changing (
ADDED/MODIFIED/REMOVEDrequirements with### Requirement:/#### Scenario:blocks), not the whole world. "You describe the diff, not the destination." - Archiving folds the change's delta specs into the living spec under
openspec/specs/<capability>/spec.md, then moves the change folder tochanges/archive/with a date stamp. The living spec now describes the new reality; the change folder is retained as history. - The OPSX workflow is action-based, not phase-based:
propose/explore/apply/verify/archive— do any of them anytime. Dependencies are enablers, not gates.
This is the promote step made mechanical. Where brickhouse says "promote
durable content into docs/ then delete," OpenSpec says "the fold is the
promotion, and the archive retains the change folder." No manual promotion
step, no lost content.
Sources:
SpecD (specd-sdd) is a more opinionated sibling with an explicit state machine and approval gates:
drafting → designing ⇄ ready → implementing ⇄ verifying → done → archivable → archiving
↓ ↓
pending-spec-approval pending-signoff
↓ ↓
spec-approved signed-off
Three layers: CLI (engine — manages changes, validates artifacts, enforces
gates), workflow (rules — states, transitions, required artifacts, gates
from schema + config), skills (interface — slash commands that orchestrate
the CLI). Approval gates are optional (approvals.spec, approvals.signoff).
A redesign transition is available from any active state, including
archiving when recovery is needed.
Source: SpecD — Change Lifecycle Guide
kentra spec-lifecycle is a pure-Go reimplementation of the OpenSpec format
(not the runtime). Stages: refine, design, plan (features) / repro,
fix (bugs). Gate records are durable JSON (approval-state.json) —
records, not enforcement; an external engine or CI reads and blocks.
Notably, the design doc itself carries a STATUS (2026-07-27, read first)
header noting that change 007-yaml-source-of-truth superseded its
OpenSpec-format premise — it practices what it preaches about supersession.
Source: kentra-io/spec-lifecycle
Specs must be version-controlled alongside the implementation. Explicitly flags the anti-pattern of "letting documentation fall out of date as agents evolve, producing specifications that describe yesterday's behavior rather than today's." The remedy it advocates is keeping specs current, not deleting them. Specs decouple agent behavior from its author — when the original developer leaves, the next maintainer picks up the system through the spec.
Source: AWS Well-Architected Agentic AI Lens — AGENTSUS03-BP03
A whole category of tools now exists that pairs specs to code and flags one-sided changes deterministically. This changes the keep-vs-delete calculus: the strongest argument for deleting drifted specs was that stale specs actively mislead. If drift is caught mechanically, that argument weakens and the never-delete/fold schools become more attractive.
| Tool | Approach | Notable |
|---|---|---|
| staledocs | Deterministic, no LLM in the detection path. Pairs every doc with the code it describes, records a fingerprint when confirmed matching, flags any one-sided change. | CODE_LAG catches the unbuilt spec (not just the stale doc). Two-step ack forces agents to read evidence before passing the gate. Language-agnostic. |
| spec-drift | Rust-specific. Four surfaces: docs (README/AGENTS.md/docs/), examples, test-spec, CI configs. |
Catches "lying tests" (assertion commented out, checks 200 instead of 403), constraint violations, deprecated usage. |
| spec-sync | Bidirectional spec↔code validation with severity levels. | Code exports something not in spec = Warning; spec documents something missing from code = Error. stale command finds specs that haven't kept up with code changes. |
| SpecSync | Commit-time reliability gate. | Validates spec/code/test/doc alignment before commits finalize — drift never enters the codebase. |
| SpecKit sync | AI-assisted drift resolution with human approval. | Produces a drift report (aligned / drifted / unverifiable counts) and proposes fixes interactively. |
The staledocs framing is the sharpest: "Agents change code at a pace docs
never survive. Wire check --gate strict into pre-commit/CI and agents are
mechanically forced to keep docs current — and cannot rubber-stamp their way
past the gate, because the two-step ack makes them read the evidence first."
Treat plans and specs separately, and borrow the fold idea from OpenSpec and the never-delete-without-annotation idea from ADR practice.
- While in progress, a plan lives at
plans/<date>-<slug>.md(OpenSpec'stasks.md). - When the feature ships, in the same PR:
- Add a row to the Deleted plans table in the ledger: date, title, ship commit.
- Delete the plan body.
- To recover a plan body later:
git show <ship-commit>:docs/superpowers/plans/<file>.md
Why delete plans: they're execution scratchpads. Once shipped, the code is the result; the steps that produced it have no ongoing reference value and silently rot (commands reference old paths, old test names, old branch names). Git history preserves them perfectly for anyone who needs the archaeology.
- A spec lives at
specs/<date>-<slug>-design.mdand stays in-tree after ship. If you adopt OpenSpec's model, the spec's durable content is folded into a living spec on archive and the change folder is retained as history. - When a spec is superseded by a new design, annotate, don't delete:
Use Deprecated (not Superseded) when the context is void — the feature was removed entirely with no replacement. The distinction matters: Superseded = the design was reversed; Deprecated = the thing the design governed no longer exists.
> **SUPERSEDED (YYYY-MM-DD).** Replaced by <newer spec/commit>. The text > below is retained for historical context only.
- Document the precedence rule in the specs README:
docs/+ code are the source of truth. Specs describe the design as of the date on the file and may drift. When they conflict with the implementation, the implementation wins. If a spec becomes actively misleading, annotate it (Superseded / Deprecated) — don't delete it silently.
Why keep specs: they capture why — the trade-offs, rejected alternatives, data-model rationale that isn't reconstructable from code. A spec buried in a 4-month-old git commit is effectively invisible to a future reader who doesn't know it exists; an in-tree spec with a SUPERSEDED header is honest and discoverable. With drift-detection tooling wired into CI, the "stale specs mislead" risk is mitigated mechanically.
When a spec's durable content belongs in a permanent doc (e.g. an operator
guide under docs/), the delete-on-ship convention applies with a required
promotion step:
- Promote the durable content into the permanent doc (or fold it into a living spec, OpenSpec-style).
- Add a pointer from the spec (or the spec's ledger row) to the permanent doc.
- Then delete the spec body and record it in the ledger.
Deletion without promotion is the most common mistake. It's the one that motivated this note: the first cleanup pass deleted 23 specs outright without promoting their content, and the result was strictly worse than keeping them.
If your stack supports it, pair a drift-detection tool (staledocs,
spec-sync, spec-drift) with your specs in CI. This converts "specs rot
silently" into "specs that drift fail the build." It also catches the
opposite direction — CODE_LAG / "spec documents something missing from
code" — which is the unbuilt-spec case that pure deletion would hide.
A README.md at the root of the plans/specs directory. Minimum content:
- The policy in one paragraph (what gets deleted, what gets kept, why).
- A Deleted plans table:
Date | Title | Ship commit. - A Retained specs section: list + which have active external references.
- A recovery recipe:
git show <commit>:<path>. - The going-forward convention so the next agent/author follows the same rule.
The ledger is what makes delete-on-ship safe. Without it, deleted file names
become unresolvable — a future reference like "see the 2026-05-24 cutover
spec" becomes a dead end. With it, the name resolves to a ship commit and the
body is one git show away.
The ADR equivalent is the bidirectional pointer: a superseded ADR points
to its successor (Superseded-by:) and the successor points back
(Supersedes:), with a CI job verifying both resolve. If you supersede a
spec rather than delete it, the same integrity check applies — point both
ways, and ideally CI-check the pointers.
Use this when pruning an accumulated plans/ + specs/ directory.
- Inventory —
ls plans/ specs/,wc -leach. Note plan/spec pairs by date. - Map to ship commits —
git log --oneline -- <dir>and match each plan/spec to its feature commit. Anything with no matching commit is abandoned/draft — handle separately (keep, or move to anabandoned/folder per TIM). - Find external references —
grep -rn "plans/\|specs/" docs/ AGENTS.md *.md(exclude the plans/specs dir itself). These are the only thing that should block deletion. - Decide per file type:
- Plans → delete, add ledger row.
- Specs → keep and annotate if stale (Superseded / Deprecated), OR promote-then-delete if durable content belongs in a permanent doc, OR fold into a living spec if you're on OpenSpec.
- Fix dangling references — for each external reference to a deleted file, point it at the ledger + ship commit. For references to superseded specs, add the back-pointer (the reference should mention the successor).
- Write the ledger before the deletion commit, not after.
- Verify —
grep -rn "plans/" docs/ AGENTS.mdreturns only ledger hits (no dangling links to deleted plans).find <dir> -type fshows the expected remaining set.- Every retained-spec reference resolves to a file that exists.
- If using supersession: both pointers resolve (CI integrity check).
- Commit + open MR — one commit, clear message distinguishing plans (deleted) from specs (retained / annotated / promoted / folded).
These are the mistakes made during the transport-manager cleanup that
motivated this note, plus ones visible in the ADR/SDD literature. Call them
out in review.
- Bundling plans and specs into one deletion decision. They have different durable value. Decide separately. The first pass deleted both with the same rationale and was wrong about half the files.
- Delete-without-promote. Invoking "delete-on-ship" but skipping the promotion step. The brickhouse convention is promote then delete, not delete. OpenSpec's fold makes promotion mechanical — adopt it if you can.
- No ledger. Deleting bodies with no index. The names become unresolvable; future "see the X spec" references die.
- Trusting the convention name without reading its steps. "Delete-on-ship" sounds like "just delete on ship." It isn't. Read the source.
- Leaving dangling references. External docs that point at deleted files.
Always
grepafter deletion and fix or restore. - Treating git history as equivalent to in-tree docs for design rationale. It preserves the bytes but not the discoverability. A spec at a 4-month-old commit is invisible; an in-tree spec with a SUPERSEDED header is honest and findable.
- Silent deletion of drifted specs. If a spec is wrong now, the right fix is to annotate it (Superseded / Deprecated) so the next reader knows it's stale — not to delete it so the next reader doesn't know it ever existed. This is the ADR never-delete rule.
- One-sided supersession. Updating the new ADR/spec with a
Supersedes:pointer but forgetting the back-pointer on the old one. The ADR community calls this "the most common integrity bug in ADR directories." Fix with a CI job that verifies both pointers resolve, in one atomic PR. - Using non-canonical status values.
Obsolete,Retired,Archived,Inactiveinstead ofSuperseded/Deprecated. Breaks index tooling. Stick to the five canonical values. - Assuming drift is undetectable. "We can't detect spec drift automatically, so delete to be safe" is outdated. The tooling layer exists; wire it in and the safety argument for deletion weakens.
Is the document a PLAN (execution checklist, "how")?
├── yes → Has the feature shipped?
│ ├── yes → DELETE on ship; add ledger row. (git history archives it.)
│ └── no → Keep (it's active work). Move to abandoned/ if cancelled.
└── no (it's a SPEC — design doc, "why")
├── Is the design reversed by a named new spec/ADR?
│ ├── yes → SUPERSEDE: annotate old (Status + Superseded-by pointer),
│ │ add Supersedes pointer on new, both in one atomic PR.
│ └── no →
├── Is the context void (the feature was removed, no replacement)?
│ ├── yes → DEPRECATE: Status: Deprecated + dated one-sentence reason.
│ └── no →
├── Does the durable content belong in a permanent doc / living spec?
│ ├── yes → PROMOTE/FOLD into the permanent doc, then delete the spec
│ │ body + ledger row pointing at the permanent doc.
│ └── no → KEEP in-tree as historical design reference.
└── (in all retain cases: wire drift detection so staleness is caught.)
- Where's the line between "spec" and "permanent doc"? Some specs are
really operator/user guides in disguise (e.g. an MCP server design that
became
docs/mcp.md). Heuristic: if a non-contributor would need it, it's a permanent doc — promote/fold it. If only a future maintainer would need it, it's a spec — keep it inspecs/. - Should abandoned/draft plans be kept in an
abandoned/folder (TIM) or deleted? TIM keeps them for institutional knowledge. Thetransport-managerrepo had none, so this wasn't tested. Lean toward keeping with anabandoned/prefix until you're sure they're worthless. - OpenSpec fold vs. manual promote-then-delete — when is each worth it?
OpenSpec's machinery is a real adoption cost (CLI, schema, change-folder
convention). For a repo that already has a
plans/+specs/habit, the manual hybrid policy here may be cheaper. For a greenfield repo or one already on an SDD tool, the fold is cleaner and the audit trail is mechanical. - Should the ledger be machine-generated from
git log? Tempting, butgit log -- <dir>doesn't distinguish plans from specs and doesn't know which feature commit a doc shipped with. A hand-maintained ledger with explicit ship commits is more useful and more honest. The ADR community's CI integrity jobs (verifying supersession pointers resolve) are the machine-checkable part worth automating. - Status vocabulary: adopt the ADR five across specs too?
Proposed / Accepted / Superseded / Deprecated / Rejectedis well-understood and tool-supported. Specs that use the same vocabulary as ADRs are easier to index and reason about uniformly.
Starting state: docs/superpowers/{plans,specs}/ — 30 plans (~32k lines) +
24 specs (~6.5k lines), all shipped, all tracked in git.
First pass (over-deletion): Applied "delete-on-ship" uniformly — deleted
all 30 plans and 23 of 24 specs, kept only the one spec with an active
external reference (mcp-server-design.md), added a ledger, fixed 4 dangling
references. Committed, pushed, opened MR !176.
The mistake: The case for deleting plans was strong (execution checklists, no durable value). The case for deleting specs was weak — the research sources (TIM, Agent Smith, SpecMem, AWS) actually lean toward keeping design docs, the ADR school says never delete without annotation, and the one source that deletes (brickhouse) requires a promotion step that was skipped for 22 of the specs.
Second pass (correction): Restored all 24 specs to specs/. Rewrote the
ledger with two sections: "Plans — delete-on-ship" (the deleted-plans table)
and "Specs — retained as historical design reference" (retention policy +
drift caveat). Reverted the 3 unnecessary reference edits (they pointed at
retained specs and didn't need changing); kept the 1 genuine fix (a reference
to a deleted plan). Force-pushed, updated the MR description. Merged.
Final state: docs/superpowers/ = README ledger + 24 specs, 0 plans.
~32k lines of stale execution plans removed; all design rationale preserved.
Lesson: the rigorous version of "delete-on-ship" is delete plans, promote-then-delete specs only when durable content has a permanent home (or fold them into a living spec), otherwise keep specs with a Superseded / Deprecated annotation and a drift caveat. The shorthand "delete on ship" understates the nuance and leads to over-deletion. The ADR never-delete rule and the OpenSpec fold model both exist precisely to prevent this.