Skip to content

app landing: a nav entry whose dataSource read is pending is skipped as the landing (and an all-bound app flashes no-access) #13097

Closed

Description

@objectstack-fleet

Problem

objectui#13029 (PR #13095) makes a navigation entry's declared API dataSource feed its visible, failing closed: an entry whose read is pending or failed is hidden. That is the card's rule, and the sidebar, nav:menu and the command palette honour it.

AppRootLanding (packages/app-shell/src/console/AppContent.tsx) chooses an app's landing from navEntriesShownToMember(…, gates). A pending entry is dropped there, and appServesNoNavigation is then asked over the filtered trees. The contract review of PR #13095 (on objectui#13029) reads two consequences that are new with that PR, because bound entries used to be ignored and therefore shown:

  1. An app whose entries are all bound shows AppNoAccessEmptyState until the reads answer, then re-renders and navigates. The user sees a "no access" flash.
  2. An app whose first entry is bound and whose second is plain navigates (with replace) to the second entry on first render, before the read answers. A pending bound entry can never be the landing, even when its read would have shown it.

Both are source readings from the review. Neither has been measured in a browser.

Target

The landing waits on, or re-resolves after, the reads of the entries it would choose between. Pick one of these and pin it:

  • (A) While any entry ahead of the first plain one is pending, the landing holds (the splash or a skeleton, not the no-access state), then lands on the first entry the member is shown once the reads have answered.
  • (B) A ruling that first-render landing on the first plain entry is intended. Then only (1)'s no-access flash is fixed: hold while every candidate is pending.

objectui#12426's rule ("the landing is the first entry this member is shown") reads closer to (A).

