Fix: align dev's express and @types/express ranges with core - #637
Open
AmaadMartin wants to merge 2 commits into
Open
Fix: align dev's express and @types/express ranges with core#637AmaadMartin wants to merge 2 commits into
AmaadMartin wants to merge 2 commits into
Conversation
added 2 commits
August 4, 2026 07:05
core declares express ^4.22.1 / @types/express ^4.17.25 while dev declared ^4.21.2 / ^4.17.21. Both workspaces already resolve the single hoisted express@4.22.2 and @types/express@4.17.25, so dev's ranges were a stale, untrue floor that ships in the published @google/adk-devtools manifest. Align up to core: core's floor is the newer one, and lowering it would weaken the published floor of @google/adk for no benefit. The lockfile delta is the two mirrored range strings under packages/dev; no resolved version changes.
Nothing in the repo prevented the express range drift from reappearing, which is the actual root cause. Index every dependencies/devDependencies entry of the workspaces named by the root manifest and fail when one package name is declared with more than one range string, naming every site. peerDependencies are excluded (a peer range is deliberately wider), as is the root manifest (tooling root, and it carries separately-tracked eslint/prettier drift).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Please ensure you have read the contribution guide before creating a pull request.
Link to Issue or Description of Change
N/A — no public issue is tracking this.
Problem:
coreanddevboth shipexpressas a runtime dependency, but declared different semver ranges for it, and the same for its type package:core/package.jsondev/package.jsonexpress(dependencies)^4.22.1^4.21.2@types/express(devDependencies)^4.17.25^4.17.21expresswas the only divergent runtime dependency in the repo — every other package declared by more than one workspace already uses an identical range string (js-yaml^4.1.1,winston^3.19.0,zod^4.2.1,@google/adk^1.5.0).This is not a live runtime defect: the lockfile hoists exactly one
express@4.22.2and one@types/express@4.17.25, and4.22.2satisfies both ranges. The defect is in the declared contract, and it has two consequences:dev/package.jsonreasonably concludes devtools was deliberately kept compatible with express 4.21. It was not; the range is simply stale, and because whichever workspace resolves first wins the hoist, the divergence is invisible in practice and stays wrong indefinitely.@google/adk-devtoolsis published to npm withpackage.jsonin itsfilesarray, so the stale floor is part of a released package's public contract.Solution: Align
devup tocore(express: ^4.22.1,@types/express: ^4.17.25) and regenerate the lockfile. Aligning up rather than down is deliberate:core's floor is the newer one, the installed tree already runs4.22.2, and loweringcorewould weaken the published floor of@google/adkfor no benefit.Not a breaking change; patch-level.
expressis a regulardependenciesentry of@google/adk-devtools, not a peer dependency, so narrowing it cannot cause anERESOLVEin a consumer install. A consumer already inheritsexpress@^4.22.1transitively, because@google/adk-devtoolsdepends on@google/adk@^1.5.0which demands it — so the effective resolution downstream is unchanged; only the manifest now states it honestly.@types/expressis a devDependency and is never installed by consumers.The change also adds the missing regression guard. Nothing in the repo prevented this drift from reappearing, which is the actual root cause.
tests/integration/workspace_manifests/dependency_alignment_test.tsderives the workspace list from the root manifest'sworkspacesarray, indexes everydependencies/devDependenciesentry, and fails when one package name is declared with more than one range string — naming everyworkspace (field) -> rangesite so the fix is visible without opening four files.peerDependenciesare excluded (a peer range is deliberately allowed to be wider than the dependency range satisfying it) and the root manifest is excluded (it is the tooling root, and it carries separately-trackedeslint/prettierdrift that is a different question).Out of scope, stated explicitly: the root manifest also diverges from
devoneslint(^9.37.0vs^8.57.0) andprettier(^3.6.2vs^3.2.5). That is dev-tooling-only and materially different (thedev-local lint scripts may be dead config); it is tracked separately and is not touched here. No file undercore/src,dev/src, orintegrations/srcis modified, and no coverage threshold is touched.Collision check (required before implementation): all 536 open PRs on the fork were listed and every plausibly adjacent one was diffed. No PR changes an
expressor@types/expressrange string, and none adds a cross-workspace range-alignment guard, so this is not a duplicate. Three PRs overlap by touching the same manifest lines without changing any range: #562 and #382 relocatedev's@types/expressfromdevDependenciestodependencies(keeping^4.17.21), and #292 does the same forcore(keeping^4.17.25). This PR is not stacked on any of them, deliberately: those three are mutually competing siblings (two of them make the samedevedit in different ways), so stacking would import a contested diff and pick a winner among them. This change composes with whichever lands, because the guard indexes by package name across both dependency fields — it does not care which field an entry lives in — and the manifest edit is a value change that survives the field move as a trivial conflict resolution.Testing Plan
Please describe the tests that you ran to verify your changes. This is required for all PRs that are not small documentation or typo fixes.
Unit Tests:
[x] I have added or updated unit tests for my change.
[x] All unit tests pass locally.
New guard test (run targeted, not via the full suite):
Existing express-dependent suites, re-run unmodified — no existing test was edited, skipped, or deleted:
Proof the new test can fail (mutation). Both assertions were run against mutated inputs and confirmed to fail:
git checkout HEAD~1 -- dev/package.json, restoring^4.21.2/^4.17.21) — fails with exactly the two expected violations and no others:workspacesarray — the anti-vacuity assertion fires, so a manifest-reading change that silently scans nothing cannot pass:Both mutations were reverted and the suite re-run green.
Coverage: the production change is manifest-only, so there is no new production line to cover; the coverage
includeglobs arecore/src,dev/src,integrations/srcand the new test imports no production module, so the configured thresholds are unaffected and were not touched.Manual End-to-End (E2E) Tests:
Please provide instructions on how to manually test your changes, including any necessary setup or configuration.
npm ls express— a single dedupedexpress@4.22.2for both packages, and the MCP SDK keeps its own nestedexpress@5.2.1as before:npm ls @types/express— a single@types/express@4.17.25:The install produced no
ERESOLVEand the entirepackage-lock.jsondelta is the two mirrored range strings underpackages/dev. Nothing undernode_modules/**in the lockfile changed, and no new nestednode_modules/*/node_modules/expressappeared:Real server boot on the reconciled tree (no mocks — a genuine express app over HTTP):
GET /list-appsreturned200with["agent"], andPOST /apps/agent/users/u1/sessions/s1exercisedexpress.json()body parsing, returning200with{"id":"s1","appName":"agent","userId":"u1","state":{"k":"v"},"events":[],"lastUpdateTime":...}.Repo checks that CI runs, on the exact pushed commit:
npm run build(all three workspaces),npm run lint(exit 0),npm run format:check(exit 0),npx secretlint(exit 0),scripts/check_license.sh(exit 0).One honest caveat.
npm run ts:checkexits non-zero, but it does so identically on the base commit: 281tsc --noEmiterrors before and after, with a byte-identical set offile:line:collocations, none in any file this PR touches. It is pre-existing and out of scope here (CI does not runts:check; separate PRs address it). The new test file itself produces zero type errors.Checklist
[x] I have read the CONTRIBUTING.md document.
[x] I have performed a self-review of my own code.
[x] I have commented my code, particularly in hard-to-understand areas.
[x] I have added tests that prove my fix is effective or that my feature works.
[x] New and existing unit tests pass locally with my changes.