Repository navigation
fix(console): open the record page scoped to the current app (PageSchema.apps) - #29631
Conversation
usePageAssignment now reads its app option (the app's name) and opens the record page whose PageSchema.apps lists that app before the object's default record page. RecordDetailView resolves the route's app segment to its app with matchAppBySegment and passes the app's name, never the raw segment; the record drawer reads the same host-route segment. The appObjectScope record-page picker asks the same order, so a record route reads the objects of the page it renders. With no apps anywhere, resolution is unchanged. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jTtndxBXWfjH1SiQ6w
…page The console now opens the record page an app scopes, so the apps describe drops its planned marker and its liveness row reads live, citing the console reader. The form ledger keeps its omit row with the reason it is not offered in the page form yet. Tests pin the hook, the record view (route and drawer, package-id and name segments, two apps in one package) and the record route's object scope agreeing with the hook. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jTtndxBXWfjH1SiQ6w
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jTtndxBXWfjH1SiQ6w
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jTtndxBXWfjH1SiQ6w
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 168 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 cc84837e5bdea3f9516053b7960cd7bff48e9d86 && git checkout cc84837e5bdea3f9516053b7960cd7bff48e9d86
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 4d8095ba90d8855f5b1e012d378b9e48b3a5668e 5b25775652498d9ce56104e498a290e824b32908 && git checkout -B drift-repro 4d8095ba90d8855f5b1e012d378b9e48b3a5668e && git merge --no-ff 5b25775652498d9ce56104e498a290e824b32908
node scripts/docs-audit/affected-docs.mjs --json 4d8095ba90d8855f5b1e012d378b9e48b3a5668e
|
|
domain:ui#2 · zhuangjianguo · session_01QsR2jRFuJz9QBZqPVHYVhJ 席位核对 — ACCEPT ·
|
Requested by Steedos · project thread
Fixes #29592
Clause-②: no
Metadata-protocol: none
维护者速读
apps里列了这个应用,就在该应用里打开它;其它地方(含 Setup)照旧打开对象唯一的默认记录页。记录抽屉同样生效。page.apps的活性登记从 planned 改为 live,描述去掉 PLANNED 标记。sys_userrecord page the one a person opens to: on 18.x the console opens plugin-auth'ssys_user_detail, and no declarable route reaches a second record page #28979 裁决 A 的 ui 半。规格半(PR feat(spec): a record page can name the apps whose routes open it (PageSchema.apps) #29437)已合入,但控制台没读apps,所以 Duly 这类应用在自己的路由里打开用户记录,仍然看到平台的sys_user_detail。apps时行为不变(测试有对照组,现有 291 个相关测试文件全绿)。路由段先解析成应用再比对应用名,不拿包 id 去比。回滚即 revert 本 PR。ui/packages/react/src/hooks/usePageAssignment.ts(选页顺序:应用页、默认页、声明顺序)、ui/packages/app-shell/src/views/RecordDetailView.tsx(路由段解析为应用名)、ui/packages/app-shell/src/console/appObjectScope.ts(declaredRecordPage与渲染选同一张页)。pnpm --dir ui exec vitest run packages/app-shell/src/views/RecordDetailView.appScopedPage-29592.test.tsx,应见 6 个用例全绿:Duly 应用里打开duly_member,Setup 里仍是sys_user_detail。What changes
Ruling A on #28979 lets a record page name the apps whose routes open it (
PageSchema.apps); the spec half landed in #29437. This is the console half.usePageAssignment(@object-ui/react) now reads its existingappoption, the owning app's NAME. Order: the first record page of the object for whichdefaultRecordPageObject(page, app)answers the object, then theisDefaultpage, then declaration order. The option's type is unchanged; it was declared "reserved for future use" and read by nothing, and its only caller never passed it. No new parameter, soClause-②: no(seat ruling).RecordDetailViewresolves the route's:appNamesegment withmatchAppBySegmentover the full metadata app list and passesapp.name, never the raw segment (contract-review flag ③.2 on feat(spec): a record page can name the apps whose routes open it (PageSchema.apps) #29437: the segment isapp._packageId ?? app.name). The record drawer, mounted under the host list route, reads the same segment, so a record opened in the drawer inside an app gets that app's page too. A package that ships two apps resolves its package-id segment to the first app, so the second app's scoped page opens under its name segment only, as theappAddressSegmentdocblock states.appObjectScope.tsdeclaredRecordPage(the record route's object scope, objectui#13046) asks the same order with the appAppContentalready resolved, so the route reads the objects of the page it renders. Only this picker was touched; the packaged-lookup and package-less arms are untouched.page.appsliveness rowplanned→live, citing the three console read points; the describe drops[PLANNED — not yet rendered]and the closing "Until the console reads it" sentence, and the TSDoc names the renderer. Forced by that: the describe pin instack-app-record-page.test.tsnow pins the marker's absence;liveness/state-counts/page.mdandcontent/docs/references/ui/page.mdxare regenerated. Thepage.appsomit row inmetadata-form-zod-reconciliation.test.tsstays, with a rewritten reason: live, but not offered in the Studio page form because no registered widget spells a list of app names (seat ruling; whether the form offer becomes a follow-up card is the seat's call at ACCEPT).With no
appson any page, every branch falls through to the previous line.Tests
ui/packages/react/src/hooks/__tests__/usePageAssignment.appScope-29592.test.tsx(10): the scoped page opens in its app whatever the served order; Setup and no-app open the default; a page listing several apps; declaration-order fallthrough; another object's scoped page ignored; scoped slotted page hands back slots;pageNamestill wins; CONTROL with noapps.ui/packages/app-shell/src/views/RecordDetailView.appScopedPage-29592.test.tsx(6): the ruling's pin (Duly undercom.dulyand underdulyopensduly_member;/apps/com.objectstack.setup/...openssys_user_detail), the two-apps-one-package corner, the drawer, and the CONTROL over five segments.ui/packages/app-shell/src/console/__tests__/appObjectScope.appScopedRecordPage-29592.test.tsx(3): the scope reads the scoped page's objects in Duly and the default's in Setup; the scope picker andusePageAssignmentagree across apps and orders; CONTROL.usePageAssignment.isDefault-12195,.typeDiscriminator-9674,.declarationOrder-7298,.isRecordPageOf-12715.Ablations (each mutated through scripts/ablation-replace.mjs, restored to HEAD and verified)
matchAppBySegment(metadataApps, appName)?.name→ the segment): record-view suite 3 failed / 3 passed (package-id segment, two-apps corner, drawer red; the name segment stays green, as expected).if (opts.app && false)): 8 failed / 11 passed across the three new files.Sweep
Every ui test file naming
RecordDetailView,appObjectScope,usePageAssignmentorreadAppObjectScope(291 files,git grep -lat 8fead84), three locked batches at 2 workers: 98/98 files 788 tests, 98/98 files 654 tests, 95/95 files 847 tests. Spec:src/stack-app-record-page.test.ts+src/system/metadata-form-zod-reconciliation.test.ts103/103.check:livenessandcheck:generatedgreen after regeneration.Acceptance notes
--freshbackend, Vite console on this branch's source, 1440×900): a record pagecrm_memberforsys_userwithapps: ['crm_app']was saved throughPUT /api/v1/meta/page/crm_member(200). Under/apps/com.example.crm/sys_user/record/IDit openscrm_member;/apps/crm_app/...canonicalises to the package-id segment and opens it too;/apps/com.objectstack.setup/sys_user/record/IDopenssys_user_detail. Before leg (the three ui files restored fromorigin/mainin the same running console): the CRM route openedsys_user_detail. Screenshots are in the project files underissue-29592/(before-crm-app-user-record.png,after-crm-app-user-record.png,after-setup-users-record.png); the seat links them.RecordDetailViewmounted withembeddedunder the list route (/apps/:appName/:objectName).apps(no app-name widget); see the reconciliation ledger row.Verification
Local,
dispatch-gates --commands --repo objectstack-ai/objectstackderived 107 families on this diff: all 107 run at0275f773, every one exit 0,--ranreconciliation107 run, 0 NOT-MEASURED, 0 UNRUN(the uivitest run scripts/suite andcheck:type-check-debtunder the verify lock: 170 files / 4972 tests green; debt exit 0).dispatch-gates --tier: no path-derived mandate (page.zod.tsis a Clause-② suspect hint only; the change there is describe text).After merging
main(5b257756, which brought #29552'sappObjectScope.tsarm in cleanly): spec rebuilt,check:generatedandcheck:livenessexit 0; the 16 ui test files namingappObjectScope/readAppObjectScope/usePageAssignmentplus the record-view file and #29552's new test, 18 files / 113 tests green; the two spec files 103/103; ratchets rerun on the new head:check:type-check-debt,check-changeset-no-major,check-empty-changeset,check:nul-bytesexit 0. Type-check (@object-ui/react,@object-ui/app-shell,@objectstack/spec) exit 0 and eslint 0 errors on the touched ui files, both at0275f773. CI check-runs: see this PR.Generated by Claude Code