Skip to content

fix(deps): realign apps/web lockfile with declared ranges - #1114

Merged
groupthinking merged 2 commits into
mainfrom
groupthinking-fix-apps-web-lockfile-drift
Aug 29, 2026
Merged

fix(deps): realign apps/web lockfile with declared ranges#1114
groupthinking merged 2 commits into
mainfrom
groupthinking-fix-apps-web-lockfile-drift

Conversation

@groupthinking

@groupthinking groupthinking commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Canonical issue

Closes #1110

Duplicate report: #1112 (a self-correction of #1110's claim that npm ci would reject the lockfile — it does not). Both describe the same defect; #1110 is canonical and #1112 will be closed as a duplicate.

Outcome

apps/web now installs the dependency versions its manifest actually declares. Six packages were silently resolving below their declared floors, so every developer, CI run, and production build was shipping older code than the manifest promised — including a Sentry SDK a minor version behind and an @opentelemetry/api that violated an exact pin. npm ls --all goes from 6 invalid: entries to 0, with no manifest edit and no new advisories.

Scope

Risk

  • Risk level: low — one lockfile, no manifest change, no source change; every drifted package moves up into its already-declared range, so no consumer contract changes.
  • Failure mode: the realistic risk is a runtime regression from @sentry/nextjs 10.65.0 → 10.69.0 (the only minor bump) or from the four forced transitive bumps. Mitigated by build + type-check + lint + 244/245 tests + an identical Next.js route table, all green on this head.
  • Rollback: revert the single commit (236b6597f). The lockfile is self-contained and no other file depends on it, so revert restores the exact previous tree byte-for-byte.

Verification

Every acceptance criterion in #1110, checked on head 236b6597f:

  • Focused testsapps/web suite 244/245 pass; the single failure is proven pre-existing and environmental (see below).
  • Required CIbuild, test, lint-frontend, lint-python, npm-audit, dependency-review, CodeQL, trivy, bandit, gitleaks, guards all pass on this head.
  • Review threads resolved — none open.
  • No manifest changepackage.json and apps/web/package.json SHA-256 verified byte-identical before and after.
  • npm ls --all reports zero invalid: — 6 → 0, checked against the real npm ci-installed tree, not just a lockfile parse.
  • All root overrides honored — all 17 override-target copies resolve to identical versions and paths as baseline (diff: IDENTICAL): next 16.2.12, postcss 8.5.25/8.5.21, protobufjs 7.6.5, sharp 0.35.3, @hono/node-server 2.0.12, picomatch 4.0.5, and both nested minimatch > brace-expansion pins (1.1.18 / 2.1.4).
  • Zero new advisoriesnpm audit 12 high before → 12 high after; set-differenced by advisory key: NEW advisories introduced: NONE.
  • npm ci --legacy-peer-deps exit 0
  • npm run build:web exit 0, route table diffed against baseline: identical
  • apps/web lint exit 0
  • (beyond the stated bar, since Sentry moved a minor) tsc --noEmit exit 0

Production evidence

Vercel preview built and deployed successfully from this exact head (236b6597f): https://vercel.com/garv1/v0-uvai/45sxaJMDnGTBMi54SzwJuBy8dMHLVercel, Vercel Agent Review, and Vercel Preview Comments all report SUCCESS. The Next.js production build emitted a route table byte-identical to the pre-change baseline, so the deployed surface is unchanged; the delta is confined to the versions of the code behind it.


What was wrong

Six apps/web dependencies resolved below the ranges declared in apps/web/package.json. npm ls --all flagged each as invalid:

package declared locked now
@opentelemetry/api 1.9.1 (exact) 1.9.0 1.9.1
@sentry/nextjs ^10.66.0 10.65.0 10.69.0
@stripe/stripe-js ^9.10.0 9.9.0 9.12.1
@tailwindcss/postcss ^4.3.3 4.3.2 4.3.3
tailwindcss ^4.3.3 4.3.2 4.3.3
autoprefixer ^10.5.4 10.5.2 10.5.4

Root cause (verified, not assumed)

The lockfile's recorded ranges under packages["apps/web"] are byte-identical to the manifest:

pkg                   | manifest  | lock-recorded | lock-resolved
@opentelemetry/api    | 1.9.1     | 1.9.1         | 1.9.0
@sentry/nextjs        | ^10.66.0  | ^10.66.0      | 10.65.0
...

