Part of objectstack-ai/objectui#3287
按 /pm-dispatch 跨分片移交协议从 objectui 分片转来:共享契约面(packages/spec 的退役裁决)单一 owner 是主 backlog PM ,objectui 分片 PM 不自行处置。
结论先行:#4667 的墓碑有一处事实性错误
packages/spec 在 #4667 / #4680 (ADR-0049 enforce-or-remove)把 app.homePageId 退役为 retiredKey() 墓碑,文案是:
app.homePageId was removed in @objectstack/spec 17.0.0 (#4667 , ADR-0049) — no shell ever read it . An app's landing page IS its first navigation item (by order), and the root landing follows isDefault routing.
「no shell ever read it」不成立。 objectui —— 官方渲染器 —— 一直在读它:
packages/app-shell/src/console/AppContent.tsx:870-885,resolveLandingRoute():
function resolveLandingRoute ( activeApp : any , ctx ?: NavTemplateContext ) : string {
const homePageId : string | undefined = activeApp ?. homePageId ;
const navigation = activeApp ?. navigation || [ ] ;
if ( homePageId ) {
const item = findNavItemById ( navigation , homePageId ) ;
const route = buildItemRoute ( item , ctx ) ;
if ( route ) return route ;
}
return findFirstRoute ( navigation , ctx ) ;
}
而且该函数的注释专门解释了它为什么存在:
Honors the app's explicit homePageId (Salesforce-style "Default Landing"); falls back to the first reachable nav item only when no homePageId is set or it points at something that doesn't yield a route. This is what lets the CRM example open on the Sales Dashboard instead of the Lead list.
为什么这不只是文案问题
墓碑的替代方案(「落地页就是 navigation 的第一项,按 order」)与被退役的键语义不同 ,不是同义改写:
退役前
退役后
显式钉了 homePageId 的 app
落在被指定的那一项
落在 nav 第一项
也就是说,退役会让所有显式设置过落地页的 app 静默改变落地位置 。CRM 示例原本落在 Sales Dashboard,退役后落在 Lead 列表。这不是"删掉一个没人用的键",是"移除一个有真实消费者、且行为可观测的能力"。
大概率是跨仓 liveness 审计的盲区
homePageId 的消费者不在 objectstack 仓内 ,而在 objectui 的 shell 里。仓内 grep 会得出「没有消费者」,这与我们在 objectui#3226 上刚遇到的形态是同一族:
当一个键的消费者在仓外时,仓内 grep 不构成「没有消费者」的证据。
建议核查 #4667 的 liveness 判定是如何 得出「no shell ever read it」的 —— 如果它只扫了 objectstack 仓,那么同一批退役里的其它键也可能有同样的盲区 ,这比单个键的去留更值得先查一遍。
需要拍板的
A. 裁决正确,objectui 跟随删除。 落地语义统一为「nav 第一项 + 根落地看 isDefault」,作者改落地页靠重排 navigation。需要 os migrate meta --from 16 覆盖这次静默行为变化,并且墓碑文案应改掉「no shell ever read it」这句(它是错的,会误导后来者)。
B. 前提有误,该键不该退役 (或改为标准弃用周期而非直接移除)。若「显式指定落地页」是想要的能力,objectui 已经实现了它。
我的倾向是先确认事实再定去留 —— 无论最终选 A 还是 B,墓碑上那句断言都需要修正,因为它会让后来者以为这次移除没有消费者。
objectui 侧的影响面(A 路线下,7 处,届时一并改)
packages/app-shell/src/console/AppContent.tsx — resolveLandingRoute() 的 homePageId 分支
packages/types/src/app.ts:415 — homePageId?: string
packages/types/src/__tests__/page-app-dashboard-spec-parity.test.ts:139 — 期望键清单
apps/console/src/preview-samples.ts:86 — 样例(objectui#3266 刚按当时的 spec 加上)
apps/console/src/__tests__/preview-samples-spec-valid.test.ts:162 — 其提示文案指向 homePageId,届时会指向一个同样已退役的键
objectui#3275 / PR fix(cli): dispatch OS_DATABASE_DRIVER=memory to the mingo InMemoryDriver (#3276) #3285 中 AppPreview 新增的 homePageId 读取
apps/console/src/components/RootLandingRedirect.tsx 的注释
⚠️ 在本单有结论前,objectui 侧不会单独动其中任何一处 —— 半改会造成预览、类型、运行时落地路由三者不一致。
版本现状(实测)
objectui pin ^17.0.0-rc.1,lockfile 解析到 17.0.0-rc.1,也是 npm 上最新已发布版本;
该已发布构建里 homePageId 仍是合法可选键 (dist/app.zod-*.d.ts:764;ObjectStackSchema.safeParse() 带该键 PASS);
退役只存在于 objectstack main,尚未发布。
所以今天 objectui 读它是正确的,问题只在升级那一刻爆发。
关联:objectui#3287、objectui#3275 / PR #3285 、objectui#3266、#4001 (移除 landing,当时的替代正是 homePageId)、#4667 / #4680 、ADR-0049
Part of objectstack-ai/objectui#3287
按
/pm-dispatch跨分片移交协议从 objectui 分片转来:共享契约面(packages/spec的退役裁决)单一 owner 是主 backlog PM,objectui 分片 PM 不自行处置。session_01NVPjPzmmAJ2Ngtvgg5MSRa(账号xuyushun441-sys,已在 [PM] 分片分工登记表 — 谁在管哪个仓库的队列 #4604 登记)app.homePageId,但 objectui 的AppContent正在读它 —— 升级前需要拍板 objectui#3287(已标needs-user-decision,在本单有结论前 objectui 侧不动任何一处)@objectstack/spec17.0.0 正式版的前置阻塞项结论先行:#4667 的墓碑有一处事实性错误
packages/spec在 #4667 / #4680(ADR-0049 enforce-or-remove)把app.homePageId退役为retiredKey()墓碑,文案是:「no shell ever read it」不成立。 objectui —— 官方渲染器 —— 一直在读它:
packages/app-shell/src/console/AppContent.tsx:870-885,resolveLandingRoute():而且该函数的注释专门解释了它为什么存在:
为什么这不只是文案问题
墓碑的替代方案(「落地页就是
navigation的第一项,按order」)与被退役的键语义不同,不是同义改写:homePageId的 app也就是说,退役会让所有显式设置过落地页的 app 静默改变落地位置。CRM 示例原本落在 Sales Dashboard,退役后落在 Lead 列表。这不是"删掉一个没人用的键",是"移除一个有真实消费者、且行为可观测的能力"。
大概率是跨仓 liveness 审计的盲区
homePageId的消费者不在 objectstack 仓内,而在 objectui 的 shell 里。仓内 grep 会得出「没有消费者」,这与我们在 objectui#3226 上刚遇到的形态是同一族:建议核查 #4667 的 liveness 判定是如何得出「no shell ever read it」的 —— 如果它只扫了 objectstack 仓,那么同一批退役里的其它键也可能有同样的盲区,这比单个键的去留更值得先查一遍。
需要拍板的
A. 裁决正确,objectui 跟随删除。 落地语义统一为「nav 第一项 + 根落地看
isDefault」,作者改落地页靠重排navigation。需要os migrate meta --from 16覆盖这次静默行为变化,并且墓碑文案应改掉「no shell ever read it」这句(它是错的,会误导后来者)。B. 前提有误,该键不该退役(或改为标准弃用周期而非直接移除)。若「显式指定落地页」是想要的能力,objectui 已经实现了它。
我的倾向是先确认事实再定去留 —— 无论最终选 A 还是 B,墓碑上那句断言都需要修正,因为它会让后来者以为这次移除没有消费者。
objectui 侧的影响面(A 路线下,7 处,届时一并改)
packages/app-shell/src/console/AppContent.tsx—resolveLandingRoute()的homePageId分支packages/types/src/app.ts:415—homePageId?: stringpackages/types/src/__tests__/page-app-dashboard-spec-parity.test.ts:139— 期望键清单apps/console/src/preview-samples.ts:86— 样例(objectui#3266 刚按当时的 spec 加上)apps/console/src/__tests__/preview-samples-spec-valid.test.ts:162— 其提示文案指向homePageId,届时会指向一个同样已退役的键AppPreview新增的homePageId读取apps/console/src/components/RootLandingRedirect.tsx的注释版本现状(实测)
^17.0.0-rc.1,lockfile 解析到17.0.0-rc.1,也是 npm 上最新已发布版本;homePageId仍是合法可选键(dist/app.zod-*.d.ts:764;ObjectStackSchema.safeParse()带该键 PASS);main,尚未发布。所以今天 objectui 读它是正确的,问题只在升级那一刻爆发。
关联:objectui#3287、objectui#3275 / PR #3285、objectui#3266、#4001(移除
landing,当时的替代正是homePageId)、#4667 / #4680、ADR-0049