| name | dependency-upgrade |
| description | Plan, batch, and verify dependency upgrades safely. Triages outdated packages into risk tiers, upgrades in order (dev/minor/patch first, runtime majors last), verifies each batch before moving on, and produces an auditable commit sequence. Use when asked to "upgrade deps", "bump packages", "update node_modules", "fix vulnerabilities", "upgrade React/Node/TypeScript", or after `/gsd start dep-upgrade`. Complements the dep-upgrade workflow template with execution-level rigor. |
Turn a pile of outdated packages into a series of small, verifiable upgrades with clean commits. The deliverable is an ordered upgrade plan, executed with verification between batches, and a summary that flags anything risky for follow-up. No big-bang upgrades.
gsd-pi ships a `/gsd start dep-upgrade` workflow template (`src/resources/extensions/gsd/workflow-templates/dep-upgrade.md`) that structures the phases: assess โ upgrade โ fix breaks โ verify. This skill is the execution-level detail inside the upgrade phase โ how to batch, how to verify, how to recover from a breaking upgrade without losing the good ones.
Invocation points:
/gsd start dep-upgrade workflow is running
- Ad-hoc "bump the deps" request
- Security advisory response (CVE in a direct dep)
- Framework major-version update (React 18 โ 19, Node LTS bump)
- Monthly hygiene pass
<core_principle>
BATCH BY RISK, NOT BY LAZINESS. npm update is a shortcut that blends safe and risky changes into one commit. When something breaks, you can't tell which dep caused it. Always separate: patches and dev-deps first, minors next, majors individually.
VERIFY BETWEEN BATCHES. Run the test suite after every batch. Don't stack five batches and hope. If a batch breaks something, you need to know which one.
ONE MAJOR PER COMMIT. Major version bumps are where real breakage lives. Keep them isolated so the commit history tells the truth.
</core_principle>
Step 1: Take inventory
Run the ecosystem's outdated check. Capture output, don't act on it yet:
- Node:
npm outdated or pnpm outdated or yarn outdated
- Python:
pip list --outdated or uv pip list --outdated
- Rust:
cargo outdated
- Ruby:
bundle outdated
- Go:
go list -m -u all
Also capture:
npm audit (or equivalent) โ security advisories
- Current versions of language runtime, build tool, test runner
Step 2: Classify each outdated package
For each outdated package, note:
- Type: direct dep / dev-dep / transitive
- Kind of bump: patch (1.2.3 โ 1.2.4), minor (1.2.3 โ 1.3.0), major (1.2.3 โ 2.0.0)
- Risk: Low (dev-dep, patch), Medium (runtime minor, dev-dep major), High (runtime major, framework, TypeScript compiler)
- Blast radius: How many files in the repo import it? (Rough count via
rg.)
- Has known breaking changes: Check the CHANGELOG or release notes for any major bumps โ do NOT skip this, and do NOT infer "probably fine."
Step 3: Build the batch plan
Order:
- Batch 1 โ Dev dep patches and minors. Lowest risk, fastest feedback. Includes linters, formatters, type definitions, test runners.
- Batch 2 โ Runtime patches. All patch bumps to runtime deps together.
- Batch 3 โ Runtime minors. One batch per semantic group if any minor carries risk notes; otherwise one batch for the rest.
- Batch 4+ โ Each major, individually. One commit per major. Framework bumps (React, Next.js, Vue, TypeScript) each get their own commit.
- Batch N โ Language runtime. If bumping Node/Python/Rust, last โ it changes the compile/run env for everything.
Skip (for now):
- Anything with "unreleased", "pre", "beta", "rc" tags unless the user explicitly wants pre-release tracking
- Deps you know are blocked by another dep (e.g., can't bump X until Y supports it)
Present the plan to the user. One round of adjustment. Then execute.
Step 4: Execute a batch
For each batch:
- Start from a clean working tree.
git status โ no uncommitted changes.
- Apply the upgrades using the ecosystem command (
npm install package@version ..., uv add โฆ, cargo update -p โฆ).
- Regenerate the lockfile fully if necessary.
- Build. Run the project's real build โ not just typecheck.
- Test. Run the full suite. Not a scoped subset.
- Lint.
lsp diagnostics on changed files + the project linter.
- Smoke-test the app if it has a running surface (dev server, CLI command).
If any step fails:
- Investigate before moving on. Don't skip the batch.
- If the failure is trivial (a rename, a deprecated import), fix it in the same commit โ the upgrade broke it, the upgrade commit fixes it.
- If the failure is non-trivial (an API you heavily depend on was removed), either pin back or split: upgrade the other deps in the batch in this commit, and handle the problem dep separately in Step 5.
Commit with a precise message:
deps: bump <scope> โ <what changed in one line>
- package-a: 1.2.3 โ 1.2.9 (patch)
- package-b: 2.1.0 โ 2.4.0 (minor โ no breaking changes in CHANGELOG)
- package-c: 3.0.1 โ 3.0.2 (patch)
Verified: npm test (84/84 pass), npm run build exit 0, lsp diagnostics clean.
Step 5: Handle stuck majors
For each major that breaks something:
- Read the migration guide โ do not guess at the API changes.
- Scope the blast radius โ
rg for the symbols that changed.
- Decide: upgrade now or defer? If the migration is a weekend's work and not urgent, file a
/gsd start refactor follow-up and pin the current major for now.
- If upgrading now: branch the work as its own slice (
S##). The dep upgrade is only one task; the migration is the rest of the slice.
- Verify end-to-end after migration. The test suite alone may miss behavior changes.
Step 6: Summary
After all batches, produce a rollup:
## Dependency Upgrade โ <date>
### Completed
- Batch 1 (dev patches): 12 packages โ commit abc1234
- Batch 2 (runtime patches): 5 packages โ commit def5678
- Batch 3 (runtime minors): 3 packages โ commit ...
- Batch 4 (React 18 โ 19): commit ... โ required refactors in <files>
### Deferred
- typescript 5.3 โ 5.7 โ requires updating 40+ decorator usages. Filed as M005.
- vite 4 โ 5 โ config shape changed. Owner assigned: <user>.
### Still outdated (intentional)
- <package> โ pinned because <reason>
### Security advisories
- <CVE> resolved by <package upgrade>
- <CVE> still open โ not exploitable in our usage (see comment in package.json)
Append to .gsd/KNOWLEDGE.md any non-obvious gotcha from the upgrade (API changes that tripped you up, migration rituals for this codebase).
<anti_patterns>
npm update blindly. Mixes safe and risky in one undiagnosable commit.
- Multiple majors in one commit. When it breaks, you don't know which.
- No verification between batches. You stack failures until nothing works.
- Skipping the CHANGELOG. "It's just a minor" โ until it isn't.
- Force-merging a failing batch with
--force. Leave it broken or roll back; don't lie to git.
- Ignoring
--legacy-peer-deps warnings. They're telling you the dep graph is incoherent.
</anti_patterns>
<success_criteria>
</success_criteria>