feat(deps): track @objectstack/spec 17.0.0-rc.2 and fix every break it lands (#3235, #3208, #3287, #3264) - #3315
Merged
Conversation
…t lands Bumps the workspace pin from ^17.0.0-rc.1 to ^17.0.0-rc.2 (32 declarations across 30 package.json), plus the five sibling @objectstack/* declarations (client / formula / lint). Those siblings pin @objectstack/spec EXACTLY, so leaving them behind kept a second rc.1 copy of the spec in the tree and had @objectstack/lint — an app-shell dependency — validating against schemas that still accept the keys this change retires. pnpm-lock.yaml now resolves a single spec version and carries zero rc.1 references. Bumping the pin and fixing the fallout cannot be split: the pin alone reddens CI, the code alone targets a shape that is not in effect yet. Adjudicated retirements: - app.homePageId (#3287, #3264): resolveLandingRoute() collapses to findFirstRoute(navigation, ctx) and findNavItemById() goes with it, having lost its only caller. The function name and call site stay as the seam for a future navigation[].landing flag, which is the encoding upstream recorded as correct — not implemented here, it goes enforce-first. AppPreview now DERIVES the landing entry the same way the shell does, so the preview shows the entry the runtime will actually open. - AppContextSelector.includeAll / placement (#3208): removed from the hand written ContextSelectorDef and from the parity assertions. Neither ever did anything here — context selectors are mandatory-scope, so no "All" row was rendered, and placement: 'topbar' put nothing in the topbar. - HttpMethod (#3235): aliased to the spec's HttpMethodType from ./ui. Shape is verbatim identical and the public surface is unchanged. Deliberately NOT ./shared, which is the 7-value enum. Breaks the dispatch list did not cover, found by typecheck + build + tests: - NavigationArea.visible / order / requiredPermissions retired at area level. AppSchemaRenderer's area switcher DID gate on them; the gating moves down to NavigationRenderer, where the spec put it. Premise mismatch: objectui#3311. - ui's Notification / NotificationConfig deleted with no successor, taking @object-ui/core's NotificationProtocol with them. It had zero callers here; the live implementation is @object-ui/react's NotificationContext. Premise mismatch: objectui#3310. - MetadataSaveOptions moved ./kernel -> ./system. - SchemaDiffEntryKind gained default_mismatch; the exhaustive Record failed the build exactly as designed, and now carries a label. - ActionSchema gained the package-lock envelope; SPEC_ACTION_KEYS re-derived. Vacuous passes that rc.2 turned into real failures: - The email_template and view client validators pointed at schemas the metadata kinds do not resolve to, and the lenient container silently stripped every key it was handed — so neither type was ever really validated. email_template now uses EmailTemplateDefinitionSchema; view dispatches on its own discriminant so each shape is checked against its own schema (objectui#3312). - Four console preview samples were off-spec and being stripped rather than refused: view.list.object, job.concurrency / job.timeoutMs, email_template.from / .to, datasource.capabilities / .healthCheck. These are the drafts an author — or a model generating metadata — copies. - Test fixtures pinning managedBy: 'system', a value protocol 17 retired, had stopped asserting anything: it now resolves to the default-writable fallback. Switched to engine-owned. Also updates the mobile_fullscreen JSDoc to list RichTextField alongside TextAreaField (objectui#3301). #3173 stays out: rc.2's DashboardWidgetSchema.responsive is byte-identical to rc.1, so the documented `any` is untouched.
…D gate on the area keys The docblocks claimed no objectui renderer ever read area-level `visible` / `requiredPermissions`. That is false, and it repeats the exact mistake this round is documenting upstream: `AppSchemaRenderer`'s area switcher filtered areas by both. Following the retirement is still correct — the spec's area object is `.strict()`, so a v17-valid app cannot carry the keys and the filter was unreachable — but the reason has to be the true one. See objectui#3311.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
objectui#3275's changeset says AppPreview renders `homePageId` as a nav item id. This branch retires the key and makes the preview DERIVE the landing entry instead, so the two changesets would compile into one release note that contradicts itself — one half telling readers the preview shows `homePageId`, the other telling them it no longer exists. Rewrites that one sentence to the post-retirement fact and nothing else; the bump level and the rest of the file are untouched.
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
xuyushun441-sys
marked this pull request as ready for review
August 3, 2026 18:20
This was referenced Aug 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3235
Fixes #3208
Fixes #3287
Fixes #3264
把
@objectstack/spec从17.0.0-rc.1抬到17.0.0-rc.2,并修掉这次升级打出来的全部破坏。抬 pin 与修破坏不可分离提交(只抬 pin 则 CI 必红,只改代码则改的是尚未生效的形状),所以是一个 PR。1. 抬 pin
30 个
package.json、32 处声明从^17.0.0-rc.1改为^17.0.0-rc.2。外加 5 处兄弟包声明(
@objectstack/client/formula/lint,位于app-shell/core/data-objectstack/apps/console)—— 这几个包把@objectstack/spec钉的是精确版本("@objectstack/spec": "17.0.0-rc.1",不是^)。不一起抬的话,lockfile 会同时留下 rc.1 与 rc.2 两份 spec,而@objectstack/lint(app-shell的依赖)会继续拿 rc.1 的 schema 去校验 —— 那套 schema 仍然接受本 PR 正在退役的键。判据按工单要求以 lockfile 实际解析版本为准,不是区间字串:
2. 三张单的处置
#3235 ——
HttpMethod(选项 A)@object-ui/types公共表面不变,形状逐字相同,ApiDataSource等下游零改动。没有改成从./shared取 —— 那是 7 值枚举(含HEAD/OPTIONS),与本仓 5 值语义不符。这条理由写进了注释,防止后来者"顺手统一"。#3208 —— contextSelector 两键
rc.2 的
AppContextSelectorSchema键集实测为id/label/icon/optionsSource/allValue/persist——includeAll、placement确已移除。packages/types/src/__tests__/report-chart-query-spec-parity.test.ts—— 删两条断言(valueKey/labelKey/persist三条保留,仍是活的默认值)packages/app-shell/src/layout/ContextSelectors.tsx—— 手写ContextSelectorDef删这两个可选字段;:244那条解释"为什么无视 includeAll"的注释改写为"这两个键已不存在"#3287 / #3264 ——
app.homePageId退役(路径 A)落地语义统一为「nav 首项(按
order)+isDefault根落地」。resolveLandingRoute()收敛为一行findFirstRoute(navigation, ctx),函数名与调用点保留 —— 上游记录的未来正确形状是navigation[].landing布尔标记,保留函数是给那个留的接点(本轮不实现它,按 enforce-first)。随之失去唯一调用者的findNavItemById()一并删除(已复核全仓仅那一处调用)。清干净的位置:
AppContent.tsx(分支 +:840注释 + docblock 重写)、types/src/app.ts声明、types/src/zod/app.zod.ts注释、page-app-dashboard-spec-parity.test.ts期望键、AppPreview.tsx+AppPreview.test.tsx、apps/console的preview-samples.ts/preview-samples-spec-valid.test.ts/RootLandingRedirect.tsx/README.md。全仓 grep(含.json)零残留;只剩 CHANGELOG 等历史记录未动。AppPreview从"读homePageId并解析到某个 nav 项"改为派生落地项(首个可达导航项),与 shell 的resolveLandingRoute同构 —— 作者在预览里看到的落地页,就是运行时真正会去的那个。docblock 按 #3264 口径重写:去掉 Salesforce "Default Landing" 与 CRM Sales Dashboard 的承诺,理由用更正后的那一版(形状问题:ID 交叉引用无引用完整性,悬空时静默回退首项),不是旧的"没人读"。
page-app-dashboard-spec-parity.test.ts:139按 #3264 提醒「以该测试自身语义为准,别盲改」处理:homePageId在 rc.2 里仍是 shape 的一个键(retiredKey()墓碑),所以那条断言不会自动失败 —— 但继续把它列在"已声明的可写键"里会读成"仍可作者"。改为移出该列表,另起一条独立断言钉住墓碑会拒绝而不是 strip。2 行 JSDoc
packages/types/src/field-types.ts的mobile_fullscreen消费者补上RichTextField(objectui#3301 / PR #3302)。#3173 —— 本轮除名
已复核:rc.2 的
DashboardWidgetSchema.responsive仍是ResponsiveConfigSchema.optional(),与 rc.1 逐字相同。本仓写明原因的any未改动。3. 清单外的破坏(本轮最有价值的产出)
派单清单只覆盖"导出名从某入口消失"(5 处)。实测 typecheck + 全量测试 + build 另外打出 8 类,其中 3 类是静默通过变成响亮失败的既有缺陷:
NavigationAreaSchema的visible/order/requiredPermissionsAppSchemaRenderer.AreaSwitcher一直按它过滤区域 → objectui#3311ui的Notification/NotificationConfig@object-ui/core的NotificationProtocol是唯一消费者 → 删除(见下)MetadataSaveOptions./kernel→./system(名字与含义不变,仅入口移动)SchemaDiffEntryKind新增default_mismatchValidationPanel的穷尽Record按设计编译失败,补标签即可 —— 机制按预期工作EmailTemplateSchema移除email_template校验器一直指错 schema → 改指EmailTemplateDefinitionSchema(objectstack#4616 / #4807)view校验器指着 container 而非 ViewItemActionSchema新增 package-lock 信封 7 键SPEC_ACTION_KEYS清单重新派生另有
managedBy: 'system'这个 protocol 17 已退役的值散落在 6 个测试文件里。rc.2 的resolveCrudAffordances对它走"未知桶 → 默认可写"分支,于是一批"保持锁定"的断言不再锁定任何东西。改用engine-owned(真正锁定的桶)后断言恢复承重。静默通过 → 响亮失败的三处
console 样例草稿(
apps/console/src/preview-samples.ts)—— 这些是作者(以及生成元数据的模型)直接复制的"可用示例",此前靠非 strict 被 strip 才"通过":view.list.object—— 容器根已声明object,重复一层是错的job.concurrency(无此键)、job.timeoutMs→timeout(单位本就是毫秒)email_template.from/.to—— 模板不是一次发送,没有to;发件人覆盖是fromOverride,且是{ name, address }对象而非裸地址串datasource.capabilities(objectstack#4583 移除:十一个 flag 全是声明了没人读,readOnly: true并没有让任何东西只读)、datasource.healthCheck(非 datasource 键)preview-samples-spec-valid.test.ts的 LIMIT 段落原文写着views/jobs/emailTemplates不是 strict —— 这句话现在是假的,一并更正。email_template与view校验器见上表 5、6。两者都是「客户端校验器指向了不是该 metadata kind 实际解析到的那个 schema」,正是 objectstack#4616 特意提醒的那一类。4. 需要复核的取舍(两处)—— PM 已裁决,均采 A
这两处不在任何一张单的原裁决范围内,实施侧按 AGENTS.md #0.1(契约优先、不在消费侧加宽容)做了选择,PM 复核后确认采 A,落地不变:
AreaSwitcher不再按visible/requiredPermissions过滤区域。能力没有丢失,只是下移到 item 级(NavigationRenderer用的就是原来那两个evalVis/checkPerm)。行为差异:某区域内 item 全被 gate 掉时,以前整块区域从切换器消失,现在渲染成"可见但空"。选它的理由是 spec 的 area 对象是.strict(),v17 之后任何合法 app 都写不出这三个键,该过滤分支对平台接受的元数据已不可达。@object-ui/core的NotificationProtocol(objectui#3310)。它桥接的 spec 词汇整包消失且无后继;仓内零调用,真正在跑的是@object-ui/reactNotificationContext里自己声明的同名resolveNotificationConfig(两个同名函数并存本身就是隐患,core 那个还按名字遮住 react 那个)。反向做法是在本仓重新声明一套 spec 刚删掉的词汇,那会造出第二方言。@object-ui/core公共表面的删除。第三处(
view两形状,objectui#3312):PM 裁定接受本 PR 的分派修复,「哪个是唯一 authorable 形状」转上游,不在本仓解。5. 上游前提错误(已另开单回传)
本轮又撞到两处与
homePageId同型的"退役理由与事实不符":ui的Notification/NotificationConfig在三仓 "zero importers"、"objectui's toaster never adopted it"。实际本仓两处在用。漏扫原因是两跳叠加:本仓用export … from再导出(不是import … from),且真正的消费者是从@object-ui/types而非从 spec 导入。这与派单时对HttpMethod报假阴性、以及 #4667 对homePageId断言 "no shell ever read it" 是同一个方法论坑,已经是第三次。 建议跨仓 liveness 审计改用解析后的符号图,而不是 import 语句文本。AppSchemaRenderer.AreaSwitcher一直在 gate。两单都写明:事实前提错了,但退役结论仍然站得住,本仓不主张撤销,只请上游更正文案(与 #4709 对
homePageId做的更正同类)。6. 验证
pnpm type-check(turbo,45 包)pnpm buildpnpm lintpnpm exec vitest run(仓根全量)changeset:check(no-major + fixed 组)测试一律用仓根
pnpm exec vitest run <paths>;未使用pnpm --filter <pkg> test -- --run <paths>(objectui#3288:路径过滤会被静默忽略)。所有 build/test 都包在共享的flock /tmp/os-heavy-verify.lock里跑。破坏验证(故意改坏 → 确认变红 → 还原 → 确认变绿),做在本 PR 改过行为的断言上:
findFirstLanding改为逆序(落地页派生,resolveLandingRoute的镜像)NavigationAreaSchema重新加回visiblehomePageId墓碑从派生里摘掉(改回 strip 语义)homePageId" 变红NavigationRenderer的evaluateVisibility/checkPermission改成恒真> 第 4 项的第一次破坏验证发现我自己新写的两条断言是空过的 —— 被 gate 的 item 放在了非激活区域,无论 gating 有没有生效都不会渲染。已改为放进首个(默认激活)区域,重跑破坏验证确认这两条现在真正承重。这正是这道工序的价值。
7. 变更集
.changeset/spec-17-rc2-pin-bump.md,按 fixed 组规约标minor(不是major),破坏性语义写在正文:HttpMethod改名跟随(形状不变)、落地页语义变更(显式钉了homePageId的 app 将改落在 nav 首项,指向os migrate meta --from 16)、area 三键、NotificationProtocol删除、email_template校验器修正。另修正
.changeset/previews-read-only-spec-declared-keys.md(objectui#3275 的 changeset,已在main上)中的一句话:它原本承诺"homePageId渲染成 nav item 的 id",而本 PR 恰恰删掉了该行为 —— 两条 changeset 会编进同一次 release notes,一条说 preview 会渲染homePageId、一条说该键已退役,客户读到的是自相矛盾的发版说明。既然使其失效的正是本 PR,修正它就是本 PR 的责任。只改那一句,bump 级别与其余部分一字未动。⛔ 未触碰
content/docs/releases/。Generated by Claude Code