Skip to content

perf(app-shell): a dashboard opens from the app bundle and asks only its widgets' objects by name - #29472

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-9lsrc1
Nov 2, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-9lsrc1

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #28784

Clause-②: no
Metadata-protocol: none

Claim: 6575848918

维护者速读

  • 改了什么:打开应用里的仪表盘时,仪表盘定义直接从应用 bundle 读,不再读整张仪表盘列表;对象只读本应用包的对象列表,加上小部件 requiresObject 点名的对象(按名读),不再读整张对象注册表和视图表。
  • 为什么改:每次打开仪表盘都要等整表对象(showcase 上 746 KB,staging 上 3.4–7.0 秒)、视图表和仪表盘列表都返回后,图表查询才发出,而 bundle 里已经有这个仪表盘。
  • 风险与回滚:bundle 里没有的仪表盘(别的包的、草稿预览)照旧读列表,按本包优先;小部件依赖的对象不存在时照旧隐藏该小部件,改为按服务器的"不存在"答复判断。回滚即 revert 本 PR。
  • 先看:ui/packages/app-shell/src/console/appObjectScope.ts(仪表盘路由的对象范围,readDashboardWidgetObjects / readDashboardObjectScope)、ui/packages/app-shell/src/views/DashboardView.tsx(bundle 优先读仪表盘、按名剪枝小部件)、ui/packages/app-shell/src/console/AppContent.tsx(仪表盘路由不再等 dashboard 桶)。
  • 两分钟验证:打开 showcase 的 /apps/showcase_app/dashboard/showcase_ops_dashboard,开发者工具 Network 过滤 meta/:看不到 meta/dashboard、meta/view,也看不到不带 ?package= 的 meta/object;九个图表照常出数。
  • 需要你做的:无,席位落地。

What changes

On main, every open of /apps/APP/dashboard/NAME read three whole lists before its widgets could query: GET /api/v1/meta/dashboard, the whole object registry GET /api/v1/meta/object (composed with GET /api/v1/meta/view). The route gate (routeReads for dashboard/:dashboardName is ['dashboard', 'object']) waited for both buckets, and the dashboard route was not on the object-scope list, so the object bucket meant the whole registry. The app bundle already held the dashboard.

Reader Before Now
DashboardView: the dashboard /meta/dashboard list, prefer-local the bundle's, when it holds the one the app's package owns (useBundledItem); the list, prefer-local, only on a miss
DashboardView: the widget capability gate (requiresObject, else object) the whole registry; a name absent from it hides the widget readDashboardWidgetObjects: the widgets' names, by name, beside the app's package list, without views; a name the server answers absent (missing) hides the widget
AppContent: the route gate waits for the dashboard and the whole object buckets drops dashboard when bundle reads exist (as the page route does); the route reads the app's object scope (dashboard/:dashboardName joins APP_OBJECT_SCOPE_ROUTES) and waits only for the widgets' names, and for nothing when they name none
AppContent: the chrome's objects the whole registry the app's scope: the package list plus, by name, the widgets' names, the app's requiresObject entries and the palette's objects while it is open

readObjectsByName was not needed: the package-plus-names scope already reads an out-of-package name by name. A hand-rolled metadata context (no readObjectScope) keeps the whole list, and an adapter without the bundle keeps both buckets.

Tests

  • New console/__tests__/AppContent.dashboardRouteReads-28784.test.tsx, over the real MetadataProvider, AppContent and DashboardView, a client double that records each read and an app bundle the test controls: (a) a bundled dashboard opens with no dashboard list, no whole object list and no view list; (b) the widgets' objects are asked by name and a widget whose object is absent is left out; (c) CONTROL: a dashboard outside the bundle is read from the list, prefer-local, still without the whole object list.
  • Updated console/__tests__/openPathObjectReaders-12811.test.ts: the DashboardView row is now scope.
  • No existing DashboardView*.test.tsx pin moved.

Acceptance notes

  • Expected 32 → 30 on the claim; measured 32 → 30 (see Verification).
  • On localhost the first dataset query moves little (the bundle, which the route still waits for, is the longer wait there); the change removes 782,598 bytes of metadata reads from the route's critical path and adds one 157,802-byte package list. On staging the comment's readings put the whole object list at 3.4 → 7.0 s.
  • design/dashboard/:dashboardName (the designer) and the search route still read the dashboard list; out of this card's scope.

Verification

Dashboard requests, production Console, showcase (objectstack dev --fresh), warm reload x3

Base 96fb7b5d (the ui sources restored to it, same box, same server build): 32 /api/ requests (one run 33). GET /api/v1/meta/object 746,514 B, GET /api/v1/meta/view 24,009 B, GET /api/v1/meta/dashboard 12,075 B, all after the bundle; first POST /api/v1/analytics/dataset/query at 1709 / 1135 / 1006 ms.

