Skip to content

fix(docs): unify lucide-react so fumadocs-core resolves once; guard the build - #152

Merged
leoisadev1 merged 1 commit into
mainfrom
posthog-code/fix-fumadocs-core-peer-split
Jul 7, 2026
Merged

leoisadev1 merged 1 commit into
mainfrom
posthog-code/fix-fumadocs-core-peer-split

Conversation

@leoisadev1

Copy link
Copy Markdown
Member

What happened

Production (email-sdk.dev) crashed on every page with "You need to wrap your application inside `FrameworkProvider`" after the deploys on Jul 6. It has been restored by promoting the last good deployment (email-sdk-fumadocs-3wsvdubrx, commit 599b614); this PR makes main safe to deploy again and adds guards so this class of failure can never ship silently.

Root cause

#123's in-range deps refresh bumped the app's lucide-react to ^1.23.0 while the lockfile kept fumadocs-ui's edge on 1.16.0. lucide-react is a peer dependency of fumadocs-core, so bun's isolated linker materialized fumadocs-core@16.9.1 once per peer set:

app         -> .bun/fumadocs-core@16.9.1+47290192…  (peer: lucide 1.23.0)
fumadocs-ui -> .bun/fumadocs-core@16.9.1+c955c657…  (peer: lucide 1.16.0)

Two module instances → two React contexts → RootProvider provided on one instance while components consumed the other → every page crashed at hydration. The build stayed green (runtime-only failure), so CI passed and the crash shipped.

The vite 8.0.14 → 8.1.3 bump in the same refresh was a red herring: with lucide unified, vite 8.1.3 builds a clean single-instance bundle (verified in a browser).

Fix

  • Revert lucide-react to ^1.16.0 so the app and fumadocs-ui share one install (lockfile unified, nested copy gone).
  • scripts/check-module-identity.ts (pre-build): fails the build if fumadocs-core, react, react-dom, @tanstack/react-router, or lucide-react resolve to different physical installs from the app vs fumadocs-ui. This would have failed chore(deps): refresh in-range dependencies to latest safe versions #123 in CI (release:ci runs the docs build against the frozen lockfile).
  • scripts/check-client-bundle.ts (post-build backstop): fails if the framework-context module lands in more than one client chunk (bundler-level duplication).
  • AGENTS.md: documents the invariant for future deps refreshes.

Verification

  • Broken state reproduced locally (bisected the chore(deps): refresh in-range dependencies to latest safe versions #123 dep set; lucide is the trigger, everything else including vite 8.1.3 is innocent).
  • Identity guard fails loudly on the broken state and passes on the fixed one; bundle backstop verified against the broken build output.
  • Fixed build: identity + bundle guards pass, types:check passes, 31/31 tests pass, and /, /docs, /docs/quickstart, /blog all hydrate cleanly in a real browser with zero console errors.

Created with PostHog Code

…he build

PR #123's in-range deps refresh bumped the app's lucide-react to ^1.23.0
while the lockfile kept fumadocs-ui's edge on 1.16.0. lucide-react is a
peer dependency of fumadocs-core, so bun's isolated linker materialized
fumadocs-core@16.9.1 once per peer set: two module instances, two React
contexts. RootProvider provided on one instance while components consumed
the other, and every page crashed at hydration with "You need to wrap
your application inside `FrameworkProvider`". The build stayed green, so
the crash shipped and took production down; the vite 8.0.14 -> 8.1.3 bump
in the same refresh was a red herring (verified innocent once lucide was
unified).

- Revert lucide-react to ^1.16.0 so app and fumadocs-ui share one install.
- scripts/check-module-identity.ts (pre-build): fail if fumadocs-core,
  react, react-dom, @tanstack/react-router, or lucide-react resolve to
  different physical installs from the app vs fumadocs-ui. This would
  have failed PR #123 in CI (release:ci runs the docs build with the
  frozen lockfile).
- scripts/check-client-bundle.ts (post-build backstop): fail if the
  framework-context module lands in more than one client chunk.
- AGENTS.md: document the invariant for future deps refreshes.

Production was restored by promoting the last good deployment
(email-sdk-fumadocs-3wsvdubrx, commit 599b614); this makes main safe to
deploy again.

Generated-By: PostHog Code
Task-Id: e66c853d-287b-4334-8055-51c53c0cd66b
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@vercel

vercel Bot commented Jul 7, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
email-sdk-fumadocs Ready Ready Preview, Comment Jul 7, 2026 5:35pm

Request Review

@greptile-apps

greptile-apps Bot commented Jul 7, 2026 •

Copy link
Copy Markdown

Greptile Summary

This PR restores the docs app to one shared Fumadocs/Lucide dependency path. The main changes are:

  • Reverts lucide-react to ^1.16.0 in the docs app and lockfile.
  • Adds a pre-build module identity check for singleton-critical packages.
  • Adds a post-build client bundle check for duplicated Fumadocs framework context chunks.
  • Documents the dependency invariant in AGENTS.md.

Confidence Score: 5/5

Safe to merge with low risk.

No issues were found. The lockfile change matches fumadocs-ui's declared lucide-react dependency, and the new checks are scoped to the docs build path with clear failure behavior.

