Skip to content

fix(app-shell): Studio and Setup read an item's layered view at the package that owns it - #28825

Merged
objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/project-thread-0h2wze
Nov 1, 2026
Merged

objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/project-thread-0h2wze

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #28584

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:Studio 与 Setup 里读条目分层视图(GET /meta/:type/:name/layers)的调用,现在都带上调用方本来就知道的所属包(?package=):Studio 各柱所在的包、权限矩阵的包门(packageId)、或编辑页 URL 上的 ?package=。实在不知道所属包时仍不带包,服务端的 409 拒绝原句作为加载错误显示;权限矩阵不再把这个 409 当成「没有这个权限集」。客户端不猜持有方。
  • 为什么改:meta: with two package holders of one dashboard name, GET /meta/dashboard/X/layers silently returns one holder's overlay while package-less save and delete answer 409 #28337(PR fix(meta)!: the layered view naming no package refuses a name two packages hold, as the package-less writes do #28583)之后,同名条目被两个包持有时,不带包的分层读取会被 409 RESOURCE_CONFLICT 拒绝。Studio 的列表视图检查器、Interfaces 的 app 与叶子、Data 的对象、Automations 的流程、包 OWD 总览、权限矩阵、嵌入条目编辑器,以及条目页的重置/发布/丢弃后的重读,都还在不带包地读,在这种部署上会加载失败或误判。
  • 风险与回滚:只改 ui/packages/app-shell 的 6 个源文件(只是给已有的读取补第三个参数,外加权限矩阵对 409 的一处改判)和测试;服务端与 spec 零改动。只读基座上带基座包读,服务端按 ADR-0146 D8 返回其声明的 .custom 覆盖。回滚 = revert 本 PR。
  • 先看:ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx 的 layeredPackageId 与 readLayered 的 .catch(唯一改变错误处理的地方);再看 views/studio-design/ObjectListViewInspector.tsx 的 layeredAt。
  • 两分钟验证:cd ui && pnpm exec vitest run packages/app-shell/src/views/studio-design/ObjectListViewInspector.twoHolders-28584.test.tsx packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.twoHolders-28584.test.tsx packages/app-shell/src/views/studio-design/StudioDesignSurface.twoHolders-28584.test.tsx packages/app-shell/src/views/studio-design/StudioDesignSurface.oneSurface-28572.test.tsx,应 4 个文件 20 个用例全部通过。
  • 需要你做的:无,席位落地。

What changes

