dependency-upgrade · git:20260728.3499107 · 2026-07-28 · sha256 9f05e8dfaec8bfd9
dependency-upgrade git:20260728.3499107A
Immutable. This exact content is served forever at /api/v1/blob/9f05e8dfaec8bfd9.
--- name: dependency-upgrade description: | 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. Trigger phrases: "upgrade dependencies", "outdated packages", "bump versions", "update packages", "keep dependencies current" --- # Dependency Upgrade ## 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.