Skip to content

fix: stop shadowing the repo pnpm lockfile on Vercel (both apps) - #24

Merged
fielding merged 1 commit into
mainfrom
fm/ripguard-workspace-fix-main
Jul 27, 2026
Merged

fix: stop shadowing the repo pnpm lockfile on Vercel (both apps)#24
fielding merged 1 commit into
mainfrom
fm/ripguard-workspace-fix-main

Conversation

@fielding

Copy link
Copy Markdown
Owner

Why this PR exists

PR #23 carried this fix but accidentally targeted base fable/fresh-eyes instead of main, so it landed on an already-merged feature branch and main is still brokenpackages/app/pnpm-workspace.yaml is still present on main and ripguard.xyz still fails to build. This retargets the fix to main.

It also expands the fix to packages/app-sol, which carries the identical trap.

The last green EVM production deploy was ~38 days ago. This predates the support-Telegram-link merge (PR #22) and is unrelated to it — c005209..dcaaa9d touched no package.json and no pnpm-lock.yaml.

The failure on main:

Module not found: Can't resolve '@x402/core/client'
  @coinbase/cdp-sdk@1.54.0/_esm/actions/x402/signX402Payment.js
  <- @base-org/account 2.4.0
  <- @wagmi/connectors 6.2.0 (baseAccount)
  <- @rainbow-me/rainbowkit 2.2.11        [Client Component SSR]

Root cause

The deploys were never using the committed lockfile.

Every Vercel project sets rootDirectory to a package directory, with include source files outside the root directory enabled:

project rootDirectory install command
ripguard packages/app (default pnpm install)
ripguard-testnet packages/app (default pnpm install)
ripguard-sol packages/app-sol pnpm install

So Vercel runs pnpm install inside the package directory. pnpm resolves its workspace root to the nearest pnpm-workspace.yaml walking up from the cwd — and both packages had one (each carrying the same two ignoredBuiltDependencies entries). That made each package its own workspace root, so the repo-root pnpm-lock.yaml was invisible and every dependency was re-resolved from its semver range on each deploy.

That is why the break is latent and time-triggered rather than commit-triggered. Fresh resolution today walks @rainbow-me/rainbowkit ^2.2.102.2.11@wagmi/connectors 6.2.0@base-org/account 2.4.0@coinbase/cdp-sdk 1.54.0. That version's x402 module does:

importX402Dependency("@x402/core/client", () => import("@x402/core/client"))

@x402/core, @x402/evm, @x402/svm, @x402/extensions are declared optional peer dependencies of cdp-sdk, so they are legitimately absent — but Turbopack statically analyses the dynamic import() and turns each unresolvable specifier into a hard build error (8 of them). The committed lockfile pins @coinbase/cdp-sdk 1.45.0, which has no such import, which is why CI and local builds stayed green the whole time.

The smoking gun in the failed deploy log is the store path: ./packages/app/node_modules/.pnpm/.... A workspace-root install puts the store at the repo root, not inside the package.

packages/app-sol is exposed to exactly the same mechanism. It builds green today only because its dependency tree has not yet drawn a broken upstream release — that is luck, not safety, so it is fixed here rather than left armed.

Fix

Delete both packages/app/pnpm-workspace.yaml and packages/app-sol/pnpm-workspace.yaml, and move their ignoredBuiltDependencies to the root pnpm-workspace.yaml. The two files were byte-identical (sharp, unrs-resolver) — nothing app-specific is lost.

pnpm now walks up from either package to the repo root, finds the workspace and pnpm-lock.yaml, and installs the pinned tree. Vercel sets CI=1, so pnpm additionally defaults to --frozen-lockfile. Production, CI, and local now resolve identically.

No lockfile change was neededpnpm install --frozen-lockfile at the root is clean, and CI=1 pnpm install from inside each package succeeds against the existing lockfile.

Why not the alternatives

  • Add the @x402/* packages. They are optional peers of a transitive dependency of a wallet connector RipGuard never invokes. It would pull a Coinbase payments SDK — including @x402/svm and its Solana stack — into the EVM app to satisfy an import that only exists to be caught and rethrown. It also leaves the deploys resolving fresh semver, so the next upstream release breaks prod again.
  • Pin @coinbase/cdp-sdk via an override. Treats the symptom, freezes a transitive dep the app doesn't use, and still leaves every other transitive dep free to drift on deploy.
  • serverExternalPackages / a Turbopack alias. A bundler workaround for a dependency-resolution problem, and likewise leaves the builds non-reproducible.

Restoring lockfile-driven installs is the actual root cause and is strictly the smaller change.

Verification

Both apps were verified against Vercel's real command sequence, not CI's, from a clean node_modules:

cd packages/<app> && CI=1 pnpm install && ./node_modules/.bin/next build
  • packages/app before: 8 × Module not found: Can't resolve '@x402/...', resolving @coinbase/cdp-sdk@1.54.0, store at packages/app/node_modules/.pnpm.
  • packages/app after: clean build, resolving @coinbase/cdp-sdk@1.45.0, store at the repo root. Routes /, /create, /vaults all compile.
  • packages/app-sol after: clean build, no nested store, same route set. Pinning it to the lockfile does not regress it.

No wallet-connect regression in either app. Client chunks still contain:

  • app (EVM): walletConnect 38 files, metaMask 28, injected 8, coinbaseWallet 5, baseAccount 4, rainbowkit 4. x402 appears in 0 client chunks — it was only ever reached through the SSR pass of @base-org/account's node entry.
  • app-sol: wallet-adapter 7 files, solflare 5, WalletMultiButton 5, phantom 4, backpack 1.

Also green for both packages: tsc --noEmit, lint, and test (115 app / 168 app-sol).

For reference, the equivalent change on PR #23 produced a green Vercel – ripguard preview deploy — the same pipeline whose last production run on main (ripguard-6pu8nnjtz) is still in ERROR. The preview builds on this PR are the check that matters here.

I did not view a rendered page in a browser: preview URLs are behind Vercel deployment protection and I did not authenticate through it. The claim is a green build and deploy, not a visual check of the running app.

Scope

Config only. No app source touched. AGENTS.md records the rootDirectory + nested-workspace gotcha so it does not get reintroduced.

🤖 Generated with Claude Code

All three Vercel projects set rootDirectory to a package (ripguard and
ripguard-testnet -> packages/app, ripguard-sol -> packages/app-sol), so
`pnpm install` runs inside the package directory. pnpm resolves its
workspace root to the nearest pnpm-workspace.yaml, which was the one in
the package itself -- so the repo-root pnpm-lock.yaml was never used and
every dependency was re-resolved from its semver range at deploy time.

That drift is what broke ripguard.xyz: fresh resolution now yields
rainbowkit 2.2.11 -> @wagmi/connectors 6.2.0 -> @base-org/account 2.4.0
-> @coinbase/cdp-sdk 1.54.0, whose x402 module dynamically imports the
@x402/* optional peer dependencies. They are legitimately absent, and
Turbopack turns the unresolvable specifiers into build errors. The
lockfile pins @coinbase/cdp-sdk 1.45.0, which has no such import, so CI
and local builds stayed green.

packages/app-sol carried the identical marker. It builds today only
because its tree has not yet drawn a broken upstream release, so remove
both rather than leave the second one armed.

Removing the nested markers lets pnpm find the root workspace and
lockfile from either package, making both deploys reproducible and
identical to CI. The ignoredBuiltDependencies they carried were the same
two entries and move to the root pnpm-workspace.yaml.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ripguard Ready Ready Preview, Comment Jul 27, 2026 4:48am
ripguard-sol Ready Ready Preview, Comment Jul 27, 2026 4:48am
ripguard-testnet Ready Ready Preview, Comment Jul 27, 2026 4:48am

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