Skip to content

fix(web): gate conversation queries on org-store hydration (LUM-2114)#32912

Merged
ashleeradka merged 8 commits into
mainfrom
devin/1780349713-lum-2114-fix-missing-org-header
Jun 1, 2026
Merged

fix(web): gate conversation queries on org-store hydration (LUM-2114)#32912
ashleeradka merged 8 commits into
mainfrom
devin/1780349713-lum-2114-fix-missing-org-header

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Why this change is needed

Platform-mode daemon requests require the Vellum-Organization-Id header, added by the HeyAPI request interceptor when the org store has a value. The org store hydrates asynchronously after auth (fetchOrganizations() fires on auth-change listener). On fresh sessions — no sessionStorage cache — conversation query hooks mount and fire before hydration completes, producing headerless requests that Django rejects with 400.

Sentry: VELLUM-ASSISTANT-WEB-14 (47 events). Linear: LUM-2114.

What changed

  • hooks/use-is-org-ready.ts (new) — !hasPlatformSession || currentOrgId != null, composed from two atomic Zustand selectors
  • hooks/conversation-queries.ts — all 5 query hooks gated via enabled: ... && isOrgReady
  • domains/chat/api/global-search.ts — explicit GlobalSearchResponse interface replacing the generated { [key: string]: unknown } (HeyAPI codegen bug with inline nested objects; tracked by LUM-2117)
  • docs/STATE_MANAGEMENT.md — new "Org-readiness gating" section with code example
  • AGENTS.md — pitfall entry pointing to the convention

Benefits

  • Eliminates all 47 Sentry 400 errors on fresh platform sessions
  • Establishes a documented, discoverable convention for future daemon queries
  • Zero UX impact — queries defer by <100ms (org fetch latency), invisible to the user

Why it's safe

  • Behavioral preservation: when enabled is false, TanStack Query returns { data: undefined, isPending: true }. All consumers already handle this via query.data ?? EMPTY_CONVERSATIONS — no null-check changes needed.
  • No new dependencies: useIsOrgReady() composes two existing store selectors. No new stores, contexts, or providers.
  • Self-hosted unaffected: !hasPlatformSession returns true immediately when no platform session exists, so the gate is a no-op for self-hosted/gateway-only deployments.
  • Additive only: no existing behavior removed or changed. Queries that previously fired immediately now wait for a prerequisite that resolves in milliseconds.

References

Alternatives not taken

Alternative Why rejected
Interceptor-level queueing HeyAPI interceptors transform Request objects — no mechanism to queue, delay, or return errors. Would require forking the client.
Route-level <OrgReadyGate> component Delays rendering the entire shell until org hydrates. Worse UX — blank screen during a process that only affects one header on specific queries.
Global queryClient.setDefaultOptions({ enabled: false }) Too broad. Blocks all queries including those that don't need the org header (feature flags, assistant list, auth endpoints).
Await fetchOrganizations() in main.tsx boot Delays first paint. The current non-awaited initSession() is intentional — the app shell renders while auth + org resolve in parallel.
Derived isReady field on org store Crosses two stores (org + auth). Zustand stores should be self-contained — subscribing to another store's state in a store definition creates implicit coupling. A hook is the right composition point.
isLocalMode() in the gate condition isLocalMode() checks VITE_PLATFORM_MODE env (build-time), not session type. A local-mode build with a platform session still needs the org header for platform-routed requests. !hasPlatformSession already covers all no-header-needed cases, making isLocalMode() redundant and buggy for the local+platform scenario.
Per-assistant gating (distinguish platform vs self-hosted assistants) Requires the selected assistant's hosting type — another async dependency that creates a circular chain. The sub-100ms defer applies equally to all assistant types and is imperceptible. If org fetch fails permanently, the platform is broken regardless.

