Skip to content

Fix: declare zod, @google-cloud/vertexai and openapi-types at the workspace root - #601

Open
AmaadMartin wants to merge 1 commit into
mainfrom
fix/hoist-zod-vertexai-openapi-types-to-root
Open

Fix: declare zod, @google-cloud/vertexai and openapi-types at the workspace root#601
AmaadMartin wants to merge 1 commit into
mainfrom
fix/hoist-zod-vertexai-openapi-types-to-root

Conversation

@AmaadMartin

Copy link
Copy Markdown
Owner

Please ensure you have read the contribution guide before creating a pull request.

Link to Issue or Description of Change

  1. Link to an existing issue (if applicable):
    Closes: #issue_number
    Related: #issue_number
  2. Or, if no issue exists, describe the change:

Problem: Files under tests/** are owned by the workspace-root package (adk, the manifest at package.json) — they are not inside core/, dev/ or integrations/, so the root package.json is the nearest manifest on their Node resolution path. Three third-party packages they import are declared in no manifest on that path:

Package Range Declared in On the tests/** resolution path?
zod ^4.2.1 core/package.json dependencies no
@google-cloud/vertexai ^1.12.0 core/package.json dependencies no
openapi-types ^12.1.3 core/package.json devDependencies no

They resolve today only because npm workspaces hoists core's dependencies into the repo-root node_modules/. That coupling is invisible and unenforced — a phantom dependency. openapi-types is the sharpest case: it is only a devDependency of core, i.e. something core itself treats as disposable, yet a root e2e test imports it.

13 import sites are affected:

  • zod (8): tests/e2e/routing/planning_mode_test.ts, tests/e2e/streaming/streaming_e2e_test.ts, tests/integration/streaming_sse/sse_agent_test.ts, tests/integration/a2a/basic/remote_a2a/weather_time_agent.ts, tests/integration/adk_web/agent/agent.ts, tests/integration/agents/simple_agent_with_tools/agent.ts, tests/integration/skills/inline/agent.ts, tests/integration/streaming/agent.ts
  • @google-cloud/vertexai (4): tests/integration/agents/agent_with_sandbox_executor_test.ts, tests/integration/memory/vertex_ai_memory_bank_service_test.ts, tests/integration/sessions/vertex_ai_session_service_test.ts, tests/integration/tools/agent_tool_vertexai_test.ts
  • openapi-types (1): tests/e2e/tools/rest_api_tool_auth_e2e_test.ts

Severity is workspace hygiene, not a consumer-facing break: the root package is the workspace host and is never published, so no installer of @google/adk or @google/adk-devtools is affected.

Solution: Declare all three in the root devDependencies at the exact range the owning workspace already declares (copied verbatim, not re-derived from the lockfile), and let a plain npm install regenerate package-lock.json. This is the direct sibling of the @google/genai hoist that landed as google#564, and reproduces its shape: added lines in devDependencies, plus the mirrored hunk in the lock's root workspace node. Nothing else changes — no source file is touched.

Mirroring the owner's range (rather than pinning, widening, or upgrading) keeps the two manifests textually consistent, so the root range cannot silently drift from its owner.

Collision check against open PRs on this fork

Run before implementing, per contribution workflow:

gh pr list --repo AmaadMartin/adk-js --state open --limit 1000 --json number,title,headRefName

499 open PRs; one overlaps and the reviewer should know about it. PR #360 ("Fix: declare the root workspace's test dependencies in package.json", branch fix/root-phantom-test-dependencies) is a strict superset of this PR: it adds the same three declarations at the same three ranges, plus @mikro-orm/core (^6.6.10) and dotenv (^17.2.3). The two PRs edit the same devDependencies block in the same two files, so whichever lands second will conflict textually and should be rebased, not merged blind.

They are not equivalent in scope. This PR deliberately excludes the two extra packages (see the exclusion list below); #360 includes them. Landing this one and rebasing #360 leaves only the @mikro-orm/core/dotenv question to decide; landing #360 makes this PR redundant and it should be closed. Either order is fine — please just don't merge both without a rebase.

Adjacent PRs reviewed and ruled out as non-collisions:

Explicitly out of scope (and why)

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. — N/A, deliberately. This change adds no executable code. vitest.config.ts scopes coverage to core/src, dev/src, integrations/src, none of which is touched, so the configured thresholds are unmoved. A test asserting the contents of package.json would be test theatre; the verification burden is discharged by the falsifiability and tree-invariance evidence below instead.
[x] All unit tests pass locally.

Falsifiability — proving the change is load-bearing

1. Resolution-path check (all 13 sites). For each import site, walk the Node resolution path upwards and report the first manifest declaring the bare specifier. Run against HEAD (before) and the working tree (after):

BEFORE: phantom sites: 13 / 13   (exit 1)
AFTER:  phantom sites:  0 / 13   (exit 0)   — all now declared by package.json (devDependencies)

2. TS2307 experiment (scratch copy: drop the three from core/package.json, rm -rf node_modules package-lock.json && npm install, then tsc --noEmit). Filtered to tests/**, without a root declaration:

tests/e2e/tools/rest_api_tool_auth_e2e_test.ts:12:25 - error TS2307: Cannot find module 'openapi-types' or its corresponding type declarations.
tests/integration/agents/agent_with_sandbox_executor_test.ts:7:22 - error TS2307: Cannot find module '@google-cloud/vertexai' or its corresponding type declarations.
tests/integration/memory/vertex_ai_memory_bank_service_test.ts:7:22 - error TS2307: Cannot find module '@google-cloud/vertexai/build/src/genai/client.js' or its corresponding type declarations.
tests/integration/sessions/vertex_ai_session_service_test.ts:7:24 - error TS2307: Cannot find module '@google-cloud/vertexai/build/src/genai/sessions.js' or its corresponding type declarations.
tests/integration/tools/agent_tool_vertexai_test.ts:7:22 - error TS2307: Cannot find module '@google-cloud/vertexai/build/src/genai/client.js' or its corresponding type declarations.
tests/integration/tools/agent_tool_vertexai_test.ts:8:24 - error TS2307: Cannot find module '@google-cloud/vertexai/build/src/genai/sessions.js' or its corresponding type declarations.

Honest caveat on zod: it does not reproduce under experiment 2, and I am not going to claim it does. Removing zod from core and dev still leaves zod@4.4.3 installed and hoisted, because it is a real dependencies entry of @modelcontextprotocol/sdk (^3.25 || ^4.0) and a peer of zod-to-json-schema, both of which core depends on:

zod version with NO first-party declaration at all: 4.4.3
resolved solely via: node_modules/@modelcontextprotocol/sdk node_modules/zod-to-json-schema

That makes the zod exposure worse, not milder, and it is why experiment 1 is the primary evidence: tests/** currently satisfies its zod import from a transitive dependency of a third-party package, entirely outside first-party control. The range that governs it today is the MCP SDK's ^3.25 || ^4.0, which permits zod 3 — a different major with an incompatible API — while core is written against ^4.2.1. Declaring zod: ^4.2.1 at the root makes the root a direct declarer, so npm must satisfy ^4.2.1 at the top level regardless of what the SDK resolves to.

Tree-invariance proof

The installed dependency tree must not change — every range added is already satisfied by the copy npm hoists today. Signature over node_modules (count + sha256 of every sorted path name@version), captured before and after npm install:

BEFORE: 1086 4be4f0928146bc39d9659ea8665b13774f525014b06b3b2fe1fa8b2a17eb9044
AFTER:  1086 4be4f0928146bc39d9659ea8665b13774f525014b06b3b2fe1fa8b2a17eb9044   — identical

Lock entries confirm no resolution or dev flag moved:

node_modules/zod                    version=4.4.3   dev=(absent → production)
node_modules/@google-cloud/vertexai version=1.12.0  dev=(absent → production)
node_modules/openapi-types          version=12.1.3  dev=true

Regression checks (before → after, same machine, identical node_modules)

Check Before After
npm run build exit 0 exit 0
npm run lint exit 0 exit 0
npx prettier --check package.json package-lock.json exit 0
npm run ts:check 281 errors, exit 2 281 errors, exit 2 — byte-identical error set
targeted --project integration (5 files) 5 files / 7 tests passed
targeted --project e2e (3 files) 2 failed, 1 skipped (3 files); 3 failed, 8 skipped (11 tests) identical: 2 failed, 1 skipped (3); 3 failed, 8 skipped (11)

Two pre-existing conditions worth flagging, both unchanged by this PR and neither introduced by it:

  • npm run ts:check does not exit 0 on main. It reports 281 errors, almost all from test files importing @google/adk (which resolves to core/dist types) and comparing them against core/src types. I verified the error set is byte-identical before and after this change (diff of the error lines is empty), so this PR adds zero new type errors. CI does not run ts:check; PR Fix: make npm run ts:check a deterministic, build-independent check (tranche 1/5) #568 targets making it deterministic.
  • The 3 targeted e2e failures are credentials-gated, not logic failures: API key must be provided via constructor or GOOGLE_GENAI_API_KEY or GEMINI_API_KEY environment variable. Identical counts before and after.

Manual End-to-End (E2E) Tests:
Please provide instructions on how to manually test your changes, including any necessary setup or configuration.

# 1. Manifest-only diff — must print exactly package.json and package-lock.json
git diff --name-only main...HEAD

# 2. The lock hunk is confined to the root workspace node (expect +4 -1)
git diff --stat -- package-lock.json

# 3. The lockfile is at a fixed point: a second install produces no further diff
npm install && git status --porcelain     # only the two manifests, unchanged

# 4. The three are in, the three excluded are out
node -e "const d=require('./package.json').devDependencies;console.log(d['zod'],d['@google-cloud/vertexai'],d['openapi-types'],d['dotenv'],d['@mikro-orm/core'],d['onnxruntime-node'])"
# → ^4.2.1 ^1.12.0 ^12.1.3 undefined undefined undefined

# 5. Formatting (husky/lint-staged runs prettier over staged JSON)
npx prettier --check package.json package-lock.json

# 6. Nothing regressed
npm run build && npm run lint

Note on the lockfile: it was regenerated with npm install --registry=https://registry.npmjs.org so that every new resolved URL points at the public registry. All 1167 resolved entries remain on registry.npmjs.org, unchanged from main.

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. — N/A; the diff is six manifest lines with no code.
[x] I have added tests that prove my fix is effective or that my feature works. — see the falsifiability evidence above; no test file added, deliberately, for the reason stated.
[x] New and existing unit tests pass locally with my changes.

…he workspace root

Files under tests/** are owned by the workspace-root package, so the root
package.json is the nearest manifest on their Node resolution path. Three
packages they import are declared in no manifest on that path and resolve
only because npm workspaces hoists a sibling's dependency into the repo-root
node_modules.

Declare all three in the root devDependencies at the exact range the owning
workspace already uses:

  zod                    ^4.2.1   (core dependencies)
  @google-cloud/vertexai ^1.12.0  (core dependencies)
  openapi-types          ^12.1.3  (core devDependencies)

openapi-types is the sharpest case: a root e2e test imports something core
itself treats as a dev-only dependency.

package-lock.json is regenerated by a plain `npm install`; the change is
confined to the root workspace node. The installed tree is unchanged -- every
range added is already satisfied by the copy npm hoists today, verified by an
identical pre/post signature over node_modules (1086 packages, sha256
4be4f0928146bc39d9659ea8665b13774f525014b06b3b2fe1fa8b2a17eb9044).

Follows the @google/genai hoist in google#564.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant