# Plan Retirement

> Partial Skill: invoke by name only — the SDD Doctrine loop's last retro step — the gated, idempotent tracked deletion of a retired mission plan. Invoked by the doctrine-loop Scanner, not user-triggered; the clearance contract lives in the body + README.

- Skill: `cyberuni/plan-retirement` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add cyberuni/plan-retirement`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cyberuni/plan-retirement/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: cyberuni (https://skillmd.com/u/cyberuni)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cyberuni/plan-retirement

---


# SDD Plan Retirement

The Doctrine loop's **last retro step** (`sdd:doctrine-loop`; the provenance shape is
`sdd:combat-log-governance`). Because plans are **tracked** (committed with
the work, not gitignored), a retired plan leaves the tree by a deliberate **tracked deletion** —
never a gitignore side effect. This skill carries a self-contained `.mts` sweep that, for each
cleared `<cr-ref>`, deletes that CR's whole **transient artifact set**: the plan pair
(`<cr-ref>.plan.md` + `<cr-ref>.log.jsonl`) plus its **transient CR-level planning briefs**
(`<cr-ref>.design.md`, `<cr-ref>.operations.md`, `<cr-ref>.evidence.md`) from `.agents/plans` —
so a retired CR leaves no orphan behind.

## The transient briefs ride along, never gate

`design.md` / `operations.md` / `evidence.md` are optional, cr-ref-scoped planning briefs that
share the plan's lifetime. They retire alongside the plan pair but do not participate in the
retirement decision. Two boundaries:

- **They do not widen the distilled gate.** The gate keys on the combat log's presence only — a
  cr-ref with briefs but no `log.jsonl` still retires without a distilling `strategy` entry. A
  brief owes no distillation (its content was consumed by the mission itself).
- **They do not anchor presence.** `<cr-ref>.plan.md` is the sole presence signal. A brief
  without a `plan.md` is left untouched.

## Distill and delete are decoupled

- **Distill (early).** At `→ implemented`, the Scanner reads the concluded combat log and distills
  recurring `cause`s into the ledger's `strategy` lines (`sdd:doctrine-loop`).
- **Delete (late).** This sweep runs as a **separate, later** step, gated on source = `done`/merged
  **and** the plan distilled. Never delete an un-distilled plan (the retro never ran).

## The clearance boundary — split by verifiability

The two gating signals split by what the sweep can check itself:

- **source = `done`/merged** — the **caller's judgment**: query the source natively (`github-NN` → GH
  issue, `asana-<gid>` → Asana, `local-<slug>` → the local store); needs network/`gh`. The caller
  passes the source-cleared set via `--retire`. `sdd:doctrine-loop`'s Scanner is the standard
  caller: during its pass it cross-checks each brief's own `todos-all-done` against `source-closed`
  and only passes through a cr-ref where **both** agree terminal — a disagreement (source closed
  but the brief's own todos are not all done, or the reverse) is held back and surfaced as a
  flagged finding for a human, never passed through on source alone.
- **distilled** — **verified mechanically by the sweep**: a `strategy` entry with `distills ==
  <cr-ref>` must exist in the project ledger (`--ledger`). The sweep keys on the structured
  `distills` field, **never** a `<cr-ref>` that appears only in a strategy's `evidence`
  cross-references, and an **unratified** distilling entry still counts
  (`sdd:combat-log-governance`). Its absence is **fail-closed** — but only when a combat log
  **exists**: a cr-ref whose `<cr-ref>.log.jsonl` was never written (a non-gated mission — hand-run,
  chore-tracked, investigation — runs no gate cycle and emits no correction) has **nothing to
  distill**, so it retires on clearance + presence alone. The fail-closed leaves an existing,
  undistilled log's plan intact so its distillation can still be drafted.

Leaving the distilled half to the caller once let a plan + combat log be deleted before any
distillation existed (the evidence the distill was meant to preserve). Because the check is local,
the sweep does it itself. Only the genuinely non-local judgment (source status) stays with the caller.

## Run the sweep

```bash
node "<skill>/scripts/retire-plans.mts" \
  --root .agents/plans \
  --ledger .agents/specs/<project>/ledger \
  --retire github-34,asana-7 [--dry-run]
```

- **`--ledger <dir>`** points at the project's ledger directory (the `ledger/` sibling of the root
  `spec.md`). **Required for any deletion** — omit it (or an unreadable dir) and the sweep
  fail-closes: nothing is deleted (the no-log branch only applies once a ledger is present to consult).
- Deletes the transient artifact set (`<cr-ref>.plan.md`, `<cr-ref>.log.jsonl`,
  `<cr-ref>.design.md`, `<cr-ref>.operations.md`, `<cr-ref>.evidence.md`) only for a `<cr-ref>` that
  is cleared (`--retire`) **and** present on disk (`<cr-ref>.plan.md` exists) **and** either
  distilled (a `strategy` with `distills == <cr-ref>` in `--ledger`) **or** has no combat log to
  distill (no `<cr-ref>.log.jsonl` on disk). Each brief is deleted only if present; an absent one is
  a no-op, same as the missing log half.
- **Fail-closed** — a plan not named in `--retire`, or whose combat log **exists** but has no
  distilling ledger entry, is never touched (and neither are its briefs).
- **Idempotent** — a cleared `<cr-ref>` with no plan on disk (already retired, or an open CR the
  caller declined to clear) is a no-op, even if a brief for it exists; the sweep is safe to re-run.
- `--dry-run` prints the planned deletions without touching the tree.

When `node` is absent, an agent performs the same decision by hand: for each cleared `<cr-ref>`, if
its `<cr-ref>.log.jsonl` **exists**, **first confirm a `strategy` entry with `distills == <cr-ref>`
exists in the project ledger** (not a mere `evidence` mention; unratified still counts) — if none,
skip it. A cr-ref with **no** `log.jsonl` has nothing to distill and needs no such entry. Only then
delete `<cr-ref>.plan.md`, `<cr-ref>.log.jsonl`, `<cr-ref>.design.md`, `<cr-ref>.operations.md`, and
`<cr-ref>.evidence.md` if present, touching nothing else — and only if `<cr-ref>.plan.md` is
present in the first place (a brief alone never triggers deletion).

