feat(web): port provider stack (Auth, Organization, Feature Flags) from platform repo#31099
Conversation
Add the foundational Auth, Organization, and Feature Flag providers that every domain (Chat, Settings, Library, Onboarding) depends on. - Auth provider (allauth-client, csrf, auth-provider): session probe on mount, re-validate on focus/visibility, cross-tab logout sync via BroadcastChannel - Organization provider: resolves active org from sessionStorage, keeps module-level request state in sync for API interceptors - Feature flag provider: typed context with all current feature flags - API interceptors: attaches Vellum-Organization-Id header and X-CSRFToken on mutating requests to both auth and platform clients - AppProviders: scope-keyed QueryClient (by user+org) so switching users/orgs yields fresh cache - Updated main.tsx to compose the full provider tree - Upgraded tsconfig lib to ES2023 for Array.findLast support - Removed dead query-client.ts (replaced by scope-keyed provider) Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
The provider stack imports from generated HeyAPI clients which are gitignored. CI needs to regenerate them from the committed OpenAPI specs before typecheck and build can resolve the imports. Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a927cc91e4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| import { client as authClient } from "@/generated/auth/client.gen.js"; | ||
| import { client as platformClient } from "@/generated/api/client.gen.js"; |
There was a problem hiding this comment.
Add generation before importing gitignored HeyAPI clients
These imports make a fresh checkout depend on apps/web/src/generated/**, but that directory is gitignored (apps/web/.gitignore) and there are no tracked generated files (git ls-files apps/web/src/generated/** returns 0). I also checked the web PR workflow: after install it runs only lint, bun run typecheck, and build, with no bun run openapi-ts step, so CI and local bun run typecheck/bun run build will fail with missing @/generated/... modules unless the clients are generated as part of install/build/CI or committed despite the ignore.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 8a79627a4b — added bun run openapi-ts codegen step to pr-web.yaml before lint/typecheck/build. The OpenAPI specs are committed at apps/web/openapi-schemas/, so CI can regenerate the clients from them.
There was a problem hiding this comment.
✦ APPROVE
Value: Every parallel domain migration session (Chat, Settings, Library) now has a shared, production-grade auth/org/feature-flag foundation to import from instead of independently stubbing useAuth() — eliminating the merge conflicts and drift that stub-per-domain would have caused.
What this does: Ports AuthProvider, OrganizationProvider, FeatureFlagProvider, the HeyAPI request interceptors (Vellum-Organization-Id + X-CSRFToken), and a scope-keyed AppProviders composition from vellum-assistant-platform → apps/web/src/lib/. Removes the dead query-client.ts. Adds a bun run openapi-ts codegen step to pr-web.yaml (CI was missing it; generated files are gitignored). Upgrades tsconfig.json to ES2023 for Array.findLast support in CSRF cookie parsing.
Codex P1 — resolved ✅
Codex flagged (on stale commit a927cc91) that @/generated/ imports would fail CI because the directory is gitignored and no codegen step existed. Devin fixed this in 8a79627a by adding bun run openapi-ts before lint/typecheck/build in pr-web.yaml. The OpenAPI specs are committed at apps/web/openapi-schemas/, so CI can always regenerate. Verified: pr-web.yaml diff adds the step at the correct position (post-install, pre-lint). ✅
Architecture observations:
organization-state.ts — module-level state with useSyncExternalStore-compatible subscription. Correct pattern per KB: external stores (module-level) use useSyncExternalStore to avoid tearing, not useEffect. subscribeToActiveOrganizationIdForRequests returns a stable unsubscribe function and moves subscribe/getSnapshot to module scope. ✅
app-providers.tsx — the two-layer QueryClient design is thoughtful: AuthScopedQueryClientProvider (keyed by user identity) wraps OrganizationProvider so org-list queries survive org switches; ScopeKeyedQueryClientProvider (keyed by user+org) wraps domain queries so the domain cache flushes on org switch. Data leakage across account switches is prevented. ✅
api-interceptors.ts — interceptors install at the HeyAPI client level, before globalThis.fetch is called. Capacitor/iOS safe: this is header injection, not SSE consumer wiring, so CapacitorHttp's fetch interception doesn't affect it. ✅
BroadcastChannel auth sync — correctly guarded with if (typeof BroadcastChannel === "undefined") return; and the cleanup closes the channel. ✅
Non-blocking observations:
-
refreshOrganizationsdep on fullorganizationsQueryobject —useCallback(async () => { await organizationsQuery.refetch(); }, [organizationsQuery])uses the whole query object as a dep. React Query'suseQueryreturns a new object reference on every render (even when data hasn't changed), sorefreshOrganizationsis recreated on every render. Since it's in theuseMemodeps for the contextvalue,useOrganization()consumers re-render on every parent render cycle. Perf non-issue today (few consumers), but worth fixing before more components subscribe. Fix: destructurerefetchfirst and dep on[refetch], which React Query guarantees is stable:const { refetch: refetchOrgs } = organizationsQuery; const refreshOrganizations = useCallback(async () => { await refetchOrgs(); }, [refetchOrgs]);
-
initSessionmissingcancelledguard — KB requires acancelledflag in asyncuseEffectcallbacks (PR #6726 evidence).AuthProvideris root-level and won't unmount in practice, so this is harmless today. Leaving a note for if the hook shape ever changes. -
refreshSession()in focus/visibility handlers unguarded —onFocusandonVisibilityChangecallrefreshSession()but don't.catch()the returned promise. IfgetSession()throws on a network failure, this becomes an unhandled rejection that Sentry would capture as noise. Trivial fix:refreshSession().catch(err => console.warn("Session refresh failed", err)).
Merge gate:
- ✅ Vex approves at
8a79627a - ⏳ Devin reviewed at HEAD (COMMENTED — needs to post explicit "No Issues Found" or APPROVE for merge criteria)
- ⏳ CI — confirm
bun run openapi-tsstep passes in the new workflow
Vellum Constitution — Yours: every domain migration session now shares a single, correct auth identity — no more per-session stubs that quietly diverge.
- Destructure refetch from organizationsQuery to stabilize the refreshOrganizations callback reference (React Query guarantees refetch is stable, but the full query object is recreated per render) - Add cancelled guard to initSession useEffect to prevent state updates after unmount - Wrap refreshSession calls in focus/visibility handlers with .catch() to prevent unhandled rejection noise on network failures Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
97d633a
|
All 3 non-blocking observations fixed in
|
The provider stack PR (#31099) introduced imports from generated HeyAPI clients but only added the codegen step to pr-web.yaml. The main branch CI workflow was missing it, causing type check failures on every push to main. Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Prompt / plan
Port the foundational Auth, Organization, and Feature Flag providers from
vellum-assistant-platformto the assistant repo. These are cross-domain shared utilities required by every domain (Chat, Settings, Library, Onboarding) — without them, every parallel domain migration session would independently stubuseAuth(),useAppFeatureFlags(), anduseOrganization(), creating inconsistency and merge conflicts.Platform files frozen in migration table: PR #7198.
What's included
lib/auth/allauth-client.ts,lib/auth/csrf.ts,lib/auth/auth-provider.tsxAuthProvidercontext (session probe on mount, re-validate on focus/visibility, cross-tab logout via BroadcastChannel)lib/organization/organization-state.ts,lib/organization/organization-provider.tsxuseSyncExternalStore-compatible),OrganizationProvidercontext (fetches orgs via React Query, resolves active org, syncs to request interceptor)lib/feature-flags/feature-flag-provider.tsxAppFeatureFlagProvider+useAppFeatureFlags()context with all current flagslib/api-interceptors.tsVellum-Organization-Idheader andX-CSRFTokenon mutating requests for both auth and platform HeyAPI clientslib/providers/app-providers.tsxChanges from platform source
typeof window,typeof document) — SPA always has DOMProviderSignupContext.accounttyped correctly against generatedProviderAccount(platform had drift)libfrom ES2022 → ES2023 forArray.findLastsupport in CSRF cookie parsingquery-client.ts(replaced by scope-keyed provider inAppProviders)Alternatives not taken
keyprop remounting.Test plan
bunx tsc --noEmitpasses (zero type errors)bun run lintpasses (zero lint errors)Link to Devin session: https://app.devin.ai/sessions/536ceadb023a4059908b0609b9833bc1
Requested by: @ashleeradka