How WorldScript Studio reviews and merges Dependabot PRs. Written 2026-08-20 after a real incident (below) showed that patch-level auto-merge isn't safe when a single upstream release spans multiple independently-versioned references in this repo. Every Dependabot PR now gets manual review before merging β there is no auto-merge path anymore.
On 2026-08-20, github/codeql-action released v4.37.7. Dependabot opened four separate PRs
(#410, #413, #416, #418), one per uses: subpath this repo references
(init, autobuild, analyze, upload-sarif) β each is a distinct dependency from Dependabot's
point of view, even though they all live in one upstream repo and must be version-locked together.
The previously-existing dependabot-automerge.yml workflow auto-merged all four independently the
moment each one's own CI went green, because each is a textbook semver-patch bump. Merging any
one of them alone breaks CI: github/codeql-action/analyze hard-fails with Loaded a configuration file for version X, but running version Y if its pinned version differs from
init's β confirmed on PR #410's own CI run before it was caught.
Fix applied:
- All four PRs closed; the four references bumped together in one manual commit.
.github/dependabot.yml'sgithub-actionsentry now has acodeql-actiongroup (github/codeql-action*) so a futurecodeql-actionrelease opens one PR touching all matchinguses:lines, not one per subpath.dependabot-automerge.ymlremoved outright β see Why no auto-merge.
This is the general failure mode to watch for: any action, npm package, or Cargo crate that this
repo references via more than one distinct uses:/import path from the same upstream repo is a
grouping candidate, not just codeql-action. Re-run the audit in
Detecting future grouping candidates whenever a new
workflow or dependency is added.
| Ecosystem | directory |
Groups configured | Notes |
|---|---|---|---|
| npm | / |
react (react/react-dom/@types), dev-tooling (vitest/playwright/storybook/biome/stryker) |
Root workspace; open-pull-requests-limit: 10 |
| cargo | /src-tauri |
tauri-deps (tauri*/wry/tao) |
Tauri desktop backend; open-pull-requests-limit: 5 |
| cargo | /crates |
none yet | Wave 2 Rust Core workspace (crates/worldscript-project); currently only serde/serde_json β add a group if this workspace grows a same-repo-multi-path dependency |
| github-actions | / |
codeql-action (github/codeql-action*) |
See incident above |
All four entries share cooldown: default-days: 7 (matches .npmrc's
minimum-release-age=10080 for npm; applied uniformly to cargo/github-actions too so a freshly-cut
release doesn't land in a PR before it's had a week to get pulled if broken).
dependabot-automerge.yml (added 2026-07-28, removed 2026-08-20) auto-merged
version-update:semver-patch PRs once CI was green, to stop PRs stacking up during commit gaps. The
codeql-action incident showed the semver-patch signal alone isn't sufficient: a patch bump can still
be part of a larger multi-reference change that only makes sense atomically. Since there's no cheap
way to detect "this dependency has sibling references that must move with it" other than the
grouping config itself (which only covers what's already been identified), the safer default is
manual review for everything, with the grouping config as the primary defense against the specific
failure mode above.
If PR backlog becomes a problem again, prefer widening the groups: blocks (cheap, targeted) over
reinstating blanket auto-merge.
| Semver level | npm | cargo | github-actions |
|---|---|---|---|
| Patch | CI green is sufficient | CI green is sufficient | CI green and confirm no sibling uses: path for the same repo was left behind (see incident) |
| Minor | CI green and a changelog skim for breaking changes despite semver (JS ecosystem doesn't always honor semver strictly) | CI green and changelog skim | CI green and changelog skim; same sibling-reference check as patch |
| Major | Full manual review β changelog, migration guide, likely held for a dedicated PR with its own test plan | Same | Same |
"CI green" always means the full suite for that PR (Quality Gate Γ2, Build, E2E, E2E Deep
Coverage, Storybook, Lighthouse where applicable) β not just the required-check subset. See
PR-CI-MERGE-WORKFLOW.md for the general "wait for advisory jobs too"
rule; it applies to dependency PRs exactly as it does to feature PRs.
The 2026-08-20 consolidation review found 19 entries in src-tauri/osv-scanner.toml, all
currently sharing the 2026-11-30T00:00:00Z review deadline. 2026-09-08 update: two more npm
entries were added under the same deadline β a second, distinct extract-zip 2.0.1 advisory
(GHSA-7pqw-9j4j-h8q3, not an alias of the original) and adm-zip 0.6.0
(GHSA-vwc7-r8mq-g2x9) β bringing the total to 21. This is a synchronized review cliff, not
evidence that the risks were extended or resolved; src-tauri/osv-scanner.toml is the dynamic
source of truth for the exact count and entries β treat this list as a cluster summary, not a
substitute for reading the file. The entries remain grouped by their real reason: legacy GTK3/WebKit
bindings, build-time proc-macro-error/paste, archived Unicode data crates, both extract-zip
advisories, and adm-zip.
No ignore deadline was extended in this pass. extract-zip remains a transitive dependency of
@lhci/cli's own Puppeteer stack (lighthouse -> puppeteer-core -> @puppeteer/browsers ->
extract-zip, verified via pnpm audit β not this repo's separate Playwright devDependency)
with no patched release and no production-runtime footprint; it must still be
rechecked before expiry and removed as soon as an upstream fix or dependency-path change makes that
possible. Review each cluster against current upstream status before changing any deadline.
These need more than a changelog skim because of documented quirks elsewhere in this repo:
| Dependency | Why it needs extra care |
|---|---|
vite |
Production build uses rolldown, not esbuild/rollup; verify the current vite.config.ts and package.json sideEffects contract. Rolldown ignores rollupOptions.treeshake and ties tree-shaking to package.json "sideEffects" β a Vite bump that changes how a dependency's sideEffects field is honored can produce a blank-screen prod build that CI's vite dev-based E2E suite won't catch. Run pnpm run build && pnpm run smoke:prod after any Vite bump, not just CI green. |
zod |
Has a repo-local patch (patches/zod@4.4.3.patch, forces "sideEffects": true) applied via pnpm patch. A version bump may need the patch re-applied/re-verified against the new version. |
react / react-dom |
Already grouped β must stay in lockstep, split bumps cause version-mismatch errors at test time. |
tauri* / wry / tao |
Already grouped (tauri-deps) β same lockstep concern for the desktop backend. |
github/codeql-action* |
Already grouped β see incident above. |
@biomejs/biome |
Check the installed package version against biome.json's $schema URL after every bump; the schema URL is versioned independently from the npm dependency declaration. |
Any WASM/WebGPU-adjacent package (@huggingface/transformers, @mlc-ai/web-llm, onnxruntime-web) |
These ship in vendor-* SW-excluded chunks (vite.config.ts globIgnores) β verify a version bump didn't change the package's exported chunk structure in a way that breaks the manual-chunk mapping. |
Per standing policy for this session: do not stack merges. For each Dependabot PR, in order:
- Confirm the PR's own CI is fully green (see triage matrix above for what "ready" means at that semver level).
- Merge (squash).
- Wait for the resulting push-triggered CI run on
main(not just the PR's own run β a squash merge produces a new commit SHA that reruns the full pipeline) to reach a concluded, successful state. - Only then move to the next PR.
This is slower than merging a batch back-to-back, but avoids diagnosing a main failure against a
pile of unrelated changes, and avoids the specific "PR A's CI was green against a main that PR B's
merge just changed underneath it" class of race.
Run this whenever a new workflow file is added, or periodically as a health check β it surfaces any action referenced via more than one subpath of the same repo (the exact shape of the codeql-action incident):
grep -rhoE "uses: [a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+(/[a-zA-Z0-9._/-]+)?@[a-f0-9]+" \
.github/workflows/*.yml \
| sed 's/uses: //' \
| sed -E 's#(/[^/]+)$##' \
| sort | uniq -c | sort -rnA count > 1 for the same org/repo line is not automatically a problem (e.g. Swatinem/rust-cache
is used twice across src-tauri and crates jobs, each independently pinned β that's fine, they
don't need to match each other's version). It only matters when the multiple uses: lines are
subpaths of the same action that must agree on version at runtime, as codeql-action's
init/analyze do. Judgment call per hit, not a mechanical rule.
If a Dependabot PR's CI fails for a reason unrelated to the bump itself (flaky test, infra), retry
via re-running the failed job before assuming the dependency is at fault. If it's a genuine break
caused by the new version, do not force-merge β either pin to the last-known-good version explicitly
(pnpm.overrides / [patch] per ecosystem, documented in AUDIT.md) or leave the PR open with a
comment explaining the blocker until upstream fixes it.