You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
console splash waits on six serial request waves: the organisation chain (list → get-full-organization → get-active-member) and AppContent's object/dashboard/report/page metadata preload run before the first paint #12809
Filing gate: (b) a named producer is blocked by it. objectstack-ai/cloud#2914 (the control plane's console open: 13 s to the last API response on staging, 30 calls with duplicates) cannot meet its acceptance (a splash under 4 s, a call budget) from cloud. The os-dev on that card measured one full console open on a local control plane at framework f7e836da1ed3 / objectui ef71a09f9e5f, with Chromium and CDP initiator stacks mapped through source maps, on SQLite and on Postgres behind a 50 ms round trip (report 6225536289 on cloud#2914; every call's table is in the Attribution section of PR objectstack-ai/cloud#2941). Filed by the repo:cloud#1 seat (seat post objectstack-ai/objectstack#6026, session session_01WVbr5J6u8BHh8EyFtcWciH). ⛔ Not a claim.
Dedupe: the 74 most recent open objectui issues carry no card on the splash critical path, scopeMetaReady or the AuthProvider organisation waterfall.
Measured (objectui ef71a09f9e5f, Postgres at 50 ms RTT)
The splash (data-boot-splash) is on screen from about 1.1 s to 4.0–4.2 s. Each of its six serial waves is issued by objectui and answered by the framework; no cloud handler is on the path.
The organisation chain.packages/auth/src/AuthProvider.tsx:639, then :646 (getActiveOrganization = createAuthClient.ts:854getFullOrganization), then :718, run one after another: organization/list → get-full-organization → get-active-member. get-full-organization is 9 serial queries, 491 ms at 50 ms RTT (the card read 4.9 s on staging).
The metadata preload gates the splash.packages/app-shell/src/console/AppContent.tsx:223-238 (scopeMetaReady, gate at :798) preloads object, dashboard, report and page metadata before it leaves the splash. That preload starts only after me/permissions (wave 6, 3.28–3.89 s of a 4.0–4.2 s splash).
Done when
The critical path is session + me + the current app's metadata. Everything else loads after the first paint and shows skeletons where it lands: entitlements, preferences, other apps' metadata, and translations beyond the current locale. This is the card's Target 2.
The active organisation comes from session.activeOrganizationId and the list answer, and the member row is fetched in parallel.
A test pins which requests gate the splash.
AppContent.tsx (+116/−83) and AuthProvider.tsx (+13/−1) changed on objectui maina8add5c711 since the pin. Re-measure there first.
Why p1 (the filer's): six serial request waves stand before the first paint. It is the critical-path half of cloud#2914 (p1).
For the claimant: Parallelize what does not depend on the organisation chain, and move the metadata preload off the splash path. Serial with objectui#12808 (same open path); the same seat can take both.
Claim: PM loop round 3 (priority:p1; filed by the repo:cloud#1 seat as the critical-path half of objectstack-ai/cloud#2914, the 13 s console open, and graded by the triage seat, 6226217769; serial after objectui#12808, which landed as 5d69b0e)
Session: session_01SJn1GEkrbbBQkhGC26wEDw
Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-12809-splash-critical-path
Worktree: objectui-issue-12809 (in the cloud session's own container)
Domain: domain:ui
Seat: domain:ui#4
File surface: packages/auth/src/AuthProvider.tsx and packages/auth/src/createAuthClient.ts (the organisation chain: the active organisation from session.activeOrganizationId and the list answer, the member row in parallel), packages/auth/src/useWorkspaceAdminStatus.ts (only if the splash gate's me/permissions wave moves), packages/app-shell/src/console/AppContent.tsx (the splash gate and its metadata preload), the route components under packages/app-shell/src/console/ that read a metadata bucket at render and so must wait on their own type (each named in the report), tests beside each, one .changeset/12809-*.md per released package touched. ⛔ packages/components/src/ui/** stays out of bounds. Stop on breach; explain in the report.
Container & model: L, mode:cloud, model: opus (cloud on the maintainer's stance 「可以的话,ui 后续开发尽量用云 session,并且要求截图给我看」; lane scope on 「你也可以接ui车道的卡」; batch 3 on 「当前任务处理完,后续并发降到3」, this session's chat, 2026-10-16)
Clause-②: yes
Clause-② reason: @object-ui/auth exports useAuth's context and useWorkspaceAdminStatus; when the active organisation and the member row resolve may change what those values read during boot. «拿不准 ⇒ 按 yes». The seat's TIER_DEFAULT review comes before ready.
Responsibility: n/a — measured serial request waves on every console open
Thread-read: 6226217769
Serial constraints cleared: objectui#12808 (the same boot path) landed as 5d69b0e. No open objectui PR touches AuthProvider.tsx, createAuthClient.ts or AppContent.tsx (read at dispatch). The server-side query count of get-full-organization (9 serial queries) is a framework card and not this one.
{
"issue": 12809,
"status": "done",
"branch": "claude/issue-12809-splash-critical-path",
"pr": 12855,
"session": "session_019B3t48EDiMzo9xVuXhracK — the Claude-Session trailer on both commits",
"premise_still_valid": true,
"summary": "Shipped on 82150d0 (2 commits on 5d69b0e). AuthProvider now names the active organisation from session.activeOrganizationId plus the organization/list answer. It asks for the member row (get-active-member) at the same time as the list. get-full-organization is gone from boot: every first-paint reader of activeOrganization reads only id, name or slug, and the list row carries all three (measured live). AppContent's four-bucket preload no longer holds the splash. A declared per-route reads table (appRouteMetadataReads.ts) makes the splash wait only for the buckets the current route reads. After the first paint, a route whose buckets have not landed shows a skeleton inside the console chrome. A test reads the route table out of AppContent.tsx and fails on any route without a declared row. Live, production build, +50 ms per request, 3 opens per side: the splash was on screen 756–904 ms on main and 628–796 ms on the branch. The waves after get-session went from list, then get-full-organization, then member and me/permissions, then the preload, to list and member together, then me/permissions. PM assumptions 1–3 held. Assumption 4 is falsified: useWorkspaceAdminStatus issues no request. me/permissions comes from apps/console's ConsolePermissionsScope (fail-closed LoadingScreen). That scope mounts below RequireOrganization, which waits on isOrganizationsLoading, so it used to wait for get-full-organization. me/permissions stays on the critical path as the ruled 'me'. The card's assignee was not written. The shared checkout /home/user/objectui is left detached at 5d69b0e: I detached it to delete the empty local branch of this name before cutting the worktree.",
"tests": "All runs on 82150d0, from the repo root.\n(1) New pins:\n- packages/auth/src/__tests__/orgChainParallel-12809.test.tsx: 6/6 passed. The member row is asked while organization/list is still pending; getActiveOrganization is never called; the active org is the list row named by the session id. CONTROLs: StrictMode replay asks once; a switch re-reads the member row for the new org; a session without the key takes the old path; a stale id names no org and clears the stored id (objectui#12698); one membership is repaired once (ADR-0081).\n- packages/app-shell/src/console/__tests__/AppContent.splashCriticalPath-12809.test.tsx: 3/3 passed. Real AuthProvider, real useWorkspaceAdminStatus and AppContent. The splash holds through the list and member answers and leaves once the route's object bucket settles. get-full-organization (never answers) is not called; ensureType is asked only for object; dashboard, report and page stay untouched. CONTROLs: a later report route shows route-metadata-skeleton inside the chrome, then the report; a refetching bucket keeps the route mounted; an idle bucket waits again.\n- packages/app-shell/src/console/__tests__/appRouteMetadataReads-12809.test.ts: 18/18 passed. A TypeScript-AST read of every Route path in AppContent.tsx must equal the table (both directions, positive control included), plus URL-to-row matching with host routes ranked.\n(2) Ablation, one per pin, via objectstack scripts/ablation-replace.mjs. Each anchor hit 1 to 0, each pin went red, each restore had blob equal to HEAD and git diff HEAD empty:\n- the early member ask disabled: 1 failed / 5 passed ('asks for the member row while organization/list is still in flight');\n- the route-reads term removed from the splash gate: 1 failed / 2 passed (expected false to be true: the splash left before the object bucket);\n- the 'search' row deleted: 1 failed / 17 passed (Received 'search').\n(3) Suites:\n- pnpm --filter @object-ui/auth test: exit 0, 34 files, 317 tests.\n- app-shell full suite as pnpm exec vitest run packages/app-shell/ --shard=K/8 for K=1..8, each exit 0. The suite exceeds the 10-minute foreground cap, and the shard 1–3 loop was moved to the background by the harness and collected in the turn. Totals: 1527 files (1526 passed, 1 skipped); 14395 tests passed, 9 skipped.\n- pnpm exec vitest run apps/console/: exit 0, 222 files, 2300 tests.\n- The 46 existing test files that reference AppContent: 46/46, 377 tests, before the pins were added.\n(4) Live: hotcrm objectstack dev --fresh on :4321 behind a +50 ms-per-request proxy. Console production builds (vite build, VITE_BASE_PATH=/) of main 5d69b0e and of the branch, served by vite preview. Chromium via Playwright: sign in, one warm open, then the measured open of /apps/crm_enterprise/crm_account. Splash on screen (data-boot-splash shown to hidden), ms: main 598–1498, 571–1327, 650–1554; branch 615–1243, 671–1467, 670–1344. A dev-server run first gave 1744 vs 1726 ms; that interval is dominated by Vite module loading (a Suspense boundary keeps the splash up), so the production builds are the reading. Isolation on the branch, +600 ms on one request: sys_user_preference splash 549 ms, me/localization 646, meta/page 632, so none of them holds the splash; me/permissions 1021, so it does.",
"gates": "On 82150d0, exit codes captured before any pipe:\n- pnpm --filter @object-ui/auth type-check: 0. pnpm --filter @object-ui/app-shell type-check: 0 (after building the app-shell dependency closure).\n- eslint on the 7 touched files: 0, with 0 errors and 43 warnings; main has 38 on the same files. The difference is +7 react-hooks/refs (render-time ref reads in AppContent's first-paint latch and the settled-type set) and −1 set-state-in-effect. lint.yml sets no --max-warnings.\n- Builds: pnpm --filter @object-ui/auth --filter @object-ui/app-shell build: 0. pnpm --filter '@object-ui/console^...' build: 0. pnpm --filter @object-ui/console build: 0.\n- pnpm check:eager-closure: 0 (aggregate 3135.6 of 3744.1 KB).\n- pnpm check:i18n-keys 0, check:i18n-drift 0, check:spec-symbols 0, check:new-line-citations 0 (0 new), check:test-path-roots 0.\n- node scripts/check-changeset-presence.mjs: 0 (5 source files of 2 released packages, 1 changeset).\n- The heavy runs went through objectstack's os-verify-lock.sh (slot issue-12809, every VERDICT command-exit 0).\n- No PR exists, so there are no check-runs to cite: CI is NOT MEASURED until the PM opens the PR. objectui has no dispatch-gates.mjs, so the gate list was derived by hand from the dispatch.",
"mcp_calls": "3 GitHub MCP calls, all reads: issue_read get, issue_read get_comments (#12809) and pull_request_read get (#12840). Also add_repo x2 (claude-code-remote, read; both answered read_available and attached nothing) and ToolSearch. No MCP write tool was used.",
"api_writes": "0 REST writes. git push only: 3 pushes of claude/issue-12809-splash-critical-path (the empty branch first, then the 2 commits) and 1 push of the evidence branch claude/issue-12809-evidence at 42c88701243d586710a3a87f53df603c2570ba07. No PR, comment, label or assignee was written.",
"open_questions": [
{
"question": "The os-dev contract tells the dev to open the draft PR and post the report as a card comment; the dispatch's degraded channel tells the dev to do neither. Which one held?",
"options": [
"A: follow the dispatch: no PR and no comment from this container; the PM opens the PR from pr_body_draft and posts this report",
"B: try the relay anyway"
],
"recommendation": "A was followed. The dispatch says the fleet-write relay is refused here (403), so the file's write steps cannot run. I did not probe the relay. The conflict is recorded here rather than silently resolved."
},
{
"question": "Where should each route's wait live: in every reader, or in one place?",
"options": [
"A: one gate in AppContent, keyed on a declared per-route reads table, with an enumeration pin that fails when a route has no row",
"B: an ensureType wait inside each reader"
],
"recommendation": "A, decided and shipped. With B the edits land in views/, layout/, plugin-designer and apps/console (ObjectView, ObjectDataPage, RecordFormPage, RecordDetailView, ReportView, DashboardDesignPage, CreateAppPage/EditAppPage, SearchResultsPage, AuditLogPage, FlowRunsPage), all outside the claimed surface, and a missed reader fails silently. A puts the contract in one declared table that a test re-derives from the route source (axis 3: hard to get wrong), and adds no published surface. Host routes (extraRoutes) cannot be enumerated from here, so they wait for all four buckets, which is the old behaviour."
},
{
"question": "How should a session answer without activeOrganizationId be treated?",
"options": [
"A: the old getActiveOrganization path",
"B: treat the absence as 'no active org'"
],
"recommendation": "A, decided. A sign-in response and a server without the organization plugin both lack the key. Reading the absence as 'no org' would be a wrong answer. Mutation callers of refreshOrganizations also keep asking the server, because the session in hand may predate the mutation (for example an accepted invitation that moves the active org)."
}
],
"out_of_scope_findings": [
"class: none (observation, read-only) · reach: n/a · useTrackRouteAsRecent records a visit only if the objects list is in hand when the route effect runs; at boot it fires before the object bucket lands, so the landing route's first visit can go unrecorded. Pre-existing and unchanged by this PR; not reproduced live · carrier: none — noted, not filed",
"class: none (observation, dev only) · reach: n/a · under React StrictMode in dev, organization/list, meta/app, meta/object, meta/view and me/permissions are each requested twice on console open; a production build requests each once (measured) · carrier: none — noted, not filed",
"class: none (observation) · reach: n/a · MetadataProvider loads its eager types app then view one after another, and meta/view was in flight on every open measured; whether view holds the splash was NOT MEASURED. MetadataProvider.tsx is outside the claimed surface · carrier: none — noted, not filed"
],
"deviations": [
"packages/auth/src/types.ts is outside the claimed surface. It gains one optional member, AuthClientSession.activeOrganizationId (string | null), which is the type of useAuth().session. The ruling allows the fewest optional members for useAuth's context; this is that one member.",
"New file packages/app-shell/src/console/appRouteMetadataReads.ts holds the per-route reads table, collectRoutePaths and metadataReadsForAppRoute, beside AppContent. It is not exported from the package entry. No route component under console/ (or elsewhere) was edited: the waiting is centralised in AppContent, as in open question 2.",
"useWorkspaceAdminStatus.ts is unchanged: the me/permissions wave does not move (it is the ruled 'me'). Its docblock still describes the old chain, listOrganizations then getActiveOrganization; that prose is now stale and was left as it is.",
"When existing values resolve (published face): on a session that carries the key, useAuth().activeOrganization lands right after organization/list instead of after get-full-organization. isOrganizationsLoading goes false sooner. activeMember and isMembershipResolved land sooner, because the member read runs beside the list. The activeOrganization object is now the list row, whose type and fields (id, name, slug, logo, createdAt) match what consumers read; the full-organization answer also carried members and invitations, which nothing reads. The internal refreshOrganizations callback gained an optional second parameter; the context type stays () => Promise.",
"Evidence was measured on production builds via vite preview, not on the Vite dev server that objectui#12840 used, because the dev-mode splash is dominated by module loading. Latency is +50 ms per request at a local proxy, applied on both sides.",
"The app-shell shard 1–3 loop and the apps/console run exceeded the 10-minute foreground cap and were moved to the background by the harness. Both were awaited and read inside the turn."
],
"files_changed": [
"packages/auth/src/AuthProvider.tsx",
"packages/auth/src/types.ts",
"packages/auth/src/__tests__/orgChainParallel-12809.test.tsx",
"packages/app-shell/src/console/AppContent.tsx",
"packages/app-shell/src/console/appRouteMetadataReads.ts",
"packages/app-shell/src/console/__tests__/AppContent.splashCriticalPath-12809.test.tsx",
"packages/app-shell/src/console/__tests__/appRouteMetadataReads-12809.test.ts",
".changeset/12809-splash-critical-path.md"
],
"screenshots": [
"https://github.com/objectstack-ai/objectui/blob/42c88701243d586710a3a87f53df603c2570ba07/docs/design/findings/12809/console-home-after-12809.png?raw=true",
"https://github.com/objectstack-ai/objectui/blob/42c88701243d586710a3a87f53df603c2570ba07/docs/design/findings/12809/app-route-skeleton-12809.png?raw=true",
"https://github.com/objectstack-ai/objectui/blob/42c88701243d586710a3a87f53df603c2570ba07/docs/design/findings/12809/app-route-after-skeleton-12809.png?raw=true"
],
"line_budget": "n/a",
"pr_title_draft": "fix(auth,app-shell): take the organization chain and the metadata preload off the console splash's critical path (objectui#12809)",
"pr_body_draft": "(elided here: it is the body of objectstack-ai/objectui#12855, the dev's draft verbatim)"
}
Relayed by seat domain:ui#4 (session_01SJn1GEkrbbBQkhGC26wEDw) from the cloud session's final report (degraded channel: the session pushed and returned pr: null). The seat opened #12855 from claude/issue-12809-splash-critical-path at 82150d0 and set pr above. The three open questions (the contract conflict, one gate in AppContent against a wait in each reader, a session answer without activeOrganizationId) are ruled in the ACCEPT on the PR, and the seat's TIER_DEFAULT contract review follows it.
present: @object-ui/auth and @object-ui/app-shell minor
—
a model name in the merge commit's message
0
—
PNG files in the merge's diff
0
the screenshots stay on claude/issue-12809-evidence (42c8870)
State change: the card closed as completed through Fixes #12809, and pm:dispatched is removed in this act. The cloud session session_019B3t48EDiMzo9xVuXhracK is archived.
Records of this card:
the report relayed: 6229666401;
the ACCEPT: 6229670863;
the TIER_DEFAULT contract review: 6229693622 (PASS);
the landing note: 6229718715.
What the splash still waits for after this PR: the session, the member row, me/permissions and the current route's metadata. The next lever is MetadataProvider's serial app → view load (an observation, not measured as gating). The whole-registry meta/object read is #12811, dispatched next on this boot path.
Activity
objectstack-fleet commentedon Oct 16, 2026
Triage: first grade,
domain:ui·area:devpath·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-16T22:54ZTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015). ⛔ Not a claim, ⛔ not a dispatch.
objectstack-fleet commentedon Oct 17, 2026
Claim: PM loop round 3 (
priority:p1; filed by therepo:cloud#1seat as the critical-path half of objectstack-ai/cloud#2914, the 13 s console open, and graded by the triage seat,6226217769; serial after objectui#12808, which landed as5d69b0e)Session:
session_01SJn1GEkrbbBQkhGC26wEDwAccount:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12809-splash-critical-pathWorktree:
objectui-issue-12809(in the cloud session's own container)Domain:
domain:uiSeat:
domain:ui#4File surface:
packages/auth/src/AuthProvider.tsxandpackages/auth/src/createAuthClient.ts(the organisation chain: the active organisation fromsession.activeOrganizationIdand the list answer, the member row in parallel),packages/auth/src/useWorkspaceAdminStatus.ts(only if the splash gate'sme/permissionswave moves),packages/app-shell/src/console/AppContent.tsx(the splash gate and its metadata preload), the route components underpackages/app-shell/src/console/that read a metadata bucket at render and so must wait on their own type (each named in the report), tests beside each, one.changeset/12809-*.mdper released package touched. ⛔packages/components/src/ui/**stays out of bounds. Stop on breach; explain in the report.Container & model:
L,mode:cloud,model: opus(cloud on the maintainer's stance 「可以的话,ui 后续开发尽量用云 session,并且要求截图给我看」; lane scope on 「你也可以接ui车道的卡」; batch 3 on 「当前任务处理完,后续并发降到3」, this session's chat, 2026-10-16)Clause-②: yes
Clause-② reason:
@object-ui/authexportsuseAuth's context anduseWorkspaceAdminStatus; when the active organisation and the member row resolve may change what those values read during boot. «拿不准 ⇒ 按 yes». The seat'sTIER_DEFAULTreview comes before ready.Responsibility: n/a — measured serial request waves on every console open
Thread-read: 6226217769
Serial constraints cleared: objectui#12808 (the same boot path) landed as
5d69b0e. No open objectui PR touchesAuthProvider.tsx,createAuthClient.tsorAppContent.tsx(read at dispatch). The server-side query count ofget-full-organization(9 serial queries) is a framework card and not this one.objectstack-fleet commentedon Oct 17, 2026
os-dev-report
{ "issue": 12809, "status": "done", "branch": "claude/issue-12809-splash-critical-path", "pr": 12855, "session": "session_019B3t48EDiMzo9xVuXhracK — the Claude-Session trailer on both commits", "premise_still_valid": true, "summary": "Shipped on 82150d0 (2 commits on 5d69b0e). AuthProvider now names the active organisation from session.activeOrganizationId plus the organization/list answer. It asks for the member row (get-active-member) at the same time as the list. get-full-organization is gone from boot: every first-paint reader of activeOrganization reads only id, name or slug, and the list row carries all three (measured live). AppContent's four-bucket preload no longer holds the splash. A declared per-route reads table (appRouteMetadataReads.ts) makes the splash wait only for the buckets the current route reads. After the first paint, a route whose buckets have not landed shows a skeleton inside the console chrome. A test reads the route table out of AppContent.tsx and fails on any route without a declared row. Live, production build, +50 ms per request, 3 opens per side: the splash was on screen 756–904 ms on main and 628–796 ms on the branch. The waves after get-session went from list, then get-full-organization, then member and me/permissions, then the preload, to list and member together, then me/permissions. PM assumptions 1–3 held. Assumption 4 is falsified: useWorkspaceAdminStatus issues no request. me/permissions comes from apps/console's ConsolePermissionsScope (fail-closed LoadingScreen). That scope mounts below RequireOrganization, which waits on isOrganizationsLoading, so it used to wait for get-full-organization. me/permissions stays on the critical path as the ruled 'me'. The card's assignee was not written. The shared checkout /home/user/objectui is left detached at 5d69b0e: I detached it to delete the empty local branch of this name before cutting the worktree.", "tests": "All runs on 82150d0, from the repo root.\n(1) New pins:\n- packages/auth/src/__tests__/orgChainParallel-12809.test.tsx: 6/6 passed. The member row is asked while organization/list is still pending; getActiveOrganization is never called; the active org is the list row named by the session id. CONTROLs: StrictMode replay asks once; a switch re-reads the member row for the new org; a session without the key takes the old path; a stale id names no org and clears the stored id (objectui#12698); one membership is repaired once (ADR-0081).\n- packages/app-shell/src/console/__tests__/AppContent.splashCriticalPath-12809.test.tsx: 3/3 passed. Real AuthProvider, real useWorkspaceAdminStatus and AppContent. The splash holds through the list and member answers and leaves once the route's object bucket settles. get-full-organization (never answers) is not called; ensureType is asked only for object; dashboard, report and page stay untouched. CONTROLs: a later report route shows route-metadata-skeleton inside the chrome, then the report; a refetching bucket keeps the route mounted; an idle bucket waits again.\n- packages/app-shell/src/console/__tests__/appRouteMetadataReads-12809.test.ts: 18/18 passed. A TypeScript-AST read of every Route path in AppContent.tsx must equal the table (both directions, positive control included), plus URL-to-row matching with host routes ranked.\n(2) Ablation, one per pin, via objectstack scripts/ablation-replace.mjs. Each anchor hit 1 to 0, each pin went red, each restore had blob equal to HEAD and git diff HEAD empty:\n- the early member ask disabled: 1 failed / 5 passed ('asks for the member row while organization/list is still in flight');\n- the route-reads term removed from the splash gate: 1 failed / 2 passed (expected false to be true: the splash left before the object bucket);\n- the 'search' row deleted: 1 failed / 17 passed (Received 'search').\n(3) Suites:\n- pnpm --filter @object-ui/auth test: exit 0, 34 files, 317 tests.\n- app-shell full suite as pnpm exec vitest run packages/app-shell/ --shard=K/8 for K=1..8, each exit 0. The suite exceeds the 10-minute foreground cap, and the shard 1–3 loop was moved to the background by the harness and collected in the turn. Totals: 1527 files (1526 passed, 1 skipped); 14395 tests passed, 9 skipped.\n- pnpm exec vitest run apps/console/: exit 0, 222 files, 2300 tests.\n- The 46 existing test files that reference AppContent: 46/46, 377 tests, before the pins were added.\n(4) Live: hotcrm objectstack dev --fresh on :4321 behind a +50 ms-per-request proxy. Console production builds (vite build, VITE_BASE_PATH=/) of main 5d69b0e and of the branch, served by vite preview. Chromium via Playwright: sign in, one warm open, then the measured open of /apps/crm_enterprise/crm_account. Splash on screen (data-boot-splash shown to hidden), ms: main 598–1498, 571–1327, 650–1554; branch 615–1243, 671–1467, 670–1344. A dev-server run first gave 1744 vs 1726 ms; that interval is dominated by Vite module loading (a Suspense boundary keeps the splash up), so the production builds are the reading. Isolation on the branch, +600 ms on one request: sys_user_preference splash 549 ms, me/localization 646, meta/page 632, so none of them holds the splash; me/permissions 1021, so it does.", "gates": "On 82150d0, exit codes captured before any pipe:\n- pnpm --filter @object-ui/auth type-check: 0. pnpm --filter @object-ui/app-shell type-check: 0 (after building the app-shell dependency closure).\n- eslint on the 7 touched files: 0, with 0 errors and 43 warnings; main has 38 on the same files. The difference is +7 react-hooks/refs (render-time ref reads in AppContent's first-paint latch and the settled-type set) and −1 set-state-in-effect. lint.yml sets no --max-warnings.\n- Builds: pnpm --filter @object-ui/auth --filter @object-ui/app-shell build: 0. pnpm --filter '@object-ui/console^...' build: 0. pnpm --filter @object-ui/console build: 0.\n- pnpm check:eager-closure: 0 (aggregate 3135.6 of 3744.1 KB).\n- pnpm check:i18n-keys 0, check:i18n-drift 0, check:spec-symbols 0, check:new-line-citations 0 (0 new), check:test-path-roots 0.\n- node scripts/check-changeset-presence.mjs: 0 (5 source files of 2 released packages, 1 changeset).\n- The heavy runs went through objectstack's os-verify-lock.sh (slot issue-12809, every VERDICT command-exit 0).\n- No PR exists, so there are no check-runs to cite: CI is NOT MEASURED until the PM opens the PR. objectui has no dispatch-gates.mjs, so the gate list was derived by hand from the dispatch.", "mcp_calls": "3 GitHub MCP calls, all reads: issue_read get, issue_read get_comments (#12809) and pull_request_read get (#12840). Also add_repo x2 (claude-code-remote, read; both answered read_available and attached nothing) and ToolSearch. No MCP write tool was used.", "api_writes": "0 REST writes. git push only: 3 pushes of claude/issue-12809-splash-critical-path (the empty branch first, then the 2 commits) and 1 push of the evidence branch claude/issue-12809-evidence at 42c88701243d586710a3a87f53df603c2570ba07. No PR, comment, label or assignee was written.", "open_questions": [ { "question": "The os-dev contract tells the dev to open the draft PR and post the report as a card comment; the dispatch's degraded channel tells the dev to do neither. Which one held?", "options": [ "A: follow the dispatch: no PR and no comment from this container; the PM opens the PR from pr_body_draft and posts this report", "B: try the relay anyway" ], "recommendation": "A was followed. The dispatch says the fleet-write relay is refused here (403), so the file's write steps cannot run. I did not probe the relay. The conflict is recorded here rather than silently resolved." }, { "question": "Where should each route's wait live: in every reader, or in one place?", "options": [ "A: one gate in AppContent, keyed on a declared per-route reads table, with an enumeration pin that fails when a route has no row", "B: an ensureType wait inside each reader" ], "recommendation": "A, decided and shipped. With B the edits land in views/, layout/, plugin-designer and apps/console (ObjectView, ObjectDataPage, RecordFormPage, RecordDetailView, ReportView, DashboardDesignPage, CreateAppPage/EditAppPage, SearchResultsPage, AuditLogPage, FlowRunsPage), all outside the claimed surface, and a missed reader fails silently. A puts the contract in one declared table that a test re-derives from the route source (axis 3: hard to get wrong), and adds no published surface. Host routes (extraRoutes) cannot be enumerated from here, so they wait for all four buckets, which is the old behaviour." }, { "question": "How should a session answer without activeOrganizationId be treated?", "options": [ "A: the old getActiveOrganization path", "B: treat the absence as 'no active org'" ], "recommendation": "A, decided. A sign-in response and a server without the organization plugin both lack the key. Reading the absence as 'no org' would be a wrong answer. Mutation callers of refreshOrganizations also keep asking the server, because the session in hand may predate the mutation (for example an accepted invitation that moves the active org)." } ], "out_of_scope_findings": [ "class: none (observation, read-only) · reach: n/a · useTrackRouteAsRecent records a visit only if the objects list is in hand when the route effect runs; at boot it fires before the object bucket lands, so the landing route's first visit can go unrecorded. Pre-existing and unchanged by this PR; not reproduced live · carrier: none — noted, not filed", "class: none (observation, dev only) · reach: n/a · under React StrictMode in dev, organization/list, meta/app, meta/object, meta/view and me/permissions are each requested twice on console open; a production build requests each once (measured) · carrier: none — noted, not filed", "class: none (observation) · reach: n/a · MetadataProvider loads its eager types app then view one after another, and meta/view was in flight on every open measured; whether view holds the splash was NOT MEASURED. MetadataProvider.tsx is outside the claimed surface · carrier: none — noted, not filed" ], "deviations": [ "packages/auth/src/types.ts is outside the claimed surface. It gains one optional member, AuthClientSession.activeOrganizationId (string | null), which is the type of useAuth().session. The ruling allows the fewest optional members for useAuth's context; this is that one member.", "New file packages/app-shell/src/console/appRouteMetadataReads.ts holds the per-route reads table, collectRoutePaths and metadataReadsForAppRoute, beside AppContent. It is not exported from the package entry. No route component under console/ (or elsewhere) was edited: the waiting is centralised in AppContent, as in open question 2.", "useWorkspaceAdminStatus.ts is unchanged: the me/permissions wave does not move (it is the ruled 'me'). Its docblock still describes the old chain, listOrganizations then getActiveOrganization; that prose is now stale and was left as it is.", "When existing values resolve (published face): on a session that carries the key, useAuth().activeOrganization lands right after organization/list instead of after get-full-organization. isOrganizationsLoading goes false sooner. activeMember and isMembershipResolved land sooner, because the member read runs beside the list. The activeOrganization object is now the list row, whose type and fields (id, name, slug, logo, createdAt) match what consumers read; the full-organization answer also carried members and invitations, which nothing reads. The internal refreshOrganizations callback gained an optional second parameter; the context type stays () => Promise.", "Evidence was measured on production builds via vite preview, not on the Vite dev server that objectui#12840 used, because the dev-mode splash is dominated by module loading. Latency is +50 ms per request at a local proxy, applied on both sides.", "The app-shell shard 1–3 loop and the apps/console run exceeded the 10-minute foreground cap and were moved to the background by the harness. Both were awaited and read inside the turn." ], "files_changed": [ "packages/auth/src/AuthProvider.tsx", "packages/auth/src/types.ts", "packages/auth/src/__tests__/orgChainParallel-12809.test.tsx", "packages/app-shell/src/console/AppContent.tsx", "packages/app-shell/src/console/appRouteMetadataReads.ts", "packages/app-shell/src/console/__tests__/AppContent.splashCriticalPath-12809.test.tsx", "packages/app-shell/src/console/__tests__/appRouteMetadataReads-12809.test.ts", ".changeset/12809-splash-critical-path.md" ], "screenshots": [ "https://github.com/objectstack-ai/objectui/blob/42c88701243d586710a3a87f53df603c2570ba07/docs/design/findings/12809/console-home-after-12809.png?raw=true", "https://github.com/objectstack-ai/objectui/blob/42c88701243d586710a3a87f53df603c2570ba07/docs/design/findings/12809/app-route-skeleton-12809.png?raw=true", "https://github.com/objectstack-ai/objectui/blob/42c88701243d586710a3a87f53df603c2570ba07/docs/design/findings/12809/app-route-after-skeleton-12809.png?raw=true" ], "line_budget": "n/a", "pr_title_draft": "fix(auth,app-shell): take the organization chain and the metadata preload off the console splash's critical path (objectui#12809)", "pr_body_draft": "(elided here: it is the body of objectstack-ai/objectui#12855, the dev's draft verbatim)" }Relayed by seat
domain:ui#4(session_01SJn1GEkrbbBQkhGC26wEDw) from the cloud session's final report (degraded channel: the session pushed and returnedpr: null). The seat opened #12855 fromclaude/issue-12809-splash-critical-pathat82150d0and setprabove. The three open questions (the contract conflict, one gate inAppContentagainst a wait in each reader, a session answer withoutactiveOrganizationId) are ruled in the ACCEPT on the PR, and the seat'sTIER_DEFAULTcontract review follows it.Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Landed · PR #12855 merged as
84b8ffaonmain· read 2026-10-17T04:50Zorigin/main84b8ffaAuthClientSession.activeOrganizationIdinpackages/auth/src/types.tsstring | null(:141)6229693622namedbootMemberRefinpackages/auth/src/AuthProvider.tsxrefreshOrganizationstakes the optionalsessionActiveOrgId(:681)AuthContext.tsstill typesrefreshOrganizationswith no argumentscopeMetaReadyinpackages/app-shell/src/console/AppContent.tsxAppContent.tsxuseLazyMetadataSettled(:1578),route-metadata-skeleton(:1609)APP_ROUTE_METADATA_READSinpackages/app-shell/src/console/appRouteMetadataReads.ts*-12809test filesorgChainParallel(auth),AppContent.splashCriticalPath,appRouteMetadataReads(app-shell).changeset/12809-splash-critical-path.md@object-ui/authand@object-ui/app-shellminorclaude/issue-12809-evidence(42c8870)State change: the card closed as completed through
Fixes #12809, andpm:dispatchedis removed in this act. The cloud sessionsession_019B3t48EDiMzo9xVuXhracKis archived.Records of this card:
6229666401;6229670863;TIER_DEFAULTcontract review:6229693622(PASS);6229718715.What the splash still waits for after this PR: the session, the member row,
me/permissionsand the current route's metadata. The next lever isMetadataProvider's serialapp→viewload (an observation, not measured as gating). The whole-registrymeta/objectread is #12811, dispatched next on this boot path.Generated by Claude Code