Acceptance

  • Pins through the real AppRootLanding:
    • all-bound pending shows no AppNoAccessEmptyState;
    • first-bound pending, then ready and shown, lands on the bound entry (A) or on the plain one (B), as ruled;
    • a failed read lands on the next shown entry;
    • an app with no bound entry lands on first render exactly as today (control).
  • One measured browser reading of an app whose first nav entry is bound (cloud#3002's 环境 row is the named consumer).

Surface

packages/app-shell/src/console/AppContent.tsx (AppRootLanding), utils/appServesNoNavigation.ts if the predicate needs a pending state, hooks/useNavMemberGates.ts (it already exposes the read state), and tests beside them.

Filed from the objectui#13029 contract review (PR #13095) by domain:ui seat 2 (session_01XVp19E1MYUTFM3dnkwz8gB).


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: p2 · bug · lane domain:ui · area:api · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-19T23:54Z

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015). ⛔ Not a claim, ⛔ not a dispatch.

    Filing-gate answers completed under ruling 「B」 (objectstack-ai/objectstack#24589).

    • Category ①: a named producer read from source by PR feat(layout,app-shell): a nav entry reads its declared API dataSource, so its visible sees the selected value as data #13095's contract review. The defect is new with that PR, which merged at 23:39Z.
    • Reader who acts: the objectui domain:ui execution seat, on the card's surface list.
    • Dedupe: MCP search_issues "app landing nav entry dataSource pending skipped landing no-access flash AppRootLanding bound entry", objectui, closed included: 7 hits.
      • The nearest is objectui#12426 (closed): the landing is the first entry this member is shown.
      • Next are objectui#4473 and objectui#6507 (closed, other landing and boot-gate faults).
      • None covers a pending bound entry.

    Fresh read (objectui origin/main 633ef99a3f). AppRootLanding reads navEntriesShownToMember(app?.navigation || [], gates) (AppContent.tsx:1621) and asks appServesNoNavigation over the filtered tree. A pending entry is already gone by then. The premise holds.

    Direction: (A). No ruling is needed. objectui#12426's rule is "the landing is the first entry this member is shown". An entry whose read is still pending has not been decided, so landing past it (B) answers a question the read has not answered yet. While a candidate ahead of the first plain entry is pending, the landing holds on the splash or a skeleton, never the no-access state, then lands as #12426 says.

    Why p2. It reaches only apps whose nav entries declare a dataSource, which is new with objectui#13029; the named consumer is cloud#3002's 环境 row. But for those apps, the first render either flashes "no access" or lands on the wrong entry, and both are visible to every member.

    First act: the browser reading the card's acceptance names, before the fix.


    Generated by Claude Code

  2. added
    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobs
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Oct 19, 2026
  3. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 14 (priority:p2)
    Session: session_01XVp19E1MYUTFM3dnkwz8gB
    Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-13097-landing-waits-reads
    Worktree: objectui-issue-13097
    Domain: domain:ui
    Seat: domain:ui#2
    File surface:

    • packages/app-shell/src/console/AppContent.tsx (AppRootLanding). While an entry ahead of the first plain shown entry has a pending read, the landing holds on the splash or a skeleton, never AppNoAccessEmptyState. Once the reads answer, it lands on the first entry the member is shown (objectui#12426). This is direction (A), per triage 6279966884.
    • packages/app-shell/src/utils/appServesNoNavigation.ts, if its predicate needs a pending state.
    • packages/app-shell/src/hooks/useNavMemberGates.ts, only to expose the read state objectui#13029 already holds. No new fetch.
    • Tests beside them.
    • One .changeset/13097-landing-waits-reads.md (@object-ui/app-shell patch).

    ⛔ No new package export. Stop on breach; explain in the report.
    Container & model: S, mode:subagent, model: opus (default tier; no path-derived mandate over this surface)
    Clause-②: no (the console's landing choice for apps with bound nav entries; no export, key or prop moves)
    Responsibility: AppRootLanding filters pending bound entries out before choosing | objectui#13029's useNavMemberGates already holds each binding's read state | every member opening an app whose nav entries declare a dataSource (cloud#3002's 环境 row is the named consumer)
    Thread-read: 6279966884
    Serial constraints cleared: objectui#13029 (1b94d50) landed. The in-flight objectui#12725 PR #13100 (Studio, data-objectstack) touches none of these files. No other open objectui PR touches them (read at 00:23Z).
    Claimed at: 2026-10-20T00:23Z


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 13097,
    "status": "done",
    "branch": "claude/issue-13097-landing-waits-reads",
    "pr": "#13105",
    "session": "session_01XVp19E1MYUTFM3dnkwz8gB",
    "premise_still_valid": true,
    "summary": "Direction (A). AppRootLanding now asks the landing twice: once over the trees as the member is shown them, and once over the trees as if every pending dataSource entry were shown once it answers (navEntriesShownOncePendingReadsShow, with the same gates except that a pending entry's visible is treated as undecided). When the landing route or the no-access answer differs between the two, it holds on the existing SkeletonGrid (test id app-landing-pending). It neither navigates nor shows AppNoAccessEmptyState, and it lands when the reads answer. There are no extra reads: useNavMemberGates also returns readEntry (NavMemberGatesWithReads, module-internal), and the NavMemberGates shape is unchanged. Base measured first, through the real AppContent with a held apiFetch: (i) the all-bound app held app-no-access-empty-state at /apps/crm; (ii) with the first entry bound and pending, the app was already at /apps/crm/crm_case on first render. Surface note: appServesNoNavigation lives at console/appServesNoNavigation.ts, not utils/; it was not edited. No browser reading was taken (NOT MEASURED: no backend booted).",
    "tests": "New file packages/app-shell/src/console/tests/AppContent.landingWaitsReads-13097.test.tsx, pins (a) (b-ready) (b-failed) (c) (d CONTROL, MutationObserver: no hold ever drawn, no read). Base run: 4 failed / 1 passed. With the fix: 5 passed. Ablation via objectstack scripts/ablation-replace.mjs (hold condition replaced by false; anchor 1 to 0; blob 9667c66bf4a0 to 5c960f6b8053): (a) and both (b) red, (c) and (d) green (3 failed, 2 passed); restored blob == HEAD 9667c66bf4a0, git diff HEAD empty. On final head 0b5e5b4 under the lock: app-shell type-check (tsc --noEmit && tsc -p tsconfig.test.json, which includes src/**/*.test.tsx) exit 0; vitest on 13097 + 12426 + 12079 + 13029: 4 files, 42 tests passed. Family (42 test files naming useNavMemberGates / navEntriesShownToMember / AppContent, in packages and apps) on 49f4556: 21 files 172 tests + 21 files 199 tests, all passed; the only later commit is type-only. Closure build (app-shell^...) exit 0. ESLint --no-inline-config on the 3 changed ts/tsx files (json count 3): 0 errors; AppContent.tsx has 43 warnings against 44 on base, useNavMemberGates.ts 2 against 2; no type-aware lint configured. Battery exit 0: phantom-deps, self-import, unreferenced-sources, doc-example-readers, handler-key-reads, side-effects-array, element-data-source-declaration, spec-symbols, action-forward-parity, designer-field-key-parity, icon-record-names, i18n-keys, i18n-drift, i18n-designer-parity, control-bytes, new-line-citations, check-hand-rolled-comment-mask, check-changeset-fixed, check-changeset-presence. Governed guard: NOT GOVERNED. Declared to CI: the full packages/app-shell dir and pnpm lint.",
    "mcp_calls": "0",
    "api_writes": "2 relay strokes: pr_create (POST /repos/objectstack-ai/objectui/pulls, draft, plus POST /issues/13105/assignees baozhoutao) and this os-dev-report comment (POST /repos//issues/13097/comments). Plus 3 git pushes of the branch. No label writes (objectui path labels belong to labeler.yml; the dispatch named none).",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: none (no one will touch the file) · noted in the PR's Acceptance notes, not filed: the useAuth mock in AppContent.landingMemberGates-12426.test.tsx returns a fresh getAuthConfig on every render. AppContent's auth-config effect is keyed on that identity, so any re-render loops it without end. It surfaced here as a 15 s timeout once a held read answered. Test harness only; the shipped provider is not affected.",
    "carrier: none · noted, not filed: the hold has no timeout. A deciding nav read that never settles keeps the landing skeleton up, as the permissions loading fallback does."
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #13105 @ 0b5e5b4b15e240fc938e4595a045bf6aebee42df · domain:ui seat 2 (session_01XVp19E1MYUTFM3dnkwz8gB)

    • Clause-②: no. AppRootLanding is internal. useNavMemberGates / NavMemberGatesWithReads / navEntriesShownOncePendingReadsShow are not on @object-ui/app-shell's entry (checked), and the NavMemberGates shape is unchanged. No contract review is owed, and the seat read the diff.
    • Seat review:
      • The landing is asked twice: over the shown trees, and over the trees as if every pending bound entry were shown. When the route or the no-access answer differs, it holds on the existing SkeletonGrid and never navigates or shows no-access. That is direction (A) from triage 6279966884, and it holds only while a pending read can still change the answer.
      • It reuses the read state objectui#13029 already holds (readEntry), with no extra read.
    • Measured first on base through the real AppContent with a held apiFetch: (i) an all-bound app showed app-no-access-empty-state; (ii) a first-bound app was already at the second entry on first render. Pins (a), (b-ready), (b-failed), (c) and the (d) CONTROL (no bound entry: no hold ever drawn, no read) went 4 red / 1 green on base and 5/5 with the fix. The ablation (hold off) turned (a) and both (b) legs red, with the restore proven by blob.
    • Changeset: @object-ui/app-shell patch.
    • CI: all 36 check-runs completed on the head, 33 passed and 3 skipped.
    • Path fork: no governed path, 423 changed lines. Seat landing.
    • Findings:
      • Acceptance notes: the hold has no timeout. A bound read that never settles keeps the skeleton up, as the permissions-loading fallback does. A failed read releases it.
      • Acceptance notes: the useAuth mock in the objectui#12426 landing test returns a fresh getAuthConfig on each render (test harness only; the shipped provider is unaffected).
      • The browser reading the card's acceptance names is NOT MEASURED (no backend booted). The landing is pinned through the real AppContent.

    Next: ready + auto-merge; landing is verified by content on main.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:ui seat 2 · session_01XVp19E1MYUTFM3dnkwz8gB

    • Merged: PR fix(app-shell): the app landing waits on its nav entries' pending API reads #13105 through the merge queue as main d87e4f2ec1.
    • Verified by content: each of the 4 files the PR changes has the same blob at d87e4f2ec1 as at the accepted head 0b5e5b4b15. The merge message carries no model identifier.
    • Landed: the app landing holds on its skeleton while a pending bound nav entry could still change the landing route or the no-access answer. It then lands on the first entry the member is shown (objectui#12426). An app with no bound entry lands on first render as before.

    pm:dispatched is removed.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions