Skip to content

feat(studio): an All metadata area in the builder, per package and across packages - #28623

Merged
objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/project-thread-ddww6q
Nov 1, 2026
Merged

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

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #28561

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:Studio 构建器在四个支柱旁加了「全部元数据」区域,把 Setup 现有的元数据页(类型目录、列表、通用条目页、历史、诊断)挂进来,不复制。按包是 /studio/<包>/metadata/…,跨包是 /studio/~all/metadata/…,后者服务环境目录(岗位、能力、权限集、共享规则,?scope=environment)。支柱类型(object、flow、page、dashboard、report、view、permission)从区域打开时跳到对应支柱并带 ?surface=,其余类型打开通用条目页。
  • 为什么改:ADR-0146 D1 规定 Studio 负责全部元数据,N-26a([Epic] Package-level override: every customization of an installed package lives in its paired <base>.custom package, one declared override shared by code and Studio (supersedes #28130) #28236 的子卡)。以前只有 Setup 能列出所有类型。
  • 风险与回滚:没有新的写入路径。可写包的保存与 Setup 带 ?package= 时完全相同。只读包(含切换器隐藏的内核包)上,条目页、历史、权限编辑器和新建都只读。Setup 的页面没有改动:所有新行为都挂在一个只有区域才提供的 context 上,没有 provider 时页面行为不变。回滚就是 revert 本 PR。
  • 先看:studio-design/metadataArea.ts(两条路由腿和跳转规则)、studio-design/StudioMetadataArea.tsx(区域本身与只读判断)、metadata-admin/metadataAreaHost.ts(页面读到的 context)。
  • 两分钟验证:打开 /studio/<可写包>/metadata,确认目录只列该包、顶栏「全部元数据」高亮;点一个 flow 应落到 Automations 并带 ?surface=flow:<名>;点一个 translation 应打开通用条目页。再打开一个只读包的 translation,应看到只读提示、没有编辑或重置按钮。最后从 Studio 首页的「环境目录」进入 /studio/~all/metadata。
  • 需要你做的:无需决策。N-28 做 Setup 重定向时须放过 datasource:区域会把 datasource 的地址送回 /apps/setup/metadata/datasource,两边都重定向就会循环。

What changes

  • The area, per package. /studio/<package>/metadata/… mounts the metadata-admin pages Setup mounts, with the route shapes Setup has under /apps/setup/metadata. The pages are lazy and load through the same specifier (../metadata-admin/index.js) that AppContent uses. Studio and Setup share one chunk, so the builder's own chunk does not grow by them. The header links the area beside the four pillars and names it from 1280 px, because the 1024 px row was sized for the four pillar names. The console route is :packageId/metadata/*. Without it, :packageId/:tab matches one segment only, and an area URL such as /studio/crm/metadata/flow would match no route.
  • The cross-package leg. /studio/~all/metadata/… serves the environment catalog with no package: position, capability, permission and sharing_rule, each opened with ?scope=environment. It is the area alone, in the frame the Studio landing draws. The landing's "Not in a package" row gains an "Environment catalog" card. ~all is a reserved segment that no package id can spell (metadata is a legal package id, so there is no bare /studio/metadata). It is not ~org, the package-less flows scope. Every other ~all URL lands on the area.
  • The query keeps its Setup meaning (areaLocationRedirect):
    • ?package= naming another package re-roots onto that package.
    • ?scope=environment and ?package=all move to the cross-package leg.
    • A package leg without ?package= gets its own. The item page binds its saves to ?package= (readActivePackageBinding).
    • ?review=1 opens the generic item page and its review.
  • Pillar types open their pillar with ?surface=: object → Data, flow → Automations, page, dashboard, report and view → Interfaces, permission → Access. Every other type opens the generic item page, and so does any item on the cross-package leg, which names no package. The choice is made once per item, so the item page dropping ?review=1 after it opens the review does not send an open review on to the pillar.
  • Interfaces opens an off-nav item by its link. A page, dashboard, report or view that no nav entry opens used to land on the first nav leaf. It now opens from the package's own lists once they load, and falls back to the first leaf only when the package has no such item. This is what makes the Interfaces redirect land on the item.
  • Read-only package. The area reads GET /api/v1/packages through fetchListedPackageManifests, which keeps the kernel packages that the switcher hides. A package listed as not writable gets a read-only area:
    • The item page shows a package read-only banner and offers no edit, overlay save, source save, reset or delete.
    • The history page offers no rollback.
    • The permission editor offers no Save.
    • The list offers no create, and a create URL lands on the list.
    • Layers, references, history, audit and source stay.
    • A package the list cannot answer for stays ungated, as the pillars do. The server's write gate is the authority.
  • Setup's parts stay Setup's (ADR-0146 D7).
    • A position's holders and a permission set's assignments are a link to their Setup page (SetupAssignmentsLink), not drawn.
    • The datasource tile and every datasource URL in the area open Setup's Datasources page.
  • Setup's pages do not change. Every page reads the area through MetadataAreaHostContext, which only the area provides. With no provider each page renders as it does today.

Outside the card's file surface, with reasons:

  • item-tools/ResourceHistoryPage.tsx: the routed history page gets the rollback gate and its "back to item" link under the area.
  • i18n studio en.ts / zh.ts: the six new strings and the not-found sentence naming "All metadata".
  • app-shell src/index.ts: exports the area and its route constants for the console.
  • console studioBuilder.ts: the one lazy boundary the console loads the builder through.

Tests

  • studio-design/metadataArea.test.ts: the legs, every redirect rule with the case that would break it, and the pillar map.
  • studio-design/StudioMetadataArea.routes-28561.test.tsx (stub pages that print the host) covers:
    • mounts on a writable package, a read-only package, a kernel package, an unanswerable list, and the cross-package leg;
    • the environment scope re-rooting, diagnostics and history, create on a read-only package, and the datasource URL;
    • object, flow, view and permission landing in the right pillar with ?surface=;
    • a translation opening the generic page;
    • ?review=1 opening and staying on the review;
    • a cross-package permission opening the generic page.
  • metadata-admin/metadataAreaHost.gates-28561.test.tsx (real item pages) covers:
    • a code-shipped translation under no area, a writable area and a read-only area;
    • history rollback;
    • the permission editor's assignments link and Save.
  • metadata-admin/metadataAreaHost.scope-28561.test.tsx (real directory and list) covers:
    • Setup's repair as a control, then a package pin with no repair;
    • the datasource tile;
    • the cross-package environment types and the ?scope=environment permission list;
    • no create on a read-only package.
  • studio-design/InterfacesPillar.offNavDeepLink-28561.test.tsx: a view, report, page and app-less dashboard open by link, with controls.
  • studio-design/StudioDesignSurface.metadataArea-28561.test.tsx (real surface and area) covers:
    • the header link, and the area as current on its route;
    • a kernel package read-only, and a missing package with no area.
  • console/StudioRoute.metadataArea-28561.test.tsx (real studioRoutes) covers:
    • both legs, and the ~all landings;
    • the entry gate refusing both legs.
  • Existing header tests count five tabs, and the package-less scope asserts no area link.

Each new gate was checked to fail with its fix taken out. Without the area's canWrite gate, 2 cases fail. Without the off-nav deep link, 3 fail. Without the surface's area prop, 3 fail. Without the two new console routes, 4 fail.

Acceptance notes

Verification

  • The new tests above (10 + 16 + 9 + 9 + 5 + 5 + 8 cases), the edited header tests (43) and the 8 console Studio route files (69): green.
  • The 283 existing test files that import a changed module, run in six batches: green.
  • @object-ui/i18n suite plus the studio-namespace tests (98 files): green.
  • Type-check of @object-ui/app-shell (tests included), @object-ui/console and @object-ui/i18n: green.
  • ESLint on the 29 changed ts/tsx files: 0 errors and 0 findings on changed lines (128 warnings already there).
  • check:i18n-keys, check:i18n-drift and check:i18n-dead-keys: green. node scripts/check-sdui-manifest.mjs: green.
  • Console vite build, then check:eager-closure: 2052.2 KB gzipped eager, against a budget of 2431.6 KB. Every per-chunk ceiling holds, and the 41 ineffective dynamic imports are all already pinned, none new.
  • dispatch-gates --commands: 34 derived, 34 run, all exit 0. --ran reconciles 34 of 34.
  • Main merged at a9c6eac. Main's 9 new commits share no file with this diff.
  • The dogfood battery is n/a: no runtime, protocol or seed change.
  • The diff is 2192 changed lines (873 source, 1319 tests), under the 3000-line Tier H line.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01QsR2jSErqBgsh8YbjzzqZ9

…ross packages

Mounts the metadata-admin directory, lists, generic item page, history and
diagnostics in Studio at /studio/<package>/metadata and /studio/~all/metadata.
Pillar types open their pillar; a read-only package's item page offers no write.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jSErqBgsh8YbjzzqZ9
…unt five tabs

The four pillar names fit the 1024 px row; the fifth tab is named from xl so
the row does not overflow at lg. The existing header tests now count the
area's link beside the pillars.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jSErqBgsh8YbjzzqZ9
The real surface and area on the console's two builder routes: the header
links the area beside the pillars and marks it current on its route, a kernel
package opens it read-only, a missing package has none, and the package-less
scope offers no area link.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jSErqBgsh8YbjzzqZ9
…legs

On the real studioRoutes: a package's area is the pillar builder with its area
open, the cross-package leg is the area alone in the landing's frame, every
other ~all URL lands on it, and the entry gate refuses both legs.

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

Copy link
Copy Markdown
Contributor Author

domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y

席位核对 · ACCEPT · 2026-11-01T09:13Z

Head-sha: db61260351167dca36bed0a4bee06ee3f13a124a

  • Shape: draft → main, first content line Fixes #28561, no other card number beside a closing keyword. 30 files, +2134 / −58 (2192 lines ≤ 3000; 873 source, 1319 tests), no governed path (check-governed-merges --pr 28623). Author objectstack-fleet[bot]. Clause-②: no / Metadata-protocol: none line-initial in body and changeset, as claimed.
  • Clause-② re-measured: the six new strings and the edited not-found sentence live in @object-ui/i18n's studio namespace, declared const STUDIO_EN: Record<string, string> (namespaces/studio/en.ts), outside the locales/* packs. So no published literal key type widens, unlike a locales/en.ts key (feat(console): the row and record-page Delete confirmations name the objects a delete cascades into, from the served cascadeDeleteObjects #28490). The new app-shell barrel exports are in a private: true package. No packages/spec/src/**, so no contract-review face is owed.
  • Wanted, item by item: (1) /studio/<pkg>/metadata/… mounts the metadata-admin pages through the lazy specifier AppContent uses, and the tab is registered, not a not-found state. (2) /studio/~all/metadata/… is the cross-package leg with ?scope=environment. (3) object → Data, flow → Automations, page/dashboard/report/view → Interfaces, permission → Access, each with ?surface=; other types open the generic item page. (4) ?package=, ?scope=environment and ?review=1 are kept through areaLocationRedirect. (5) On a read-only package there is no rollback, source save, reset, delete, create or overlay save, and layers, references, history, audit and source stay. (6) Assignments link to Setup, and datasource opens Setup. (7) Setup is unchanged, because the area-only MetadataAreaHostContext carries the pin. All seven hold in the diff and are pinned by the new tests.
  • Gates (check-runs on this head): 41 success, 11 skipped, 0 failed or pending. All ten required contexts are present and green except Build Core, which its path filter skips on a ui-only diff. The main red main red: Dogfood Regression Gate — GET /packages/:id/customizations is ledgered but not mounted (#28595), so the route 404s at runtime #28622 sits in Dogfood Regression Gate (1/3), a shard this diff's path filter skips. The PR does not touch that route.
  • Accepted deviations (named in the PR body): files outside the surface (ResourceHistoryPage.tsx rollback gate and back link, the studio i18n packs, the app-shell barrel, console studioBuilder.ts). InterfacesPillar opens an off-nav item by its ?surface= link. The header names the area from 1280 px. A package the list cannot answer for stays ungated, as on the pillars, and the server's write gate stays the authority. QuickFind on the cross-package leg is not pinned by a test.
  • Tests: seven new files (62 cases). Each of four ablations reds only its own pins. 283 existing touched-module files are green, 69/69 console Studio route tests pass, and the i18n key/drift/dead-key checks pass (dev report 6555825150).
  • Out-of-scope findings: filed Setup › Metadata: an item history page's "Back to item" lands on a type list named after the item, not the item #28626, Setup's history "Back to item" resolving .. by route. Carried to N-28 in the card's parent: Setup → Studio redirects must exempt datasource, or the two redirects loop.
  • Landing: ordinary queue landing, pr_ready then automerge_enable. Fixes #28561 closes the card on merge. Serial neighbour Studio: the landing card of each installed base says "n customized, m switched off", read from the per-package customization read #28566 merges main after this lands.

Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y

席位核对 · ACCEPT (head moved) · 2026-11-01T11:14Z

Head-sha: 5c4f96bbee56304a0d393e20be33bd5ab4d56156


Generated by Claude Code

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

1 participant