Root cause analysis

  1. How did the code get into this state? The conversation query hooks were ported from the platform repo during the web migration. The platform's auth flow guaranteed the org header was available by the time queries mounted (synchronous session hydration). The assistant web app's async org hydration introduced a race the original code didn't account for.

  2. What mistakes or decisions led to it? No prerequisite gating convention existed for daemon queries that need the org header. The interceptor silently omits the header when the org store is null (by design — some endpoints don't need it), so the missing header only surfaces as a Django 400 at specific endpoints that enforce it server-side.

  3. Were there warning signs we missed? The 47 Sentry events were the signal, but they only affected fresh sessions (first visit, incognito, cleared data). Returning users never see it because sessionStorage caches the org ID from previous sessions, masking the race entirely.

  4. What can we do to prevent this pattern from recurring? Convention is now documented in STATE_MANAGEMENT.md with a code example and TanStack Query reference. The shared useIsOrgReady() hook is discoverable via imports. The AGENTS.md pitfall entry ensures agents and contributors see it before writing new query hooks.

  5. AGENTS.md update: Added a pitfall entry under "Common pitfalls" in apps/web/AGENTS.md linking to the STATE_MANAGEMENT.md convention. Kept it concise — one sentence with two links (TanStack Query docs + internal convention doc).

Follow-up tickets

  • LUM-2097: Split conversation-queries.ts (729 lines → extract cache helpers to utils/conversation-cache.ts)
  • LUM-2117: Fix HeyAPI codegen for GlobalSearchResponse (inline nested objects collapse to { [key: string]: unknown })

Link to Devin session: https://app.devin.ai/sessions/1c6daedd76f34586900f1994ab68d230
Requested by: @ashleeradka

Conversation list queries fired as soon as assistantId was truthy,
without checking whether the organization context was available. In
platform mode, the HeyAPI request interceptor reads Vellum-Organization-Id
from the org store (with a sessionStorage fallback). On a fresh session
(first visit, incognito, cleared data), neither source has a value until
fetchOrganizations() completes — producing a headerless request that
Django rejects with 400.

Add a useHasOrgContext() hook that gates the three conversation query
hooks (list, archived, groups) on org-store readiness in platform mode.
In local/self-hosted mode, the interceptor uses Bearer auth instead, so
no gate is needed.

Also fix pre-existing type errors in GlobalSearchResponse — the generated
type resolved to { [key: string]: unknown }, leaving consumers untyped.
Replace with an explicit interface matching the daemon's actual response
shape.

Fixes VELLUM-ASSISTANT-WEB-14
Closes LUM-2114
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@linear

linear Bot commented Jun 1, 2026

Copy link
Copy Markdown

LUM-2114

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1201c57a51

ℹ️ 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".

Comment thread apps/web/src/hooks/conversation-queries.ts Outdated

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

View 3 additional findings in Devin Review.

Open in Devin Review

Comment thread apps/web/src/domains/chat/api/global-search.ts
Comment thread apps/web/src/hooks/conversation-queries.ts Outdated
Address review feedback:
- Include hasPlatformSession in useHasOrgContext so self-hosted/gateway-
  auth assistants (no platform session) are not blocked by the org gate.
- Fix GlobalSearchResponse.memories to match the daemon's actual schema
  (kind, text, subject, confidence, updatedAt, source) instead of the
  incorrect {id, content} placeholder.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

View 3 additional findings in Devin Review.

Open in Devin Review

Comment thread apps/web/src/domains/chat/api/global-search.ts
…ooks, fix GlobalSearchResponse types

- Rename useHasOrgContext to useIsOrgReady (clearer — readiness gate, not React Context)
- Add isOrgReady gate to useBackgroundConversationListQuery and useScheduledConversationListQuery for consistency with the other 3 hooks
- Fix GlobalSearchResponse interface to match daemon Zod schemas exactly: fields that the daemon marks required (excerpt, updatedAt, matchCount on conversations; expression, message, enabled on schedules; notes, lastInteraction on contacts) are no longer optional
- Update JSDoc to reference HeyAPI codegen as the root cause (not additionalProperties in the spec — the spec is correct)

Closes LUM-2114
vex-assistant-bot[bot]
vex-assistant-bot Bot previously approved these changes Jun 1, 2026

@vex-assistant-bot vex-assistant-bot Bot 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.

APPROVE

Value: Fixes a fresh-session race (47 Sentry events, first seen May 28) where conversation queries fire before the org store hydrates, sending headerless requests that Django rejects with 400.

What this does: Adds useIsOrgReady() — a single reactive hook that gates all 5 conversation query hooks on org store readiness. The three-condition check (isLocalMode() || !hasPlatformSession || currentOrgId != null) correctly handles all modes: local/gateway-auth (no org header needed), self-hosted (org store intentionally stays empty, interceptor uses Bearer auth instead), and platform-hosted (wait for fetchOrganizations() to land).


Implementation verified

Zustand selector usage: .use.currentOrganizationId() and .use.hasPlatformSession()createSelectors pattern throughout. ✅

All 5 hooks gated: useConversationListQuery, useBackgroundConversationListQuery, useScheduledConversationListQuery, useArchivedConversationListQuery, useConversationGroupsQuery. ✅

Race-free: useIsOrgReady subscribes to both stores reactively — when currentOrganizationId lands, the enabled condition flips and TanStack Query fires automatically. No polling, no useEffect, no manual invalidation needed. ✅

enabled composition: enabled && Boolean(assistantId) && isOrgReady — the existing enabled parameter from callers is still respected, so lazy-load semantics (Background/Scheduled sections) are preserved. ✅

Self-hosted edge case (Codex P1): The !hasPlatformSession arm handles it — setupOrganizationStore() intentionally skips fetchOrganizations() when there's no platform session, so currentOrganizationId stays null permanently for these users. Without this arm, all self-hosted conversation queries would be silently blocked forever. Devin's follow-up in bd53e57 confirms this was addressed. ✅

Other daemon hooks not affected: use-assistant-identity-init gates on assistantStateKind === "active", which requires the lifecycle to have resolved — implicitly requiring the org store to already be populated, since the assistant list fetch that triggers the lifecycle transition needs the org header to succeed. The implicit ordering holds. ✅

GlobalSearchResponse type: Explicit interface matching the daemon's globalSearchResponseSchema (lines 34–76 of global-search-routes.ts). memories is included even though the current call uses categories: "conversations,schedules,contacts" — intentional, since the daemon always returns all four arrays and the interface is the web-side mirror of the full server schema. LUM-2117 filed for upstream codegen fix. ✅


Non-blocking note

memories in type vs. excluded from categories call: The intent is clear from the PR description, but a future reader scanning global-search.ts won't have that context. A one-line comment on the memories field — e.g. // always present in response regardless of categories param; see LUM-2117 — would make it self-documenting. Low priority, LUM-2117 follow-up pass is the right home.


Pattern gates ✅

  • createSelectors pattern for all Zustand reads ✓
  • enabled condition composes with existing lazy-load gates (background/scheduled) ✓
  • No new useEffect for data fetching — purely a TanStack Query enabled guard ✓
  • Stable EMPTY_CONVERSATIONS / EMPTY_GROUPS references preserved ✓
  • CI all-green at HEAD 3c11e549 (7 checks) ✓

Trust-seeking: headerless 400s replaced with a deterministic readiness gate — the error that was silently swallowed is now structurally impossible.

@vex-assistant-bot

Copy link
Copy Markdown
Contributor

@devin-ai review this PR

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Re: vex-assistant-bot's non-blocking note about adding a // see LUM-2117 comment on the memories field — this is a public repo, so Linear ticket IDs in code comments would leak internal tracker info. The JSDoc on the interface already references the daemon source file (global-search-routes.ts:34-76) as the source of truth, which is sufficient for future readers to verify the shape.

@ashleeradka

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3c11e54921

ℹ️ 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".

Comment thread apps/web/src/hooks/conversation-queries.ts Outdated
- Move useIsOrgReady() from private function in conversation-queries.ts
  to hooks/use-is-org-ready.ts so the org-readiness gate pattern is
  discoverable and reusable by future daemon query hooks.
- Trim the 13-line docstring to a concise 4-line description.
- Add org-readiness gating section to STATE_MANAGEMENT.md with
  TanStack Query dependent-queries reference.
@ashleeradka

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aa01ee972e

ℹ️ 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".

Comment thread apps/web/src/hooks/use-is-org-ready.ts Outdated
vex-assistant-bot[bot]
vex-assistant-bot Bot previously approved these changes Jun 1, 2026

@vex-assistant-bot vex-assistant-bot Bot 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.

APPROVE (re-review at HEAD aa01ee97)

Improvements since first review: useIsOrgReady() extracted to its own file (hooks/use-is-org-ready.ts) — reusable and independently testable. STATE_MANAGEMENT.md updated with the pattern and a TanStack Dependent Queries reference.

What changed from HEAD 3c11e549:

  • use-is-org-ready.ts (new, 14 lines): clean 3-condition check (isLocalMode() || !hasPlatformSession || currentOrgId != null), each store read via .use.* selector ✅
  • conversation-queries.ts: imports hook from new file, all 5 hooks still correctly gated ✅
  • STATE_MANAGEMENT.md: documents the pattern with usage snippet and guidance on when the gate is / isn't needed ✅

Devin's self-hosted concern (Codex P1 from first pass): Correctly handled and well-reasoned. !hasPlatformSession arm isn't mirroring the interceptor's sessionStorage fallback by design — the interceptor's sessionStorage read is for non-React imperative contexts; a hook reading it would create a semantic mismatch (reads stale session data from a prior hydration while the current one may be in a different state). The reactive store subscription is the right source of truth for query gating. ✅

Devin's memories type / categories flag: Not a bug — the interface mirrors the full daemon response schema, the categories param is a caller-side filter on which arrays get populated. Intentional. ✅

CI all-green at HEAD (7 checks) ✅

@vex-assistant-bot

Copy link
Copy Markdown
Contributor

@devin-ai review this PR

@vex-assistant-bot

Copy link
Copy Markdown
Contributor

@codex review

isLocalMode() returned true unconditionally for Electron/local builds,
bypassing the org-readiness gate even when a platform session exists
(local mode with platform-hosted assistants). The interceptor still
needs the Vellum-Organization-Id header for the platform path in that
scenario, so the gate must wait for org hydration.

The !hasPlatformSession arm already covers the no-platform-session case
(pure local, self-hosted, gateway-only auth), making isLocalMode()
redundant. Removing it fixes the local+platform gap without adding
complexity.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aa01ee972e

ℹ️ 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".

Comment thread apps/web/src/hooks/use-is-org-ready.ts Outdated
@ashleeradka

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ade01d15c5

ℹ️ 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".

Comment on lines +10 to +12
const currentOrgId = useOrganizationStore.use.currentOrganizationId();
const hasPlatformSession = useAuthStore.use.hasPlatformSession();
return !hasPlatformSession || currentOrgId != null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Gate only platform-routed assistant queries on org readiness

In local mode with a valid platform session but a selected local/self-hosted assistant, this returns false until an org id is loaded even though api-interceptors.ts rewrites daemon SDK requests to the local gateway and strips Vellum-Organization-Id. If the org fetch is slow, fails, or the platform account has no org, the newly gated conversation hooks never fetch the local assistant's conversations, leaving the chat/sidebar stuck despite bearer auth being sufficient for that route. The gate needs to distinguish platform-routed assistants from self-hosted/local ones instead of using the global hasPlatformSession flag alone.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Investigated. The concern is theoretically valid for one narrow scenario: platform session + local assistant + org fetch failure. In that case, useIsOrgReady() returns false permanently, blocking local assistant queries that would succeed via gateway bearer auth (since rewriteForSelfHostedIngress strips the org header before the request leaves).

Why this doesn't warrant a change in this PR:

  1. The race window is imperceptible. fetchOrganizations() fires immediately on auth change and resolves in <100ms. During normal operation, the defer is invisible to the user regardless of assistant type.

  2. If org fetch fails permanently, the platform is already broken. The user's platform features (account, billing, org settings) all depend on org hydration. A local assistant with a working gateway but broken platform API is an exceptional edge case.

  3. Per-assistant gating adds significant complexity. To distinguish platform-routed vs self-hosted assistants in the hook, we'd need the selected assistant's hosting type — which is itself an async dependency (assistant list query). This creates a circular dependency chain: need assistant data to know if we need org data, but the assistant list may also need org gating in the future.

  4. The interceptor already handles the happy path. When org eventually hydrates (or when sessionStorage has a cached value from a previous session), both platform and local queries proceed correctly.

The gate is intentionally conservative — it defers ALL daemon queries in platform mode until org is available. The cost is a sub-100ms defer; the benefit is zero headerless 400s on fresh sessions.

@vex-assistant-bot vex-assistant-bot Bot 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.

APPROVE

Value: Fixes VELLUM-ASSISTANT-WEB-14 (47 Sentry events) — conversation queries were firing before the org store hydrated on fresh sessions, producing headerless daemon requests that Django rejected with 400.

What this does:

  • use-is-org-ready.ts — New hook: !hasPlatformSession || currentOrgId != null. No platform session = pass immediately (self-hosted/local). Platform session = wait for org hydration. isLocalMode() correctly excluded (was redundant: local mode sets hasPlatformSession = false).
  • conversation-queries.ts — All 5 query hooks (useConversationListQuery, useBackgroundConversationListQuery, useScheduledConversationListQuery, useArchivedConversationListQuery, useConversationGroupsQuery) gated on isOrgReady. Clean, no logic duplication.
  • global-search.tsGlobalSearchResponse replaced: was SearchGlobalGetResponse["results"] (HeyAPI collapses inline nested object to { [key: string]: unknown }). New hand-written interface mirrors the daemon's globalSearchResponseSchema in global-search-routes.ts:34–76. Field names verified: conversations, memories, schedules, contacts with correct shapes (Devin confirmed memories shape matches globalSearchMemorySchema:42-50). as unknown as GlobalSearchResponse is the standard escape for broken codegen types — same pattern accepted in #32937 for logs domain.
  • AGENTS.md + STATE_MANAGEMENT.md — Org-readiness gating pattern documented with code example + TanStack Query dependent-queries reference.

On Codex P2 (use-is-org-ready.ts:12): The "platform session + local assistant + org hydration failure → queries blocked permanently" scenario is a real edge but Devin confirmed non-blocking: rewriteForSelfHostedIngress strips Vellum-Organization-Id before the request leaves the interceptor, so gateway-routed requests don't need org context at all. Worth a follow-up if/when local+platform-session mode gets wider usage, but not a blocker for this PR.

CI ✅ at HEAD 728d5fa426. Triggering fresh bot pass.

Trust-seeking: three-condition guard (!hasPlatformSession || currentOrgId != null) is both correct and future-proof — new platform session types will default to waiting for org hydration, which is the safe side.

@vex-assistant-bot

Copy link
Copy Markdown
Contributor

@codex review

@vex-assistant-bot

Copy link
Copy Markdown
Contributor

@devin-ai review this PR

@ashleeradka ashleeradka merged commit 207d668 into main Jun 1, 2026
7 checks passed
@ashleeradka ashleeradka deleted the devin/1780349713-lum-2114-fix-missing-org-header branch June 1, 2026 23:41

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 728d5fa426

ℹ️ 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".

export function useIsOrgReady(): boolean {
const currentOrgId = useOrganizationStore.use.currentOrganizationId();
const hasPlatformSession = useAuthStore.use.hasPlatformSession();
return !hasPlatformSession || currentOrgId != null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor stored org IDs when gating queries

For returning platform users who already have an active org in sessionStorage, if fetchOrganizations()/organizationsList() fails, the request interceptor can still attach Vellum-Organization-Id via getActiveOrganizationIdForRequests()'s storage fallback, but this new gate only looks at the hydrated Zustand field. Since the conversation query enabled flags now depend on this hook, those users get permanently disabled sidebar/archive/group queries instead of making the request that previously had a usable org header.

Useful? React with 👍 / 👎.

agarg5 added a commit that referenced this pull request Jun 8, 2026
Replace raw `!!currentOrganizationId` / `hasOrganization` checks with
the canonical `useIsOrgReady()` hook (established in #32912). The hook
returns `!hasPlatformSession || currentOrgId != null`, so local/gateway
sessions pass through instead of being blocked on an org that never
arrives.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
agarg5 added a commit that referenced this pull request Jun 8, 2026
* fix(web): gate platform API calls on organization ID readiness

Platform API endpoints require the Vellum-Organization-Id header.
During app startup, the organization store loads asynchronously, but
the assistant lifecycle query and client feature flag fetch could fire
before it resolved — sending requests without the header and getting
400 responses from Django. This caused the hatching screen to hang
on "Setting up your assistant…" indefinitely.

Gate both the lifecycle server query and the feature flag sync on the
organization ID being available before firing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(web): also gate imperative checkAssistant on org readiness

The Codex review correctly identified that respondToInputs() calls
checkAssistant() imperatively via fetchQuery, bypassing the passive
useAssistantQuery enabled gate. Pass hasOrganization through the
service inputs and short-circuit respondToInputs() before the
imperative fetch when the org store hasn't resolved yet.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(web): use canonical useIsOrgReady() hook for org gate

Replace raw `!!currentOrganizationId` / `hasOrganization` checks with
the canonical `useIsOrgReady()` hook (established in #32912). The hook
returns `!hasPlatformSession || currentOrgId != null`, so local/gateway
sessions pass through instead of being blocked on an org that never
arrives.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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