Render the real shell from the gate's verified identity — delete the full-page skeleton#986
Merged
Merged
Conversation
…full-page skeleton The SSR gate already unseals the session per document request, so the loading silhouette was rendering 'we don't know who you are' on requests where the server knew exactly who it was serving. Now: - the gate resolves the auth hint server-side (browser cookie when it matches the verified ids, else minted fresh — org name from the local mirror) and threads it to the SSR render through middleware context; the root loader dehydrates it so the first client render agrees by construction - AuthProvider takes an initialHint: SSR paints the authenticated shell outright; hosts without the seam (self-host) keep the post-mount cookie read - org-less sessions are redirected to /create-org at the edge, so the client AuthGate's org branch only covers mid-session transitions - ShellSkeleton is deleted; the remaining placeholder is a neutral blank screen for moments between redirects The first-ever load on a fresh browser now paints the real shell with the user's identity before /account/me resolves — previously the one case that still sat on a skeleton for the whole round trip.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-marketing | 0c81edb | Commit Preview URL Branch Preview URL |
Jun 12 2026, 10:13 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-cloud | 0c81edb | Jun 12 2026, 10:14 PM |
Contributor
Cloudflare previewTorn down — the PR is closed. |
This was referenced Jun 12, 2026
RhysSullivan
added a commit
that referenced
this pull request
Jul 2, 2026
The cloud e2e project never gated CI either, so ten scenarios rotted. Refresh the four whose product behavior moved intentionally: - connect-card-ssr-origin: install URLs are org-slug-scoped since the org-slug console URLs change (#974); accept the slug form. - connection-owner-isolation: /api/auth/switch-organization was deleted with cookie-based org switching (#1000); switch orgs the way the web client does, via the x-executor-organization selector header. - oauth-connections: the popup-state fix (#1235) envelopes the callback state as base64url JSON; decode it and assert the inner state + orgSlug. - unauthenticated-skeleton: the 404 page shipped as a standalone page in the same commit as the shell-framed assertion (#986); assert the page it actually renders. Quarantine the six that need product/harness work, each with a reason: mcp-browser-approval-org-scope + the two browser-approval scenarios (cloud-only: the mcporter browser-approval completion never lands), cli-device-login (device-flow terminal never reaches the emulator), and run-panel-auto-approve (autoApprove leaves the run paused; never green since the feature landed in #1183).
RhysSullivan
added a commit
that referenced
this pull request
Jul 2, 2026
* e2e: fix stale docs, harden dev-CLI status, add cloud+selfhost CI jobs - e2e/AGENTS.md: the anatomy example predated the service-yielding scenario() signature (no more needs/ctx); capability notes said browser was cloud-only and mcp-oauth selfhost-only, both wrong per targets/*.ts; file placement now lists cloudflare/, local/, cli/; document summary, motel, test:* scripts, the viewer/ SPA, pr-media, and the Windows desktop/cli VM targets. - e2e dev CLI status: probe the app URL before reporting ready (a zombie runner with a dead server used to read as healthy), and only parse real state files in .dev/ (cloud.journey.json rendered as a garbage DEAD line). - CI: run the cloud and selfhost e2e projects on every PR/push with failure artifacts (trace.zip, session.mp4, step screenshots) uploaded per target. * Fix the MCP regressions and policy gaps the e2e suite caught Cloud (hibernatable MCP DO rework fallout): - server.ts no longer gates MCP dispatch behind the Axiom tracer install: with AXIOM_TOKEN unset (any dev boot without motel) every /mcp request fell through to the SPA router and 404ed. - agent-handler mounts a second serve() on /mcp/toolkits/:slug — the agents SDK builds an exact-match URLPattern, so the single /mcp handler never saw toolkit paths. - Restore the old envelope's transport contract: JSON-RPC 405 for verbs outside GET/POST/DELETE/OPTIONS (was a bare 404), 200 for session DELETE (agents SDK answers 204), and a reconnect-worded 404 for requests that race a condemned DO's abort. Selfhost (org-scoped MCP OAuth discovery): - The org-segment strip middleware now carries the original pathname in an internal header, and the protected-resource metadata echoes it, so a client that dialed /<org>/mcp/... passes the MCP SDK's RFC 9728 resource check. Bare paths are untouched; the header is stripped from unrewritten requests. Microsoft Graph URL policy: - microsoftHttpPlugin gains the hosts' local-network dev posture: selfhost, cloud, and the cloudflare host thread allowLocalNetwork into allowUnsafeUrlOverrides, and the override now also admits plain-http loopback URLs (local emulators). Production behavior is unchanged: the flag is unset there, and non-loopback http stays rejected even with it. Stale e2e assertion refreshed for an intentional product change: - tool-descriptions: the execute inventory is names-only since the skills tool slimming; drop the per-connection description assertions. * test(e2e): repair self-host scenarios and gate the suite in CI The self-host e2e project never ran in CI, so it drifted red while the app moved on. Repair the failing scenarios (stale connect-modal selectors, a racy action-bar position read, a shared-admin connection-count assertion, a multi-tenant-only org-slug 404 step, and a cloud-shaped toolkit MCP URL), add a documented skip affordance to the scenario helper, and quarantine the two Microsoft emulator scenarios that need a canonical block-YAML Graph spec (tracked separately). Cherry-picked from origin/fix-selfhost-e2e-and-ci (PR #1239); its CI job is superseded by the cloud+selfhost matrix job already on this branch. * test(e2e): quarantine the two agents-SDK transport gaps Both are real gaps in the hibernatable Agent bridge (standalone SSE supersede never resolves; response routing scopes JSON-RPC ids per session instead of per stream), not regressions on this branch. Skip with reasons so the suite gates CI while the gaps stay visible; fixing the bridge is tracked separately. * test(e2e): repair or quarantine the cloud scenarios that drifted on main The cloud e2e project never gated CI either, so ten scenarios rotted. Refresh the four whose product behavior moved intentionally: - connect-card-ssr-origin: install URLs are org-slug-scoped since the org-slug console URLs change (#974); accept the slug form. - connection-owner-isolation: /api/auth/switch-organization was deleted with cookie-based org switching (#1000); switch orgs the way the web client does, via the x-executor-organization selector header. - oauth-connections: the popup-state fix (#1235) envelopes the callback state as base64url JSON; decode it and assert the inner state + orgSlug. - unauthenticated-skeleton: the 404 page shipped as a standalone page in the same commit as the shell-framed assertion (#986); assert the page it actually renders. Quarantine the six that need product/harness work, each with a reason: mcp-browser-approval-org-scope + the two browser-approval scenarios (cloud-only: the mcporter browser-approval completion never lands), cli-device-login (device-flow terminal never reaches the emulator), and run-panel-auto-approve (autoApprove leaves the run paused; never green since the feature landed in #1183). * lint: suppress the adapter-boundary error checks in the MCP agent handler The condemned-DO abort surfaces as a plain runtime Error thrown out of the agents SDK's serve.fetch; its message string is the only signal. Narrow suppressions with boundary reasons, per the typed-errors skill. * test(e2e): quarantine the seat-limit scenario on the emulate 0.9.0 Autumn gap emulate 0.9.0's Autumn customer balances omit the expanded feature object autumn-js asserts, so useCustomer crashes the org page into the error boundary. Fixed upstream in UsefulSoftwareCo/emulate#8 (0.9.1); unskip once the publish lands and the e2e dependency is bumped. * ci: retrigger * ci: shard the cloud e2e job so each shard gets a fresh dev stack A full-suite run against one long-lived cloud dev server degrades partway through: sign-in starts refusing connections and everything after fails with fetch errors (the same SSE/OTel memory growth being instrumented on main). Four shards, each booting its own stack, stay under the threshold. Re-merge into one job once the leak is fixed. * ci: split the cloud e2e job into eight shards Four shards still hit the dev-server degradation a few minutes in on 2-core runners; eight keeps each stack's lifetime under the threshold. * ci: retry flaky browser scenarios twice on the same stack The remaining shard failures are scattered single-test Playwright waitFor timeouts on 2-core runners, not systemic stack death; vitest --retry clears them without hiding real regressions (a consistent failure still fails after 3 attempts). * test(e2e): quarantine the Graph default-add scenario on CI runners Compiling the Graph spec inside dev workerd 500s on 2-core GitHub runners and takes the dev stack down for every scenario after it in the shard (the auth-hint/org-slug/docs-link failures in the same shard were all downstream of this). Local runs are unaffected; skip only under CI. * selfhost: read the local-network posture from env in the plugins seam plugins() runs per request; loadConfig() does filesystem work (data dir, secret key resolution) that should not ride the request path. The env read is the same computation loadConfig makes for the flag. * e2e: bump @executor-js/emulate to 0.10.0, unskip the seat-limit scenario 0.10.0 ships the Autumn balances.feature expansion autumn-js asserts (UsefulSoftwareCo/emulate#8), so the org page renders again and the scenario passes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
After the SSR auth gate landed (#975), the full-page
ShellSkeletonwas still what every signed-in load painted first. That was backwards: the gate has already unsealed and verified the session on the very request it's serving, so the server knows who the user is — and then rendered a silhouette that says "we don't know who you are." A fresh browser (no hint cookie yet) sat on that skeleton for the whole/account/meround trip.Change
The gate's verified identity now drives the SSR render, and the full-page skeleton is deleted.
Gate resolves the auth hint server-side (
apps/cloud/src/auth/ssr-gate.ts): the browser's hint cookie is used when its user/org ids match the verified session; otherwise a hint is minted from the session (org name from the local mirror, over per-request DB layers). The hint is threaded to the SSR render via request-middleware context, and minted as aSet-Cookieon the response when the browser didn't hold a current one. The gate only re-mints on id mismatch — display-field freshness (renames) stays the client's job via/account/me, so the two writers can't ping-pong.SSR paints the authenticated shell (
packages/react/src/multiplayer/auth-context.tsx,apps/cloud/src/routes/__root.tsx): the root route's loader picks the hint out ofserverContextand dehydrates it, andAuthProvidertakes it asinitialHint— the server render and the first client render derive from the same value, so they agree by construction. Hosts without the seam (self-host) keep the post-mount cookie read unchanged.Org-less sessions redirect at the edge: a verified session with no organization is 302'd to
/create-orgbefore the document exists (route sets shared between gate and client viaauth/route-paths.ts). The client AuthGate's org branch now only covers mid-session transitions.ShellSkeletonis deleted (~60 lines): every remaining fallback is a neutral blank screen for moments between redirects. Per-section skeletons (e.g. the sidebar's integration list while it fetches) are untouched — those represent data that genuinely is loading.The first-ever load on a fresh browser
No hint cookie anywhere,
/api/account/meheld open for 2 s — the real shell with the user's identity paints immediately, and the response minted the hint for next time:Test coverage
e2e/cloud/auth-hint.test.ts— rewritten headline scenario: fresh browser, probe held open → real shell with this identity's org in the footer before/account/meresolves, hint minted on the document response; logout still clears hint + session.e2e/cloud/session-gate.test.ts— new scenario: org-less session →302 /create-orgat the edge;/create-orgitself serves without a redirect loop.e2e/cloud/unauthenticated-skeleton.test.ts— 404 assertion updated: shell-framed 404 with the real nav (the old zero-skeleton count now trips on honest per-section skeletons).All 14 auth-related e2e scenarios green; format, lint, typecheck, and unit tests pass. The two
connect-handoffe2e failures in the full cloud run fail identically on clean main (emulator payload cap, unrelated).Follow-up: painting the real shell means the connect card's
npx add-mcp <url>is now in the first SSR'd HTML — and its MCP URL flashed127.0.0.1:4000before hydration. Fixed separately in #991.