Dependency Upgrade
Trigger phrases: "upgrade dependencies", "outdated packages", "bump versions", "update packages", "keep dependencies current"
When
Staying on current versions as an ongoing practice, or reacting to a CVE. [[dependency-audit]] tells you what is
wrong; this decides what to do about it and does it. It mutates the manifest and the lockfile, so it carries
the same shape as [[db-migration]]: detect the tool, classify by risk, gate the dangerous class behind approval,
apply, verify, roll back on red.
1 — Ask the three separate questions
They are three different problems with three different answers, and the tooling keeps them separate too.
| Question |
Node |
.NET |
Python |
| What has a known CVE? |
npm audit --json |
dotnet list package --vulnerable |
pip-audit |
| What is deprecated? |
npm view <pkg> deprecated |
dotnet list package --deprecated |
registry / project notes |
| What is simply behind? |
npm outdated --json |
dotnet list package --outdated |
pip list --outdated |
The three dotnet list package flags cannot be combined — it is three separate runs, not one.
Deprecated is not an upgrade. No version bump fixes it; the package needs a replacement, which is a design
change with its own review — never fold it into a routine bump.
2 — Classify every target version before touching anything
The risk is not "how old is it", it is "how far does the jump go".
| Class |
Rule |
Approval |
| Security patch |
fixes a known CVE, within the current major |
Do it first, on its own commit — it is the one upgrade whose delay has a cost |
Patch (x.y.Z) |
bugfix only |
Apply, batched |
Minor (x.Y.z) |
additive, backward compatible by promise |
Apply, batched, only if the suite is green afterwards |
Major (X.y.z) |
breaking by definition |
Never automatic. Read the changelog/migration guide, one package per commit, and say what breaks |
| Pinned / transitive-only |
pinned deliberately, or not a direct dependency |
Leave it. Find out why it is pinned before unpinning |
Semver is a promise, not a guarantee: a minor that breaks you is a bug in the package, and your suite is the
only thing that will tell you. That is why the verify step below is not optional.
3 — Apply
- Group by class, one commit per group — a patch batch and a major are not the same change and must not
share a commit. A single commit mixing forty bumps is unreviewable and unrevertable.
- Preview first.
npm audit fix --dry-run before npm audit fix. Never reach for npm audit fix --force
without saying so out loud: it installs semver-major upgrades, which is precisely how this task breaks a
build while claiming to be a security fix.
- The lockfile is part of the change. Commit it with the manifest; a manifest bump without its lockfile is
a change nobody else reproduces ([[dependency-audit]] axis 5).
- Never hand-edit a lockfile. Let the tool regenerate it.
4 — Verify, then decide
Nothing counts as upgraded until it is observed working — install, build, full test suite, and the project's
quality gate. Run the app's real entry path if the change touches runtime, not just unit tests ([[testing]]).
- Green → keep it.
- Red → roll back this group (
git checkout -- <manifest> <lockfile> then reinstall), then either pin, split
the batch to find the culprit, or move that package to its own major-upgrade task. Do not "fix forward" a
routine bump; that turns maintenance into an unplanned feature.
- Re-run [[dependency-audit]] afterwards: an upgrade can introduce a new transitive CVE.
5 — Continuous, without an autonomous loop
"Always current" is a cadence, not an agent that runs by itself — the kit has no timed or self-triggering
loops, because every mutation here needs an approval a loop cannot give. Continuity comes from somewhere that
already has a human gate at the end:
- A scheduled CI job running the step-1 commands and failing on HIGH/CRITICAL ([[ci-pipeline]]).
- Or a bot (Renovate / Dependabot) opening one PR per group — the kit does not reimplement it; the value added
is the policy above deciding which PRs merge on green and which need a human to read a changelog.
- Either way the PR passes through the ordinary gates: build, tests, [[security-scan]], review, approval.
DoD
- Vulnerable, deprecated and outdated were asked as three separate questions, with the command output shown.
- Every applied upgrade is classed patch / minor / major, and no major went in automatically or unexplained.
- Security fixes landed first and separately.
- Manifest and lockfile moved together; no lockfile was hand-edited.
- Build + full suite were observed green after the change, or the group was rolled back and named.
- Deprecated packages are reported as replacement work, not silently bumped.
1---2name: dependency-upgrade3description: Bring dependencies current without breaking the build: find what is vulnerable, deprecated or behind, classify each target version by risk, apply what is safe, verify, and roll back what is not.4---56# Dependency Upgrade78<!-- routing-eval reads this line; it lives in the BODY so the always-on skill LISTING stays inside9 Claude Code's budget (1% of the context window) — an overflowing listing gets descriptions10 truncated or dropped, which strips the very keywords a match depends on. -->11Trigger phrases: "upgrade dependencies", "outdated packages", "bump versions", "update packages", "keep dependencies current"1213## When14Staying on current versions as an ongoing practice, or reacting to a CVE. [[dependency-audit]] tells you what is15wrong; this decides what to do about it and does it. It **mutates the manifest and the lockfile**, so it carries16the same shape as [[db-migration]]: detect the tool, classify by risk, gate the dangerous class behind approval,17apply, verify, roll back on red.1819## 1 — Ask the three separate questions20They are three different problems with three different answers, and the tooling keeps them separate too.2122| Question | Node | .NET | Python |23|---|---|---|---|24| What has a **known CVE**? | `npm audit --json` | `dotnet list package --vulnerable` | `pip-audit` |25| What is **deprecated**? | `npm view <pkg> deprecated` | `dotnet list package --deprecated` | registry / project notes |26| What is simply **behind**? | `npm outdated --json` | `dotnet list package --outdated` | `pip list --outdated` |2728> The three `dotnet list package` flags **cannot be combined** — it is three separate runs, not one.2930**Deprecated is not an upgrade.** No version bump fixes it; the package needs a replacement, which is a design31change with its own review — never fold it into a routine bump.3233## 2 — Classify every target version before touching anything34The risk is not "how old is it", it is "how far does the jump go".3536| Class | Rule | Approval |37|---|---|---|38| **Security patch** | fixes a known CVE, within the current major | Do it first, on its own commit — it is the one upgrade whose delay has a cost |39| **Patch** (`x.y.Z`) | bugfix only | Apply, batched |40| **Minor** (`x.Y.z`) | additive, backward compatible **by promise** | Apply, batched, only if the suite is green afterwards |41| **Major** (`X.y.z`) | breaking by definition | **Never automatic.** Read the changelog/migration guide, one package per commit, and say what breaks |42| **Pinned / transitive-only** | pinned deliberately, or not a direct dependency | Leave it. Find out *why* it is pinned before unpinning |4344Semver is a promise, not a guarantee: a minor that breaks you is a bug in the package, and your suite is the45only thing that will tell you. That is why the verify step below is not optional.4647## 3 — Apply48- **Group by class, one commit per group** — a patch batch and a major are not the same change and must not49 share a commit. A single commit mixing forty bumps is unreviewable and unrevertable.50- **Preview first.** `npm audit fix --dry-run` before `npm audit fix`. Never reach for `npm audit fix --force`51 without saying so out loud: it installs **semver-major** upgrades, which is precisely how this task breaks a52 build while claiming to be a security fix.53- **The lockfile is part of the change.** Commit it with the manifest; a manifest bump without its lockfile is54 a change nobody else reproduces ([[dependency-audit]] axis 5).55- Never hand-edit a lockfile. Let the tool regenerate it.5657## 4 — Verify, then decide58Nothing counts as upgraded until it is observed working — install, build, full test suite, and the project's59quality gate. Run the app's real entry path if the change touches runtime, not just unit tests ([[testing]]).6061- Green → keep it.62- Red → **roll back this group** (`git checkout -- <manifest> <lockfile>` then reinstall), then either pin, split63 the batch to find the culprit, or move that package to its own major-upgrade task. Do not "fix forward" a64 routine bump; that turns maintenance into an unplanned feature.65- Re-run [[dependency-audit]] afterwards: an upgrade can introduce a new transitive CVE.6667## 5 — Continuous, without an autonomous loop68"Always current" is a **cadence**, not an agent that runs by itself — the kit has no timed or self-triggering69loops, because every mutation here needs an approval a loop cannot give. Continuity comes from somewhere that70already has a human gate at the end:71- A scheduled CI job running the step-1 commands and failing on HIGH/CRITICAL ([[ci-pipeline]]).72- Or a bot (Renovate / Dependabot) opening one PR per group — the kit does not reimplement it; the value added73 is the policy above deciding which PRs merge on green and which need a human to read a changelog.74- Either way the PR passes through the ordinary gates: build, tests, [[security-scan]], review, approval.7576## DoD77- Vulnerable, deprecated and outdated were asked as three separate questions, with the command output shown.78- Every applied upgrade is classed patch / minor / major, and no major went in automatically or unexplained.79- Security fixes landed first and separately.80- Manifest and lockfile moved together; no lockfile was hand-edited.81- Build + full suite were observed green after the change, or the group was rolled back and named.82- Deprecated packages are reported as replacement work, not silently bumped.