npm's sync validation compares recorded range vs manifest range only. It never re-checks the resolved node version against the range binding it. So the edge looks satisfied, reify leaves the stale node untouched, and a plain npm install is a genuine no-op — confirmed by SHA-256: the lockfile was byte-identical after a full npm install --legacy-peer-deps. This is exactly why npm ci passes while six packages sit below their floors, as #1112 correctly established.

Approach — surgical, not nuclear

#1110 suggested rm package-lock.json, while noting it "carries real risk (it may move many unrelated transitive versions)". Its hard requirements are outcome-based, so I bounded the blast radius instead: evict only the six stale nodes plus their nested subtrees, then let npm install --package-lock-only re-resolve exactly those edges.

The resulting semantic delta is confined to the six targets, their dedupe-hoist from apps/web/node_modules to the root, and four transitive bumps that the upgrades require — each traced to a concrete requirer rather than accepted as churn:

transitive change required by
@apm-js-collab/code-transformer 0.15.0 → 0.18.1 the two below want ^0.18.0
@apm-js-collab/code-transformer-bundler-plugins 0.5.0 → 0.7.3 @sentry/server-utils@10.69.0 wants ^0.7.3
@apm-js-collab/tracing-hooks 0.10.1 → 0.13.0 @sentry/server-utils@10.69.0 wants ^0.13.0
enhanced-resolve 5.21.6 → 5.24.5 @tailwindcss/node@4.3.3 wants ^5.24.1

The raw diff is 2822 lines, which overstates the change: most of it is dedupe-hoisting (apps/web/node_modules/Xnode_modules/X), not version movement. The semantic delta is 38 nodes added, 43 removed, and only the 4 version bumps above.

⚠️ Worth recording for anyone hitting this class of drift again: do not reach for npm install <pkg> -w apps/web. As #1110 observed, npm loosens the manifest rather than updating the tree — it rewrites @opentelemetry/api from the exact 1.9.1 to ^1.9.0, retroactively making the wrong locked version "valid" instead of fixing it.

The one failing test is pre-existing and environmental

billing-chat-gating.test.ts > blocks free tier after daily quota times out at 5000ms locally. I did not assume it was unrelated:

  1. Checked out pristine origin/main (abd93326b) into a separate worktree, ran its own npm ci, ran that test file — fails identically with no changes applied.
  2. Isolated the actual trigger: the free-tier path in /api/chat falls through to generateText() against the Vercel AI Gateway, which is never mocked (the test mocks only grok-client, which covers the Pro path). Route line 124 short-circuits to 503 when no gateway key is present, so the test passes wherever no key is configured and hangs wherever one is:
$ npx vitest run .../billing-chat-gating.test.ts                    → 1 failed (5007ms timeout)
$ env -u AI_GATEWAY_API_KEY ... npx vitest run .../same             → 2 passed

That is why CI's test job passes on this PR — and also why it passes vacuously: in CI every loop iteration returns 503, so expect(res.status).not.toBe(402) is satisfied by the gateway being unconfigured rather than by free tier actually being served. Filed as #1116; out of scope here, since #1110 requires this be a standalone lockfile change.

Note on the brace-expansion overrides

#1110 asks that the nested minimatch > brace-expansion pins be re-verified. They are preserved unchanged and confirmed above (1.1.18 / 2.1.4, identical to baseline).

While verifying them I measured the installed copies against the OOM advisory directly rather than trusting advisory metadata, and found that the tree is safe but the floors are not: the 100000-entry expansion cap that remediates GHSA-mh99-v99m-4gvg landed in 1.1.17 and 2.1.3, whereas the pins are ^1.1.16 and ^2.1.2 — both demonstrably OOM-vulnerable when installed directly. Today the carets float up to safe versions, so the outcome is correct but incidental rather than guaranteed. Raising the floors requires editing root package.json, which #1110 forbids here, so it is filed as #1115 with the full per-version measurements and shipped in #1117.

Guarding against recurrence

Deliberately proposing no CI change here. As #1112 demonstrated, npm ci is structurally incapable of catching this class of drift, so adding it as a guard would be cargo cult. The check that would catch it is npm ls --all grepped for invalid, which I'd suggest evaluating on its own merits rather than smuggling into a lockfile fix.

CI note

Agent completion enforcement reports failure with missing_trusted_publication. This is repo-wide and pre-existing: it requires a check run named Agent Lock trusted publication published by a trusted GitHub App, and that App is not publishing. PRs #1108, #1103 and #1098 all show the same failure with zero such check runs and were merged regardless. Nothing in this PR affects it.

Six apps/web dependencies resolved *below* the ranges declared in
apps/web/package.json. npm ls --all flagged each as `invalid`:

  @opentelemetry/api    1.9.1 (exact) -> locked 1.9.0
  @sentry/nextjs        ^10.66.0      -> locked 10.65.0
  @stripe/stripe-js     ^9.10.0       -> locked 9.9.0
  @tailwindcss/postcss  ^4.3.3        -> locked 4.3.2
  tailwindcss           ^4.3.3        -> locked 4.3.2
  autoprefixer          ^10.5.4       -> locked 10.5.2

Root cause: the lockfile's *recorded* ranges under packages["apps/web"]
are byte-identical to the manifest, so npm's sync validation considers
the edges satisfied and reify leaves the stale nodes in place. npm never
re-checks a resolved node version against the range it is bound by, which
is why `npm ci` passes, plain `npm install` is a no-op (lockfile SHA-256
unchanged after a full install), and only `npm ls` surfaces the drift.

Fix: evict the six stale nodes (plus their nested subtrees) from the
lockfile and let `npm install --package-lock-only` re-resolve exactly
those edges. Preferred over the full `rm package-lock.json` regeneration
suggested in #1110 because it bounds the blast radius to the affected
subtrees instead of churning unrelated transitives.

Resulting semantic delta is confined to the six targets, their dedupe
hoist from apps/web/node_modules to the root, and four transitive bumps
that the upgrades *require*:

  @apm-js-collab/code-transformer                  0.15.0 -> 0.18.1
  @apm-js-collab/code-transformer-bundler-plugins  0.5.0  -> 0.7.3
  @apm-js-collab/tracing-hooks                     0.10.1 -> 0.13.0
  enhanced-resolve                                 5.21.6 -> 5.24.5

The first three are demanded by @sentry/server-utils@10.69.0 (^0.7.3 /
^0.13.0 / ^0.18.0); the fourth by @tailwindcss/node@4.3.3 (^5.24.1).

Verification against the #1110 acceptance criteria:

  - package.json and apps/web/package.json byte-identical (SHA-256 verified)
  - npm ls --all invalid markers: 6 -> 0
  - all 17 root-override resolutions unchanged (next 16.2.12, postcss
    8.5.25/8.5.21, protobufjs 7.6.5, sharp 0.35.3, @hono/node-server
    2.0.12, picomatch 4.0.5, nested minimatch>brace-expansion 1.1.18/2.1.4)
  - npm audit: 12 high before, 12 high after, zero new advisories
  - npm ci --legacy-peer-deps: exit 0
  - npm run build:web: exit 0, route table identical to baseline
  - apps/web lint: exit 0
  - apps/web type-check: exit 0

Refs #1110
Refs #1112

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 30, 2026 20:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review any files in this pull request.

@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
v0-uvai Ready Ready Preview, v0 Aug 29, 2026 7:40am

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 09b89b18-8010-4107-9509-01c19fe18660


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown

🔍 PR Validation

⚠️ Large PR detected (8673 lines changed)

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA def74cd.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

  • package-lock.json

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown

Agent Completion Truth Gate: NOT_APPLICABLE

Evidence agrees.

Machine-readable verdict
{
  "details": {},
  "reasons": [],
  "verdict": "not_applicable"
}

Workflow evidence

Copy link
Copy Markdown
Owner Author

Automated PR-remediation scan — substance is green; blocked only on governance + human merge

Real CI is fully passing on head 236b659: build, lint-frontend, lint-python, npm-audit, dependency-review, CodeQL, bandit, trivy, Security Scan (python/js), plus the Agent Completion Truth Gate (NOT_APPLICABLE — evidence agrees). Coverage upload was still running at scan time.

mergeable_state is unstable because three governance meta-checks fail — none is a code problem:

  1. PR GovernanceCanonical delivery contract blocked. The body needs the canonical sections ## Canonical issue, ## Outcome, ## Risk, ## Verification, ## Production evidence, and exactly one closing reference. This PR has two (Closes #1110, Closes #1112) — keep one as canonical (e.g. Closes #1110) and reference the other in-body.
  2. Agent completion enforcementmissing_trusted_publication. Needs a trusted evidence artifact published through the repo's evidence pipeline.
  3. CodeRabbit skipped review — the PR carries no label, and auto-review requires at least one qualifying label (e.g. javascript).