No files require special attention.

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex produced a proof for the posted P1 finding and referenced the corresponding review comment for details.
  • T-Rex produced a proof for the posted P2 finding and referenced the corresponding review comment for details.
  • T-Rex completed general contract validation, including guard, browser, and visual proofs, verifying guard exit codes, navigation, and rendered routes.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
AGENTS.md Documents the Fumadocs/Lucide singleton invariant and the new pre/post-build guards for future dependency refreshes.
apps/fumadocs/package.json Pins lucide-react back to the fumadocs-ui-compatible range and wires singleton/bundle checks into the build script.
apps/fumadocs/scripts/check-client-bundle.ts Adds a post-build asset scan that fails when the Fumadocs framework context marker appears in multiple client chunks.
apps/fumadocs/scripts/check-module-identity.ts Adds a pre-build resolver check ensuring singleton-critical packages resolve to the same physical install from the app and fumadocs-ui.
bun.lock Updates the frozen lockfile so lucide-react resolves once at 1.16.0 and removes the nested fumadocs-ui/lucide-react entry.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant CI as CI / Deploy Build
participant Build as apps/fumadocs build
participant Identity as check-module-identity.ts
participant Vite as vite build
participant Bundle as check-client-bundle.ts
participant Output as Static client assets

CI->>Build: bun run build
Build->>Identity: verify app and fumadocs-ui singleton resolutions
alt duplicated physical installs
    Identity-->>Build: exit 1 with resolved paths
    Build-->>CI: fail before bundling
else single physical installs
    Identity-->>Build: ok
    Build->>Vite: build docs app
    Vite->>Output: write client chunks
    Build->>Bundle: scan emitted JS chunks for FrameworkProvider marker
    alt context marker in multiple chunks
        Bundle-->>Build: exit 1 with chunk names
        Build-->>CI: fail post-build
    else zero or one context chunk
        Bundle-->>Build: ok
        Build-->>CI: build continues to ensure-root-index
    end
end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant CI as CI / Deploy Build
participant Build as apps/fumadocs build
participant Identity as check-module-identity.ts
participant Vite as vite build
participant Bundle as check-client-bundle.ts
participant Output as Static client assets

CI->>Build: bun run build
Build->>Identity: verify app and fumadocs-ui singleton resolutions
alt duplicated physical installs
    Identity-->>Build: exit 1 with resolved paths
    Build-->>CI: fail before bundling
else single physical installs
    Identity-->>Build: ok
    Build->>Vite: build docs app
    Vite->>Output: write client chunks
    Build->>Bundle: scan emitted JS chunks for FrameworkProvider marker
    alt context marker in multiple chunks
        Bundle-->>Build: exit 1 with chunk names
        Build-->>CI: fail post-build
    else zero or one context chunk
        Bundle-->>Build: ok
        Build-->>CI: build continues to ensure-root-index
    end
end
Loading

Comments Outside Diff (2)

  1. General comment

    P1 /docs/quickstart is not a valid public docs page in the built preview

    • Bug
      • The requested smoke route http://127.0.0.1:3000/docs/quickstart loaded successfully at the browser level but returned HTTP 404 and rendered the app's Page not found screen. The browser log records status 404 for /docs/quickstart with body text beginning 404 Page not found That docs route is not here, so the PR verification claim that this key public page loads/hydrates cleanly is not satisfied.
    • Cause
      • The built fumadocs route manifest/content output does not include /docs/quickstart, or the quickstart content has moved without a redirect/alias for this expected public URL.
    • Fix
      • Restore or alias the quickstart docs route at /docs/quickstart, or update the public navigation/verification target to the correct canonical quickstart URL and add a redirect from the stale route if it was previously public.

    T-Rex Ran code and verified through T-Rex

  2. General comment

    P2 Real browser smoke still records console resource errors on public pages

    • Bug
      • The Chromium smoke captured multiple console error events while loading the requested pages, including failed external simpleicons resources on /, repeated 404 resource errors, and aborted /_vercel/insights/script.js requests on the preview server. This is not the FrameworkProvider crash, but it contradicts the validation objective's requirement for no console errors on the smoke pages.
    • Cause
      • The built app references resources that are unavailable or blocked in the local preview environment, including Vercel Analytics' /_vercel/insights/script.js path and external simpleicons image URLs that Chromium blocked with ERR_BLOCKED_BY_RESPONSE.NotSameOrigin.
    • Fix
      • Gate analytics/script loading in local preview/test environments and replace or proxy external icon resources so local preview does not emit browser console errors; alternatively document and filter intentionally expected third-party resource failures in the smoke test if they are accepted.

    T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "fix(docs): unify lucide-react so fumadoc..." | Re-trigger Greptile

@leoisadev1
leoisadev1 merged commit 9e963ec into main Jul 7, 2026
4 checks passed
@leoisadev1
leoisadev1 deleted the posthog-code/fix-fumadocs-core-peer-split branch July 7, 2026 17:41

This branch was successfully deployed

1 active deployment
Preview — b1bbb93f Deployed Jul 7, 2026 by vercel[bot]
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