You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .claude/skills/bump-dependencies/SKILL.md
+48-1Lines changed: 48 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
---
2
2
name: bump-dependencies
3
-
description: Run the bundled bump-dependencies.sh to bump plugin dependencies, then walk every non-patch version change to surface breaking changes and apply any plugin-side updates needed. Use when the user says "bump dependencies", "upgrade dependencies", "update dependencies", or wants to refresh a single plugin's dependencies.
3
+
description: Run the bundled bump-dependencies.sh to bump plugin dependencies, then walk every non-patch version change to surface breaking changes and apply any plugin-side updates needed. Also audits every workspace for security advisories (audit-dependencies.sh) and fixes them via override floors or direct-dependency pins. Use when the user says "bump dependencies", "upgrade dependencies", "update dependencies", "audit dependencies", "check for vulnerabilities", or wants to refresh a single plugin's dependencies.
4
4
---
5
5
6
6
# Bump plugin dependencies and review breaking changes
@@ -69,6 +69,52 @@ Goal: bump the dependencies via the bundled script, then for every non-patch jum
69
69
70
70
Don't commit. The user runs `git commit` themselves so they can review the diff.
71
71
72
+
## Reconcile `pnpm-workspace.yaml` overrides
73
+
74
+
The script bumps `package.json` and `pnpm-lock.yaml` but never touches the `overrides:` block in each plugin's `pnpm-workspace.yaml`. Overrides win during resolution, so any dependency pinned there is silently capped — `package.json` shows the new version while the lockfile keeps the old one. After the bump, audit the overrides:
75
+
76
+
1.**Find the mismatch.** For every dependency that appears in both the bump diff and an `overrides:` block, compare declared vs resolved:
77
+
78
+
```sh
79
+
grep -E "^ <dep>:"<plugin>/pnpm-workspace.yaml # the pin
80
+
pnpm -C <plugin> list <dep># what actually resolved
81
+
```
82
+
83
+
An exact pin (`next: '16.2.6'`) caps the dep; a `^`-floor (`mongoose: '^8.22.1'`) only sets a minimum and won't block an upgrade.
84
+
85
+
2.**Bump exact-pin dedup overrides; never remove them.**`next` is pinned to one exact version on purpose: it forces a single `next` (and therefore one React) across the plugin root, the `dev*/` apps, and the `@payloadcms/*` peers. Remove the pin and the `@payloadcms/*` multi-window peer range (`>=16.2.6 <17`) lets a transitive dep pull a second `next`, so the tree resolves two versions at once — the duplicate-deps Payload admin crash (`Cannot destructure property 'config' of useConfig()`; see the `fix-payload-duplicate-deps` skill). Bump the pin to match the new `package.json`, reinstall, and confirm one version remains:
86
+
87
+
```sh
88
+
pnpm -C <plugin> list next | grep -oE 'next@[0-9.]+'| sort -u # must be a single line
89
+
```
90
+
91
+
3.**Audit `^` security floors for redundancy.** A floor exists because some transitive dep once pulled a vulnerable version. Once that dep updates its own range, the floor is dead weight. Test it empirically: strip the floor lines (keep the dedup pins), reinstall every plugin, and compare each package's lowest resolved version to its floor.
92
+
- natural resolution **≥ floor** → redundant, drop the override.
93
+
- natural resolution **< floor** → still load-bearing, keep it (removing it puts a below-floor version back in the lockfile).
94
+
95
+
A downward drop after stripping is proof the floor does real work: the pre-strip lockfile held the forced safe version, so re-resolving lower means the natural range sits below the floor. A bump as large as a Payload minor rarely retires a floor — at the 3.84→3.85 bump, even `mongoose` (whose `@payloadcms/db-mongodb` consumer now requires `8.22.1` directly) still resolved `8.15.1` from another consumer, so every floor stayed. Restore the tree with `git stash` (not `git checkout -- .`, which is denied) once measured.
96
+
97
+
## Audit for vulnerabilities and fix them
98
+
99
+
A version bump is the natural moment to clear advisories. Each plugin is its own workspace, so audit per lockfile and consolidate.
100
+
101
+
1.**Run the consolidated audit.** The helper next to this skill runs `pnpm audit` in every folder with its own `pnpm-lock.yaml` and prints one deduplicated table (one row per advisory, with the count of affected plugins) — what a per-folder loop can't show:
102
+
103
+
```sh
104
+
./.claude/skills/bump-dependencies/audit-dependencies.sh # any advisory → exit 1
105
+
./.claude/skills/bump-dependencies/audit-dependencies.sh high # CI gate: only high+ fails
106
+
```
107
+
108
+
The same advisory usually spans many plugins (shared Payload/tooling deps), so fix it once per package, not once per plugin.
109
+
110
+
2.**Prefer the root-cause fix — update the owning dependency before reaching for an override.** Run `pnpm -C <plugin> why <pkg>` to find which direct/parent dependency pulls the vulnerable version. If a newer release of that owner requires the patched range, bumping the owner clears the advisory _and_ leaves no override behind — the cleanest fix, and it tracks upstream instead of pinning against it. The bump step (`bump-dependencies.sh` → `pnpm up --latest`) already does this for **direct** deps, so within the full flow the survivors are usually transitive-only with an already-current owner — those genuinely need step 3/4. Reach for an override only after confirming the owner can't move (already latest, or its range still spans the vulnerable version). An override is a forceful pin: it masks the real graph and accumulates, so it's the fallback, not the default.
111
+
112
+
3.**Otherwise, fix with an override floor — the same mechanism as the security floors above.** For each advisory add (or raise) `pkg: '^<patched>'` in every plugin's `overrides:` block. Watch for a floor that _caps below_ the patched version: `esbuild: '^0.27.0'` excludes the `0.28.1` fix, so it has to move to `^0.28.1`, not just gain a sibling.
113
+
114
+
4.**When the override won't move a transitive, pin it as a direct devDependency.** pnpm applies overrides during _fresh_ resolution but will not re-resolve an already-locked transitive to satisfy a changed override — `install`, `install --force`, `dedupe`, and `update <pkg>` all leave it in place (the lockfile even records the new override next to the old resolved version). The reliable escape hatch is to add the package as a direct `devDependency` at the patched range in the plugin that pulls it; a direct dep always resolves to its declared range, and the transitive consumer dedupes onto it. Example: `vite` (pulled by `vitest`, peer `^6 || ^7 || ^8`) stayed on the vulnerable line under a `vite: '^8.0.16'` override, so `"vite": "^8.0.16"` went into chat-agent and vercel-deployments `devDependencies` next to `vitest`.
115
+
116
+
5.**Reinstall, re-audit to zero, then verify the toolchain.** Forcing a build-tool version is exactly where things break — `esbuild` minors (`0.x` = breaking by convention) and a `vite` major shift the transform/bundle path. After the fix, re-run the audit _and_`typecheck` + `test` for every plugin, plus `build` for any plugin that uses the tool directly (e.g. astro's `esbuild` + `vite-plugin-dts`). Don't claim the advisories are cleared until the audit is empty and the suites are green.
117
+
72
118
## Heuristics for what to read carefully vs skim
73
119
74
120
-**Payload core (`payload`, `@payloadcms/*`)** — read every minor's release notes; this repo's plugins live or die by Payload API stability. Pay attention to field-config, plugin API, and admin component prop changes.
@@ -83,3 +129,4 @@ Goal: bump the dependencies via the bundled script, then for every non-patch jum
83
129
- Script returns `FAILED` for any plugin → report and stop, don't try to fix it inline.
84
130
- A breaking change requires an architectural decision (e.g. Payload removes a hook the plugin depends on) → write up the options and ask before picking one.
85
131
- Peer-dependency range needs to widen or narrow to track upstream → confirm with the user; this changes the plugin's public install matrix.
132
+
- An exact-pin override (e.g. `next`) needs to cross a major, or a security floor's removal can't be proven redundant by the strip-and-reinstall test → confirm with the user before changing the `overrides:` block.
0 commit comments