I did not merge: this run is unattended and main is protected, so merge sign-off stays with a human. Once the three items above clear, the staged merge is:

gh pr merge 1114 --repo groupthinking/EventRelay --squash

Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Returned this PR to draft at exact head 236b6597f78380cbafc2b96b6cf8eda0ee38646e.

The code/check evidence is currently green, but independent review did not materialize: Copilot could not review the lockfile and CodeRabbit skipped because the PR had no qualifying label. The required implementation execution receipt is also absent. I added javascript to activate the configured CodeRabbit review path. Keep this draft until an exact-head review artifact and complete provenance receipt exist. No code or branch history was changed.

Copy link
Copy Markdown
Owner Author

Automated PR-remediation scan @ head 236b6597f — substance green; sole remaining red is the known repo-wide trusted-publication gate; not merging (unattended → human decision)

Re-scanned after this PR returned to ready-for-review. State has materially improved since the 20:46 scan:

  • PR Governance now passes — the canonical body carries a single closing reference (Closes #1110), fixing the earlier two-Closes block.
  • agent-completion/truth-gateNOT_APPLICABLE — evidence agrees.
  • ✅ Required CI green: validate, dependency-review (no vulnerability / license / scorecard issues), Canonical issue and evidence. npm audit unchanged (12 high → 12 high, no new advisories).
  • 0 open review threads.
  • ⚠️ CodeRabbit still reports Review skipped: excluded by label configuration despite the javascript label, so the independent review you asked for at 21:14 has not materialized. If that's a merge prerequisite for you, trigger it manually with @coderabbitai review.
  • Agent completion enforcementmissing_trusted_publication — the only red. It's repo-wide/pre-existing (needs the Agent Lock trusted publication App to publish, which it isn't), and mergeable_state: unstable (not blocked) confirms it is non-blocking; fix: honor declared picomatch override in lockfile #1108 / fix(deps): clear @hono/node-server advisory via MCP SDK 1.30.0 #1103 / fix(deps): raise transitive override floors to patched versions #1098 merged past the identical failure.

I did not merge: this is an unattended scheduled run, base main is protected, and there is no automerge label — per the publish gate the irreversible step stays with a human.

Staged merge command (for a human):

gh pr merge 1114 --repo groupthinking/EventRelay --squash

Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

@groupthinking: Triggering an independent review for the current PR head. I’ll focus on production-impacting dependency-resolution, lockfile-integrity, and supply-chain risks.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 30, 2026
@groupthinking
groupthinking marked this pull request as draft July 30, 2026 22:14

Copy link
Copy Markdown
Owner Author

Returned to draft again at exact head 236b6597f78380cbafc2b96b6cf8eda0ee38646e.

Material progress is preserved: CodeRabbit approved this exact head at 2026-07-30T22:09:45Z, and zero review threads remain. The remaining blocker is provenance, not code: the implementation still has no complete receipt identifying the implementing agent login, run ID, branch/PR, claimed timestamp, heartbeat, and exact head. The prior receipt on #1110 explicitly covers containment only and does not claim code authorship. Keep draft until the implementation receipt exists. No code or branch history was changed.

Copy link
Copy Markdown
Owner Author

Closing — branch orphaned by the secret-purge force-push

No common ancestor with current main (git merge-base returns empty), so this branch is not rebaseable.

The work is unlanded — realigning the apps/web lockfile with declared ranges. Tracked in #1378 for a fresh cut from main.

Branch retained for archive-tagging.


Generated by Claude Code

@groupthinking groupthinking reopened this Aug 29, 2026
@groupthinking
groupthinking marked this pull request as ready for review August 29, 2026 07:07
@groupthinking
groupthinking enabled auto-merge (squash) August 29, 2026 07:07
@groupthinking groupthinking self-assigned this Aug 29, 2026
auto-merge was automatically disabled August 29, 2026 07:30

Pull request was closed

@groupthinking groupthinking reopened this Aug 29, 2026
@groupthinking
groupthinking enabled auto-merge (squash) August 29, 2026 07:34
@groupthinking
groupthinking merged commit db0241a into main Aug 29, 2026
4 checks passed
@groupthinking
groupthinking deleted the groupthinking-fix-apps-web-lockfile-drift branch August 29, 2026 07:39
@linear-code

linear-code Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

GRV-444

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

apps/web lockfile drift: six dependencies resolve below their declared ranges

2 participants