Skip to content

[移交自 objectui · v17 阻塞] app.homePageId 退役墓碑写着「no shell ever read it」,但 objectui 的 AppContent 正在读它 #4865

Description

@xuyushun441-sys

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 处,届时一并改)

  1. packages/app-shell/src/console/AppContent.tsxresolveLandingRoute()homePageId 分支
  2. packages/types/src/app.ts:415homePageId?: string
  3. packages/types/src/__tests__/page-app-dashboard-spec-parity.test.ts:139 — 期望键清单
  4. apps/console/src/preview-samples.ts:86 — 样例(objectui#3266 刚按当时的 spec 加上)
  5. apps/console/src/__tests__/preview-samples-spec-valid.test.ts:162 — 其提示文案指向 homePageId,届时会指向一个同样已退役的键
  6. objectui#3275 / PR fix(cli): dispatch OS_DATABASE_DRIVER=memory to the mingo InMemoryDriver (#3276) #3285AppPreview 新增的 homePageId 读取
  7. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions