Skip to content

perf(console): answer an app page's views, page and dashboard reads from the app bundle first - #28754

Merged
objectstack-fleet[bot] merged 15 commits into
mainfrom
claude/project-thread-6p9fk2
Nov 1, 2026
Merged

objectstack-fleet[bot] merged 15 commits into
mainfrom
claude/project-thread-6p9fk2

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Part of #28645

Clause-②: yes (widening)
Metadata-protocol: none

维护者速读

What changes

The app metadata bundle (#27627) answers by-name reads, but the console's readers looked names up in whole lists. This PR moves those readers to the bundle first:

  • MetadataProvider stops reading /meta/view at mount (EAGER_TYPES = ['app']). A route's object scope composes each object the bundle holds with the bundle's views. It reads the /meta/view list only when a name it asks is outside the bundle, and only for the objects the bundle does not hold. A list already in hand keeps answering while a bundle loads, so a settled scope never unsettles. The whole registry (objects) still reads the list beside it.
  • Bundle-first by-name reads (providers/bundledReads.ts): PageView, AppContent's page scope, AppHeader's phone title, useRecentItemLabel and useNavTargetLabel read a page or dashboard from the bundle, prefer-local by owner package. A miss falls back to the list as before.
  • One bundle entry, at the console root: useAppObjectScopePrefetch enters the app's bundle as soon as the app list names the app. AppContent reuses that entry (enterAppBundle) instead of revalidating it. Leaving the app ends the entry.
  • useObjectPresence asks its object with a package-local views: false and reads no views.
  • useServedViewItems(read): AppHeader reads views only on an object's view route.
  • @object-ui/data-objectstack: new ObjectStackAdapter.peekAppBundle() and the exported types AppBundlePeek, AppBundleItems, AppBundleEntry, with TSDoc. This is the Clause-② widening.

NavigationSyncEffect (useNavigationSync.ts) is untouched. It still reads /meta/page and /meta/dashboard, which PR 2 removes (#28680, maintainer ruling A).

Tests

  • New MetadataProvider.bundleFirstReads-28645.test.tsx (17 cases) and appBundlePeek-28645.test.ts (5 cases). appObjectScope-12811.test.tsx adds the root bundle entry and its reuse through the console's adapter wrapper.
  • Updated pins that relied on the eager view read: objectScope-12811, namesOnlyScope-12980, oneMetadataRead-13044, switchDropsAdapter-13044, ConnectedShell.sessionGate.
  • Each new rule was ablated and each one turned its pin red: eager view, bundle views, views: false, the pending-before-enter rule, bundled objects keeping bundle views, and the root entry.

Acceptance notes

Measured on a production console build (pnpm build + pnpm console:build, pnpm dev -- --fresh), showcase_app, admin, API calls of a warm reload, two runs each. Base 2678600d, head of this PR:

Route Base PR 1 Difference
page/showcase_start_here 26 26 /meta/view −1, /api/v1/packages +1
dashboard/showcase_ops_dashboard 35 35 none
showcase_task (object list) 33 33 none

Expected with PR 2 (NavigationSyncEffect stops reading the lists): page 26 → 24, dashboard 35 → 33. That is /meta/page and /meta/dashboard −1 each, plus this PR's view saving on the page route. This PR ships at net zero because PR 2 needs these readers off the lists.

Deviations:

  • /api/v1/packages +1 on the page route. PageView now draws on the bundle about 250 ms before the page list would land. Its Studio design gate (useStudioDesignGate) therefore asks /packages outside the in-flight window the sidebar's gates share. packages-io.ts forbids a reuse window, so this PR does not add one.
  • The dashboard route keeps /meta/view. It reads the whole object registry (objects), as on main, and the bundle cannot spare that read without changing the dashboard route's whole-registry rule.
  • The object list route keeps /meta/view. showcase_task's lookup target sys_user is outside the bundle, so its scope reads the list for the objects the bundle lacks. A reader skips views only where its output is the same without them (useObjectPresence).
  • File surface past the first claim: app-bundle.ts, the new bundledReads.ts, appObjectScope.ts and useObjectPresence.ts (claim amendment 6559809254). AppContent's page route no longer waits for the page list bucket. RecordDetailView is unchanged, because usePageAssignment still reads the page list on record routes.
  • A miss falls back to the list read rather than a by-name read, which keeps prefer-local resolution.

Verification

Derived gates (35)

Run at 23fefe55 (the last commit after it only replaces a module variable in appObjectScope.ts; check-sdui-manifest re-ran at the final head, exit 0). dispatch-gates --ran: 35 derived, 35 run, 0 NOT-MEASURED.

node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-sdui-manifest.mjs :: exit 0
node scripts/check-sdui-manifest.mjs --self-test :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --dir ui exec vitest run scripts/ :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:error-status-conformance :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0
Tests and type-check
  • 427 test files naming the touched modules (app-shell, data-objectstack, console), 6 locked batches at --maxWorkers=2: 427 passed, 3527 tests. The full app-shell suite (~71 min) is left to CI's Test UI shards.
  • pnpm turbo run type-check --filter=@object-ui/app-shell --filter=@object-ui/data-objectstack: 66/66 tasks, exit 0.
  • node scripts/check-sdui-manifest.mjs: exit 0.
  • Merged origin/main at e735b79e with no overlap on touched files and no parallel fix found.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic.
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes. (run as eslint --quiet over the changed ui files: 0 errors; the whole-workspace lint is left to CI)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: its generated artifacts are regenerated and committed. (not touched)
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English. (except the 维护者速读 block the dispatch rules require)
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QsR2jUjXYpqdaAkEWBHbqG

claude added 14 commits November 1, 2026 13:27
…rom the app bundle first

The provider no longer reads /meta/view at mount. A scope composes its
objects with the app bundle's views when the bundle holds every object in it,
and the page route, the app bar title and the nav and recent-item labels read
their page or dashboard from the bundle, reading the list only for an item the
bundle does not hold.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jUjXYpqdaAkEWBHbqG
… list on demand in the list-sharing pins

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jUjXYpqdaAkEWBHbqG
…provider stand-in leaves readers on their list

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jUjXYpqdaAkEWBHbqG
… when every asked object is bundled

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jUjXYpqdaAkEWBHbqG
…ist, and let presence skip views

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jUjXYpqdaAkEWBHbqG
…he adapter wrapper

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jUjXYpqdaAkEWBHbqG
… the list loads for the rest

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jUjXYpqdaAkEWBHbqG
…it waits for its bundle

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jUjXYpqdaAkEWBHbqG
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling labels Nov 1, 2026
…p while it revalidates

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jUjXYpqdaAkEWBHbqG
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu

Contract review

Served-tier: TIER_DEFAULT
Head-sha: 935a89e3ee7464b4a950d1cb6e3113fabe9d4660
Local-runs: none

① Derived judgments

  • Face: the Clause-②: yes (widening) declaration alone. The PR's 21 files (.changeset/, ui/packages/app-shell/src/**, ui/packages/data-objectstack/src/**) touch no packages/spec/src/** and no governed path (check-governed-merges --pr 28754: 0 of 21 paths hit the register).
  • Published surface moved: @object-ui/data-objectstack gains ObjectStackAdapter.peekAppBundle() and the exported types AppBundlePeek, AppBundleItems, AppBundleEntry. All additive; no export is removed or renamed and no existing signature changes. @object-ui/app-shell is private: true.
  • The contract as documented: none before any app is entered; pending only while the active app has never answered (first load, or a reload after clear()); ready otherwise, empty for an app with no bundle, with a revalidation on the wire riding along as settled, which never rejects (ask catches into absent).
  • At 75df9274 the seat found peek() deciding pending by items.size === 0, so a no-bundle app past its lifetime or after markStale() answered pending against its TSDoc. Fixed at 935a89e3 (state.checkedAt === 0), with three pins in appBundlePeek-28645.test.ts (absent past TTL, absent after invalidateCache(), zero-item 200 past TTL), red on the old condition per the dev's ablation.
  • peek() re-asks a stale bundle as a side effect. That is documented in both TSDocs and guarded by inflight, so repeated peeks in one render ask once.

② Semver level

  • Changeset: @object-ui/app-shell patch, @object-ui/data-objectstack patch, Clause-②: yes (widening) and Metadata-protocol: none line-initial in the changeset and the PR body. A non-protocol widening ships patch (AGENTS.md Post-Task Checklist step 3): consistent.

③ Boundary flags

Implemented-by: claude/project-thread-6p9fk2
Reviewed-by: session_01QsR2jUJ19MstFvFhjgiYTu

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu

席位核对 · ACCEPT · 2026-11-01T17:52Z

Head-sha: 935a89e3ee7464b4a950d1cb6e3113fabe9d4660

  • Shape: draft → main, first content line Part of #28645 (PR 1 of 2; the card stays open for PR 2, [Decision] console: write app navigation when a page or dashboard is created or deleted, instead of diffing the whole page and dashboard lists on every app page #28680). 21 files, +1281 / −59 (1340 lines ≤ 3000), no governed path (check-governed-merges --pr 28754: 0 of 21). Clause-②: yes (widening) / Metadata-protocol: none line-initial in body and changeset, as amended (6558598734). Contract review TIER_DEFAULT PASS 6561446838 at this head.
  • Read in the diff:
    1. The app page's views, its page and its nav dashboards are read bundle-first (providers/bundledReads.ts, MetadataProvider.tsx, PageView.tsx, useNavTargetLabel, useRecentItemLabel); a name outside the bundle, or a same-named item another package owns, reads the list as before, prefer-local kept.
    2. The console root enters the bundle once and AppContent reuses the entry (amendment 6559809254); presence asks with views:false; the app bar reads views only on an object's view route.
    3. peekAppBundle() and AppBundlePeek / AppBundleItems / AppBundleEntry carry TSDoc; the seat's review fix (pending only before a first answer) is pinned by three tests.
  • Measured (dev, warm reload, production build, showcase_app): page 26 → 26 (view −1, /packages +1 from PageView's earlier Studio gate; no reuse window, per packages-io.ts), dashboard 35 → 35 (whole-registry view read kept), object list 33 → 33. Net zero alone, as ruled: this PR is PR 2's precondition, which is expected at page 24 / dashboard 33. Both measurements are in the PR body.
  • Gates (check-runs on this head): 44 success, 11 skipped, 0 failed or pending. mergeable_state clean. The red Test UI / Lint & Repo Gates on 75df9274 were the superseded head's cancelled lanes.
  • Tests: 427 files / 3527 tests around the touched modules; six ablations red then restored; data-objectstack 112 files / 1277 tests at this head; type-check 66/66.
  • Out of scope / NOT MEASURED: no live Console run by the seat. The dev's note (a none bundle ask recorded without a provider re-render settles on the next commit) is reasoning only, no reach measured, not filed. Skills-docs: not owed (none, no narrowing arm).
  • Landing: ordinary queue landing, pr_ready then automerge_enable. Part of closes nothing; console: on an app page, answer /meta/view, /meta/page and /meta/dashboard from the app bundle (#28321) instead of three list reads; page/pricing 24 to ~21 calls #28645 closes with PR 2.

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

Labels

documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant