Repository navigation
fix(studio): object pickers offer only the objects the edited package may reference - #28329
Conversation
…h gate resolvePackageClosure is the walk resolveWritePackageScope ran inline, moved into @objectstack/lint unchanged so Studio's object pickers can call the same function over the served package list. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvSmNNn2cV2emnvGXn
Objects outside the package's closure are listed last, disabled, with the reason and a door to declare the dependency. The closure is the publish gate's own, computed by the shared lint functions. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvSmNNn2cV2emnvGXn
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvSmNNn2cV2emnvGXn
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibvSmNNn2cV2emnvGXn
📓 Docs Drift CheckThis PR changes 2 package(s): 20 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 14 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 876c8d5a51d854cb05255973c30a15520ae70af5 && git checkout 876c8d5a51d854cb05255973c30a15520ae70af5
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 5055b102d40bde2873f29ee7385fad219e2e8e20 3c5e468cf21923d3f4f6cc2be346c8b82007f967 && git checkout -B drift-repro 5055b102d40bde2873f29ee7385fad219e2e8e20 && git merge --no-ff 3c5e468cf21923d3f4f6cc2be346c8b82007f967
node scripts/docs-audit/affected-docs.mjs --json 5055b102d40bde2873f29ee7385fad219e2e8e20
|
|
domain:ui#3 · os-tesla · session_01L9qibz8HEj53Wx2uphTo2o 维护者速读
席位意见:选择器和发布闸门共用同一个遍历函数,两边不会再对不上;手动输入仍保留,由服务端判定。可以直接落地。 席位核对ACCEPT · seat
|
domain:ui#3 · os-tesla · session_01L9qibvSmNNn2cV2emnvGXn
Requested by Tesla · project thread
Fixes #28240
Clause-②: no
Metadata-protocol: none
维护者速读
@objectstack/lint的resolvePackageClosure,服务端改为调用它,接纳范围不变(服务端原有闭包测试未改、全绿)。软件包列表读不到时不做任何收窄,由服务端判定,与闸门的失败方向一致。回滚即 revert 本 PR。ui/packages/app-shell/src/views/metadata-admin/inspectors/objectPickerClosure.ts、ObjectPicker.tsx的分组与禁用逻辑、packages/metadata-protocol/src/protocol-write-package-scope.ts。pnpm --dir ui exec vitest run packages/app-shell/src/views/metadata-admin/inspectors/ObjectPicker.closure-28240.test.tsx,预期 6/6 通过。What changes
@objectstack/lint: new exportresolvePackageClosure(packageId, getPackage), on both the root and the./runtimeentry. It is the dependency walkresolveWritePackageScoperan inline, moved verbatim: same guards (no id, thesys_metadatasentinel, an unknown package answerundefined), same DFS, a declared-but-unresolvable dependency still in the closure, own id skipped. A throwinggetPackagepropagates; the host decides.@objectstack/metadata-protocol:resolveWritePackageScopekeeps its two host guards (registry withoutgetPackage, the silentcatch) and calls the shared walk. What the gate admits is unchanged.ObjectPicker.tsx, shared by the flow New dialog's record presets, every flowobjectreference, a lookup's related object and the new-dataset dialog): when a package is being edited, it readsGET /api/v1/packages(the requestpackages-io.tsalready shares) and lazily imports@objectstack/lint, then callsresolvePackageClosureandnarrowObjectsToPackageClosureover the served object list. Neither rule is restated in ui.aria-disabledwith "From PACKAGE". A search reaches them without opening the group.PackageFormDialogmodeedit). The picker re-reads the closure onobjectui:packages-changed, so a declared dependency's objects become pickable without a reload.engine.inspector.objectPicker.*keys, one contiguous block in each studio pack (en, zh)..changeset/28240-studio-object-picker-closure.md(patch: app-shell, i18n, lint, metadata-protocol).Presentation choice (card item 1, "absent or disabled"): disabled with the reason and the declare door, as the seat ruled. A missing object reads as "the platform has no such object"; a disabled one tells the author what to do.
Tests
packages/lint/src/runtime-gate.resolve-package-closure.test.ts(new): the guards, own-only closure, transitive walk through a cycle, an unresolvable declared dependency kept, a throwing registry propagating, and that the narrow./runtimeentry names the function.ui/.../inspectors/ObjectPicker.closure-28240.test.tsx(new), with the real lint functions and only the two reads doubled:showcase_task; searching finds it disabled, with the reason, and clicking it commits nothing;com.example.showcasedeclared,showcase_taskis offered and commits;ObjectPicker.test.tsx: a module-scopefetchdouble forGET /api/v1/packages(empty list, so nothing narrows and its expectations are unchanged). The picker now reads that list on mount, and the network-escape guard reds a real socket.Readings
Verification
Head
3c5e468c(branch merged withorigin/main, full rebuildpnpm turbo run build --filter='!@objectstack/docs'115/115).node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives 64 families; each was run with</dev/nulland every one exits 0. Reconciliation--ran:64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN.node scripts/check-sdui-manifest.mjsis among the 64 (exit 0).ui check:new-line-citations: 0 new.pnpm --dir ui exec vitest run scripts/(pre-merge head): 169 files, 4901 tests passed.Live measure (card item 2, and the picker)
Own stack: showcase backend
objectstack dev --seed-admin --fresh -p 41873; the Console production build (vite build) served byvite preview --port 51874proxied to it. (The Vite dev server cannot load@objectstack/lintin the browser, see Acceptance notes, so the picker was measured on the production bundle.)Item 2, the dependencies editor is reachable from Studio, no wiring needed:
package-form-dependenciesvisible before Advanced =false, after expanding Advanced =true. It sits in the collapsed Advanced section, which is likely why the walkthrough missed it.package-form-dependenciesvisible =true, one row,com.example.showcase(declared in the step below).Item 1, the card's repro on
com.default.acme-custom(no dependencies), Automations, New, When a record is created, Object:sys_inbox_message,sys_file,sys_upload_session(platform objects); toggle "Show objects outside this package's dependencies (24)";task:showcase_taskaria-disabled="true", "From com.example.showcase", reason row "Acme Custom can use only its own objects, …"; clicking it left the input at the typedtask(nothing committed);com.example.showcase/*and saving closed it; the stored manifest reads{"com.example.showcase":"*"}, and the same picker then offeredshowcase_taskenabled.Acceptance notes
@objectstack/lintin the browser (measured on this branch:Module "module" has been externalized for browser compatibility. Cannot access "module.createRequire" in client code). The lint graph importsnode:modulein six rule files. In dev the picker therefore narrows nothing and the server judges, the documented failure direction. The production bundle loads it (measured above). The existing in-browser lint callers (preview/capabilityLint.ts,preview/securityPostureLint.ts,metadata-admin/celAuthoring.ts) take the same dynamic import, so they degrade the same way in dev. Not changed here; Refs dev: the Vite dev Console cannot import @objectstack/lint in the browser (node:module createRequire named import), so every in-browser lint caller silently degrades to no lint underpnpm dev#28333.ObjectPicker.tsxand the newobjectPickerClosure.tslive inviews/metadata-admin/inspectors/(the shared picker every Studio object picker uses), andpackages-io.tsgains a manifest reader.StudioDesignSurface.tsxis not touched.packages/lintandprotocol-write-package-scope.tsper the seat's claim amendment 6538025413.🤖 Generated with Claude Code
https://claude.ai/code/session_01L9qibvSmNNn2cV2emnvGXn
Generated by Claude Code