Repository navigation
fix(app-shell): the app landing waits on its nav entries' pending API reads - #13105
Merged
objectstack-fleet[bot] merged 2 commits intoOct 20, 2026
Merged
Conversation
… reads A nav entry whose `dataSource` read is pending is hidden (objectui#13029), so `AppRootLanding` used to choose past it: an all-bound app flashed the no-access state, and an app whose first entry is bound landed on the next plain entry. The landing is now also asked over the trees as if every pending entry were shown once it answers. When the two answers disagree, the root holds on a skeleton; when the reads answer, it lands on the first entry the member is shown. The read state comes from `useNavMemberGates` (no new request). Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
objectstack-fleet
Bot
deleted the
claude/issue-13097-landing-waits-reads
branch
October 20, 2026 01:26
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.
Fixes #13097
Clause-②: no
What
AppRootLanding(packages/app-shell/src/console/AppContent.tsx) now waits on the API reads of the nav entries it would choose between. This is direction (A), as triage6279966884set it.A nav entry whose
dataSourceread is pending is hidden (objectui#13029). The landing used to choose from the trees with those entries already gone. Now it also asks the same two questions over the trees as they would be if every pending entry were shown once it answers. Those two questions are: which route does the landing pick, and does the app show this member nothing. If either answer differs, a pending read ahead of the first decided entry could still change the landing. The root then holds on the existingSkeletonGrid(test idapp-landing-pending): it does not navigate and does not showAppNoAccessEmptyState. When the reads answer, it lands on the first entry the member is shown (objectui#12426). A failed read hides its entry, so the landing moves on. An app with no bound entry gets the same answer twice and lands on its first render.There are no extra reads.
useNavMemberGatesnow also returnsreadEntry, the read state itsvisibleevaluator is already built on (NavMemberGatesWithReads).navEntriesShownOncePendingReadsShowreusesnavEntriesShownToMemberwith one change: a pending entry'svisibleis treated as not yet decided, so the entry is kept. Every other gate applies unchanged. Both live in the hook module. Nothing is added to the package entry, and theNavMemberGatesshape that other call sites build is unchanged.Measured on base (
d11174ad10), before the fixThese come from the new test file, run against the base sources through the real
AppContent, with a stubbedapiFetchheld pending:app-no-access-empty-stateat/apps/crm./apps/crm/crm_case(the plain entry). The failing assertion was "expected '/apps/crm/crm_case' to be '/apps/crm'".I took no browser reading because no backend was booted for this run. That gap is listed under Acceptance notes.
Pins (
AppContent.landingWaitsReads-13097.test.tsx)/apps/crm/crm_env.readyand shown, it lands on the bound entry. If the read fails (HTTP 500), it lands on the plain entry.MutationObserverwatches every node the document receives.Ablation
The hold condition was replaced by
falsethroughablation-replace.mjs(anchor 1 to 0, blob9667c66bf4a0to5c960f6b8053). Result: (a) and both (b) cases went red, and (c) and (d) stayed green (3 failed, 2 passed). Restore was proven by blob hash: after the restore the file equals the HEAD blob9667c66bf4a0, andgit diff HEADis empty.Gates (final head
0b5e5b4b15unless stated)pnpm --filter "@object-ui/app-shell^..." build(closure): exit 0.pnpm --filter @object-ui/app-shell type-check(tsc --noEmit && tsc -p tsconfig.test.json; the test config includessrc/**/*.test.tsx): exit 0 on0b5e5b4b15.0b5e5b4b15: the new file plus the 12426, 12079 and 13029 files. 4 files, 42 tests passed.49f45565fc: every test underpackages/**/__tests__andapps/**/__tests__that namesuseNavMemberGates,navEntriesShownToMemberorAppContent. 42 files (172 + 199 tests) passed. The only later commit is a type-only change (the area list is typed instead ofany).--no-inline-config;--format jsoncounted 3 files): 0 errors.AppContent.tsxhas 43 warnings against 44 on base, anduseNavMemberGates.tshas 2 against 2. The flat config enables no type-aware linting, so this diff cannot move the verdict on any file it does not touch.check:phantom-deps,check:self-import,check:unreferenced-sources,check:doc-example-readers,check:handler-key-reads,check:side-effects-array,check:element-data-source-declaration,check:spec-symbols,check:action-forward-parity,check:designer-field-key-parity,check:icon-record-names,check:i18n-keys,check:i18n-drift,check:i18n-designer-parity,check:control-bytes,check:new-line-citations,scripts/check-hand-rolled-comment-mask.mjs,scripts/check-changeset-fixed.mjsandscripts/check-changeset-presence.mjs.check-governed-queue-guard --teston the 4 paths: NOT GOVERNED.packages/app-shelldirectory (whole-package runs were not allowed by the dispatch lock rules) andpnpm lint.Acceptance notes
/me/permissionsis unanswered. Nothing in this PR adds a timeout.useAuthmock hands back one stable value.AppContentre-reads the auth config whenevergetAuthConfig's identity changes. A mock that returns a fresh function on every render (as the 12426 file's mock does) sendsAppContentinto an endless re-render once anything re-renders it. In this file, that showed up as a 15 s timeout as soon as a held read answered. The shipped provider is not affected.Generated by Claude Code