This branch ae3d70fb: 30 requests. No meta/dashboard, no meta/view, no meta/object without ?package=; GET /api/v1/meta/object?package=com.example.showcase 157,802 B instead. First dataset query at 1654 / 1032 / 885 ms. The nine dataset queries and every other request are the same set.

Ablations (each leg through scripts/ablation-replace.mjs, restored with git diff HEAD empty)
  • A1 route gate keeps the dashboard bucket → (a), (b) red.
  • A2 DashboardView ignores the bundle's hit → (a), (b) red.
  • A3 dashboard/:dashboardName off APP_OBJECT_SCOPE_ROUTES → (a), (b), (c) red.
  • A4 the prune ignores missing → (b) red.
Local runs
  • 108 app-shell test files naming DashboardView, appObjectScope, readAppObjectScope, AppContent, openPathObjectReaders, appRouteMetadataReads: 108 files / 878 tests passed; the new pin 3/3.
  • pnpm --filter @object-ui/app-shell run type-check: exit 0.
  • eslint --no-inline-config on the changed files: 0 errors. check:new-line-citations: 0 new. pnpm --dir ui check:spec-symbols: exit 0.
  • Derived gates (node scripts/pm/dispatch-gates.mjs --commands) at ae3d70fb: 34 derived, 34 run, all exit 0, reconciled with --ran (0 NOT-MEASURED); check:type-check-debt and pnpm --dir ui exec vitest run scripts/ (169 files passed, 2 skipped) under the verify lock; node scripts/check-sdui-manifest.mjs exit 0 among them. The full app-shell suite is CI's Test UI.

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.
  • 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.
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QsR2jVVA94oekr4rqqQgUJ

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 2, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu

席位核对 · ACCEPT · 2026-11-02T16:08Z

Head-sha: ae3d70fbe3df44f5713daab8c1053be28ba46276

  • Shape:
    • The PR is a draft against main. The first card line is Fixes #28784, under claim 6575848918.
    • 7 files, +467 / −20: AppContent.tsx, appObjectScope.ts, appRouteMetadataReads.ts (comment only), DashboardView.tsx, the new AppContent.dashboardRouteReads-28784.test.tsx, one row in openPathObjectReaders-12811.test.ts and a root changeset. That is the claimed surface. check-governed-merges --pr 29472: not governed, 487 changed lines, under the human-merge threshold.
    • Clause-②: no and Metadata-protocol: none start their lines in the body and the changeset, as claimed. No spec key, prop or pack key is new; the new exports are app-shell internals. The changeset is patch.
  • Read in the diff:
    • DashboardView no longer destructures dashboards or objects. It takes the dashboard from useBundledItem on a hit, reads the list prefer-local only on a miss, and treats a pending bundle as loading, so the not-found and refusal probe waits for the bundle.
    • AppContent drops the dashboard bucket only when bundle reads exist (the page route's pattern), and the dashboard route joins APP_OBJECT_SCOPE_ROUTES. Its scope reader is bundle-first with the same prefer-local fallback, so the gate and the view resolve the same dashboard.
    • readDashboardWidgetObjects asks only the widgets' requiresObject / object names, by name, views: false, riding the package scope. A dashboard naming none settles at once and waits for nothing. The prune keys on the server's missing answer; metadata.objects is read only when there is no readObjectScope, so a hand-rolled context keeps the old behaviour.
    • An adapter without the bundle keeps both buckets: the filter is gated on bundledReads.
  • Pins changed: one row in openPathObjectReaders-12811.test.ts (DashboardView → scope), which is the point of the card. No DashboardView*.test.tsx pin moved.
  • Gates (check-runs on this head): 44 success, 11 skipped, 0 failed or pending. mergeable_state is clean.
  • Tests (dev):
    • New pin 3 of 3: bundled dashboard reads no dashboard, object or view list; widget objects by name with an absent one pruned; CONTROL, an unbundled dashboard reads the list prefer-local, still without the whole object list.
    • Ablations A1 to A4 each red the expected legs and restore clean.
    • 108 app-shell files, 878 tests; type-check exit 0; derived gates 34 of 34, 0 NOT-MEASURED.
    • Live, showcase production Console against base 96fb7b5d: 32 → 30 requests. meta/dashboard, meta/view and the whole meta/object (746,514 B) are gone; meta/object?package= (157,802 B) is added; the nine dataset queries are the same set.
  • NOT MEASURED: the seat ran no live session. Staging timing is not re-measured; the localhost first-query moves little because the bundle is the longer wait there, as the body says.
  • Findings: not filed. design/dashboard/:dashboardName and the search route still read the dashboard list; the body records them as out of scope, and they are not this card's acceptance.
  • Serial: none open.
  • Landing: ordinary queue landing, pr_ready then automerge_enable. Fixes #28784 closes the card on merge.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 2, 2026 16:10
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit 94d3e25 Nov 2, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-9lsrc1 branch November 2, 2026 16:23
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/m tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

console: the dashboard route reads its dashboard from the app bundle, so an app's dashboard open stops reading the /meta/dashboard list

1 participant