| name | deprecation-and-migration |
| description | Plan and execute the removal of deprecated APIs without breaking
consumers. Apply when retiring `@deprecated` markers, renaming public
surfaces, or version-gating cleanup work. Triggers on "deprecate",
"remove deprecated", "migration plan", "breaking change", "v3 cleanup".
|
| allowed-tools | Read, Edit, Write, Bash, Grep, Glob |
Deprecation and Migration Skill
When to apply
- Retiring
@deprecated-marked APIs
- Renaming public types, classes, or string-union members
- Removing v-gated cleanup work
- Splitting an over-large public API into focused modules
- Sunsetting a config option, env var, or CLI flag
Skip when:
- The deprecation marker is fresh (< 1 minor version of bake) — let consumers see the warning first
- No replacement exists yet — implement the replacement first, deprecate the old, then migrate
- The change is purely internal (no public-barrel exposure) — that's a refactor, not a migration
Pre-removal checklist (before opening the cleanup PR)
The four-step process
1. Inventory
List every surface touched by the removal — types, schemas, classes, function names, internal callers, public re-exports, test fixtures, doc references.
grep -rn "DeprecatedName" src/ --include="*.ts" | wc -l
grep -rn "DeprecatedName" src/exports/*.ts src/index.ts
grep -rn "DeprecatedName" docs/ skills/
2. Decompose into batches
A deprecation removal touching > ~10 files should be split into batches by blast radius:
| Batch | Scope | Semver |
|---|
| A — Internal-only | Fields/methods never on a public barrel; runtime semantics unchanged | patch |
| B — Typed string-unions | Public union member removal; runtime unchanged | minor |
| C — Public type aliases | Public-barrel type removal; runtime unchanged | minor |
| D — Runtime API | Method/function/return-type changes consumers observe at runtime | major |
Each batch is its own PR. Never bundle batches — the failure mode (one CI gate flake or revert) is much easier when batches are isolated.
3. Per-batch implementation
- Open the branch from current
main. Confirm git status is clean.
- Make the deletion. Run
pnpm typecheck — let the type errors guide you to every internal consumer.
- Update each broken call site to the canonical replacement.
- Run
pnpm lint && pnpm typecheck && pnpm test.
- Add a
.changeset/<issue>-batch-<X>-<name>.md with semver and migration recipe.
- Sweep
docs/ for stale references (auto-regenerated docs like TypeDoc HTML refresh on next build).
- Open the PR. Run
consensus_vote QA before merge per the standard protocol.
4. Post-merge
- Verify the published artifact:
npm view <pkg> version against package.json version.
- Watch for the publish race (release-changeset-race.md) — package.json ahead of npm means the workflow re-entered PR mode.
- Close the parent issue/epic only after the artifact is live on npm, not just merged.
Anti-rationalization — Deprecation removal
| Excuse | Counter |
|---|
| "It's deprecated, so just delete it" | Deprecation is a contract with consumers. The marker says "this still works, but plan to migrate." Removal needs replacement + bake + migration recipe. |
| "Nobody uses it" | Verify with grep and npm view <pkg> dist.shasum-then-search-on-sourcegraph if you must be sure. "Nobody uses it" without evidence is a guess. |
| "I'll fix the broken consumers later" | Internal callers should be migrated in the same PR as the removal. Deferring it puts you in a state where main doesn't compile. |
| "It's just a rename" | Renames of public types are TypeScript-typed-only breaks. Minor version. Migration recipe required. |
| "v3.0 will fix it" | (Per nexus-agents experience with epic #2368: this is often a way to indefinitely defer work. Re-evaluate the v-gate periodically — many v3-gated cleanups can ship as typed-only minor breaks once they bake.) |
| "The CHANGELOG will explain" | The CHANGELOG is read by maybe 5% of consumers. Migration recipes need to be self-discoverable from a TypeScript compile error pointing at the new symbol. |
Red flags
- Removal PR with no
.changeset/*.md file
- Removal PR that doesn't compile internally (deferred caller migration)
- Removal of a symbol that has < 1 minor version of
@deprecated bake
- Removal without checking
src/exports/*.ts for public reach
- "Migration recipe" that's vague ("use the new API" with no diff block)
- Bundling multiple batches into one PR for "convenience"
Verification checklist
Reference incident
Epic #2368 (2026-05-04) retired the v3.0 deprecation gate by shipping all 31 @deprecated markers in 2.x as 5 batches:
- Batch A (#2369) — internal-only removals → patch
- Batch B (#2371) —
tech_lead AgentRole removal → minor
- Batch C (#2372) — public-barrel type removal → minor
- BaseAgent.setState removal (#2377) → patch
- agents/experts/task-analyzer.ts removal (#2378) → minor
- OrchestratorType discriminator rename (#2380) → minor
Lessons from that cycle informed this skill — including the publish-race that produced version-skip publishes (docs/ops/release-changeset-race.md).