Each package-less client.layered(...) caller in ui/packages/app-shell now names the package it already reads and saves at:

  • List-view inspector (useObjectListViewDraft): the published layer, and the copilot-marks read beside a draft, at the hook's packageId. On a read-only base that is the base, whose read serves its declared override (ADR-0146 D8, servedOverlayRowCandidates' servedOverride).
  • Interfaces pillar: the app's baseline at packageId. The leaf's baseline, and the dashboard's published-layer read for served titles, at the leaf's owner: current.addedIn for an item the customization pair adds (Studio: a base and its <base>.custom as one surface — "customized" / "added" marks, "only customizations", the pair folded under its base, New lands in the pair #28572), else packageId (the effect now depends on leafPackageId).
  • Data pillar: the object's layers at the item-level packageId (current.addedIn ?? railPackageId since Studio: a base and its <base>.custom as one surface — "customized" / "added" marks, "only customizations", the pair folded under its base, New lands in the pair #28572). Automations pillar: the flow's at draftPackageId, and none when the pillar has no package.
  • Package OWD overview: readObjectBodies takes the panel's packageId.
  • Permission matrix: layeredPackageId is the door's packageId, else the edit URL's ?package= (all, the list's every-package scope, is not a package). The load, the package door's pre-save re-read and the environment door's post-save re-read all read there. With no owner known, a 409 RESOURCE_CONFLICT from the load's read is rethrown to the load's error strip, which shows the server's sentence, instead of being caught to null ("no set known").
  • Embedded item editor: the parent's layers at the hosting edit page's ?package= (read when the save runs, as that page's own save binding is).
  • Item page (ResourceEditPage): the load already passed ownerPackageId; the reset, publish and discard re-reads passed only the member, and now pass the same scope as the load.

Unchanged: StudioItemTools (every mount passes the pillar's packageId) and StudioCustomizeControls (reads at the base) already named a package. usePublishAllDrafts' holdsNoPublishedLayer (new on main, #28783) reads only drafts that carry no package, so no owner is known; it already takes any refusal as "holds a layer", never as "no artifact", and writes no navigation entry for it. Where no owner is known (an item page with no ?package=, an Automations pillar with no package, the embedded editor with none), the read still names none and every one of those callers already shows a thrown refusal's message as its error.

Tests

  • ObjectListViewInspector.twoHolders-28584.test.tsx (new, 2): the real hook over a real MetadataClient, a server holding the view in two packages (the holder's stack with ?package=, the 409 without). Open in each package: the one layered read names it, nothing is refused, the buffer holds that holder's view.
  • PermissionMatrixEditor.twoHolders-28584.test.tsx (new, 3): the package door reads its own package's stack and shows its grants; the routed page reads the URL's owner and shows that holder's grants; with ?package=all the read names no package and the server's refusal sentence is shown, with no matrix rows.
  • StudioDesignSurface.twoHolders-28584.test.tsx (new, 3): Interfaces (app and dashboard leaf, this package's nav), Automations (flow) and Data (object), each read at this package, none refused.
  • StudioDesignSurface.oneSurface-28572.test.tsx (+2): on a read-only base, the pair's own object (Data) and dashboard (Interfaces) read their layers at the pair.
  • StudioDesignSurface.railLabels.test.tsx, StudioDesignSurface.navDeepLink-11153.test.tsx: the exact-argument assertion on the app's layered read now includes { packageId: PKG }.
Reverse verification

Acceptance notes

Verification

  • The three new files plus oneSurface-28572: 20 of 20 passed (--maxWorkers=2, under scripts/pm/os-verify-lock.sh).
  • Every app-shell and console test file naming a touched component (ObjectListViewInspector, PackageOwdOverviewPanel, PermissionMatrixEditor, EmbeddedItemEditor, MetadataDetailDrawer, ResourceEditPage, the three pillars, StudioDesignSurface, the metadata-admin index), in locked batches of 60: at 57d352cc (main merged at c064d3a2; after pnpm install and a rebuild of the console closure) 366 files, 2600 tests passed; at 1edfa495 (main at 8afaf12d, which carries feat(studio): a read-only base and its customization pair as one surface #28747) 365 files, 2595; at 99045462 (main at 706ac52d) 364 files, 2583; at 797eb378, before any merge, 363 files, 2571. CI's Test UI runs the rest of app-shell.
  • turbo run type-check --filter=@object-ui/app-shell: exit 0 at 57d352cc, 1edfa495, 99045462 (65 tasks) and 797eb378.
  • ESLint on the 11 changed ts/tsx files: 0 errors (the warnings are the files' existing react-hooks advisories).
  • Derived gates (node scripts/pm/dispatch-gates.mjs --commands): 34 of 34 run, each exit 0 (check:type-check-debt under the lock), including node scripts/check-sdui-manifest.mjs and pnpm --dir ui exec vitest run scripts/; --ran reconciles 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. Run at 57d352cc (and earlier at 1edfa495 and 99045462, each 34 of 34).

🤖 Generated with Claude Code

https://claude.ai/code/session_01QsR2jQi39yrLXvXVSvoKfv

…wning package

A layered read naming no package is refused 409 RESOURCE_CONFLICT for a
name two packages hold. Each caller now names the package it already reads
and saves at; where none is known, the server's refusal is the load's error.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jQi39yrLXvXVSvoKfv
The Interfaces leaf baseline and the dashboard's published-layer read now
name the leaf's owner: the pair for an item it adds, else this package.
A test pins both pillars reading a pair-added item at the pair.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jQi39yrLXvXVSvoKfv
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tooling labels Nov 1, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu

席位核对 · ACCEPT · 2026-11-01T22:22Z

Head-sha: 57d352cca66ce443d5a464f902588e7aa842103c

  • Shape: draft → main, first content line Fixes #28584, no other card beside a closing keyword. 13 files, +699 / −20 (719 lines ≤ 3000), no governed path (check-governed-merges --pr 28825: NOT governed). Clause-②: no / Metadata-protocol: none line-initial in body and changeset, as claimed (6561208902).
  • Read in the diff: every package-less client.layered(...) caller in ui/packages/app-shell that knows its owner from its row or route now passes it (15 sites in 6 files, enumerated in the report); where no owner is known (Setup's permission matrix) the server's 409 refusal sentence is shown instead of reading the 409 as "no artifact". No client-side guess of the holder. After merging feat(studio): a read-only base and its customization pair as one surface #28747, an Interfaces leaf added by the pair reads at current.addedIn, pinned by two oneSurface-28572 tests.
  • Gates (check-runs on this head): 44 success, 11 skipped, 0 failed or pending. mergeable_state clean. Test UI (11/16) first failed on scripts/__tests__/check-vi-mock-specifiers.test.ts timing out at 15 s (a repo-wide scan this diff does not touch, 25 s under shard load); one re-run of the failed job went green.
  • Tests (dev): the two-holder case pinned (8/8, red on main's sources, rethrow ablation red); 366 files / 2600 tests around the touched modules; type-check app-shell 0; derived gates 34/34 exit 0.
  • Acceptance notes: usePublishAllDrafts.holdsNoPublishedLayer (added by perf(console): write app navigation at page and dashboard create and delete sites #28783) stays package-less: no owner is known there and it already treats any refusal as "holds a layer". The OWD panel's getDraft and EmbeddedItemEditor's draft read/save are package-less but are not layered reads and the server refuses the writes with a 409; noted, no reach measured, not filed.
  • Out of scope / NOT MEASURED: no live Console run by the seat. Skills-docs: not owed.
  • Landing: ordinary queue landing, pr_ready then automerge_enable. Fixes #28584 closes the card on merge.

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/l tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Studio: the layered-view callers pass the owning package, since a package-less /layers read of a name two packages hold now answers 409 (#28337)

2 participants