Uh oh!
There was an error while loading. Please reload this page.
fix(metadata-admin): overlayScope 收窄为 spec 派生词表,层徽标过 i18n - #5005
Conversation
`MetadataLayered.overlayScope` 原本声明为 `string | null`,紧邻注释把词表写成 `organization | environment | package` —— 三种拼法生产者一个都不发。真实词表只在 `@objectstack/spec` 的 `GetMetaItemLayeredResponseSchema` 里: `z.enum(['org','env']).nullable()`;framework `metadata-protocol` 两处赋值也只写 `'org'` / `'env'`。因为声明是 `string`,编译器对此毫无意见,那条错注释就成了字段 唯一的说明 —— 不是过时文档,而是给下一位读者(或 agent)埋的前提。 现在按仓内惯例从 spec **派生**而非消费侧复述:新增 `MetadataOverlayScope`,以索引 spec 已发布响应类型的方式绑定,spec 加一个 scope 本仓自动跟随。复述局部 union 正是 `check:spec-symbol-derivation` 要拒绝的 fork 的镜像类。 用户可见的那一半:`LayeredDiff` 的 overlay 徽标把该值原样直出,而同一列表里另外三个 层徽标(artifact / none / merged)全部过 `translateConsoleValue`,且 `CONSOLE_VALUE_ZH.layer` 对该字段能取的两个值都没有条目。于是同一个徽标的语言取决于 数据:zh-CN 管理员打开任一有 overlay 的元数据项读到裸的 `org` / `env`,没有 overlay 的走 `set` 支路才读到「已设」。徽标现在与三个同伴一致过翻译,层词表补入「组织」/「环境」。 该词表的 overlay-scope 部分以 spec union 为键(`LAYER_SCOPE_ZH`),所以日后 spec 新增 scope 会在 `type-check` 处失败并点名缺失的标签,而不是让英文原值悄悄漏到界面上。 `translateConsoleValue` 仍然只对 zh 生效(所有分组一致)—— 是否扩到其余语言包是独立 决定,不搭这趟车。 测试:词表派生钉(双向 `satisfies` + 一条 `@ts-expect-error`,把收窄退回 `string | null` 即以 TS2578 变红)、spec schema 对三种旧注释拼法的拒绝、LayeredDiff 在 zh-CN 下按 spec 选项逐个渲染中文徽标、en-US 保持原值、无 overlay 与无 scope 两条 支路不回归。 Fixes#4982 Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
yinlianghui
commented
Aug 17, 2026
PM 验收 ✅ ACCEPT(#4982,批次 20) 实物核验(merge-base CI 亲读:20/20 check runs completed,零失败(两项 path-filter skipped 计绿)。 反向验证读数:变异①的如实分类是本单最值得记的 —— 派生钉是编译期断言,回退后 vitest 永远绿、红在 tsc(恰好两条错误,TS1360 + TS2578),dev 明确拒绝把它写成「测试变红」的模板化伪装;②③把 bug 的两半(词表缺失 / 渲染直出)分开钉住各有各的红。「引用同一性 premise 复核更强:spec 对注释声称的三种拼法逐个拒绝、全仓取值面仅 null/'org',收窄零断点(仓根 type-check 81/81 亲证)。draft 先开、反向验证三格标 ⏳ 后回填实测的时序已披露且无残留占位,记账成立。finding #5004( undraft + auto-merge(squash)。 Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#4982
前提复核(锚点在合并后 ref 上重定位)
基线
origin/main=85fdb0612,#4016 的 PR #4988(cf4f8a6e4)已在其中。分诊要求的硬串行门已满足,三处锚点复核如下:metadata-client.ts的MetadataLayered.overlayScopeLayeredDiff.tsx:196i18n.ts:4125(CONSOLE_VALUE_ZH.layer)卡面三条事实逐条复核为真:
string | null,紧邻注释把词表写成organization | environment | package;@objectstack/spec@17.0.0里,GetMetaItemLayeredResponseSchema的overlayScope是z.enum(['org', 'env']).nullable()—— 实测该 schema 对上面三种拼法逐个拒绝,package这个 scope 在此字段上从来没有过;overlayScope的取值面只有null(23 处)与'org'(4 处),没有任何一处写那三种拼法 —— 所以收窄不会打断任何现存调用点。改动
契约侧(
@object-ui/data-objectstack) —— 词表从 spec 派生,不在消费侧复述:以索引 spec 已发布响应类型的方式绑定,spec 日后加一个 scope 本仓自动跟随。复述一份局部 union 正是
check:spec-symbol-derivation要拒绝的 fork 的镜像类,所以没有走那条路;别名随包导出(MetadataOverlayScope),字段与那条错注释一并改正。因为原声明是
string,编译器对这个字段从来没有意见,那条错注释就成了它唯一的说明 —— 不是过时文档,而是给下一位读者(或 agent)埋的前提。用户可见侧(
@object-ui/app-shell) ——LayeredDiff的 overlay 徽标此前把原值直出,而同一列表另外三个层徽标(artifact / none / merged)全部过translateConsoleValue,且CONSOLE_VALUE_ZH.layer对该字段能取的两个值都没有条目。结果是同一个徽标的语言取决于数据:zh-CN 管理员打开任一有 overlay 的元数据项读到裸英文org/env,没有 overlay 的走set支路才读到「已设」。现在它与三个同伴一致过翻译,层词表补入「组织」/「环境」。补的那两条不是手写键,而是以 spec union 为键:
spec 日后新增 scope 会在
type-check处失败并点名缺失的标签,而不是让英文原值悄悄漏到界面上 —— 也就是让这个 bug 的形状在编写期就无法再成立。禁区已守:
translateConsoleValue仍然只对 zh 生效(所有分组一致,其余语言恒返回原值),这是它的现有设计;是否扩到十个语言包是独立决定,本 PR 不搭车,并且专门留了一条 en-US 用例把「没有顺手改」钉住。测试
pnpm exec vitest run packages/data-objectstack/ packages/app-shell/src/views/metadata-admin/ --maxWorkers=2→ 214 files / 2274 passed, 1 skippedpnpm exec turbo run type-check --concurrency=2→ 81 successful, 81 total,零 TS 错误node scripts/check-control-bytes.mjs(4465 个文件)、node scripts/check-spec-symbol-derivation.mjs、node scripts/check-changeset-no-major.mjs全绿no-explicit-any/react-refreshwarning,均不在本 diff 触及的行上)新增两个文件,共 8 条用例。
关于「引用同一性
toBe」这一钉:此处不适用,如实记录本仓最强的派生钉形态是
expect(local).toBe(spec)(见packages/types/src/__tests__/spec-subschema-parity.test.ts—— 忠实的拷贝也会红,因为拷贝就是 fork)。这里没有可比的运行期见证:绑定是类型别名,产品代码只import type,不引入任何 spec 值。为了让一条toBe有东西可比而从本包导出一份 enum options 拷贝,恰好就是那个 guard 要拒绝的 fork,所以刻意不做。同等强度的替代是三条:satisfies(spec 成员全部流入别名 / 别名不比 spec 更宽)。本包tsconfig.json的include覆盖整个src(含测试),所以type-check真的编译它们 —— 是 enforcement,不是装饰(objectui#3009 的区分);@ts-expect-error:把收窄退回string | null,'organization'重新可赋值,该指令失去抑制对象,tsc以 TS2578 报红。这是让本修复的回退变红的那一行;LAYER_SCOPE_ZH键类型 —— 编译期覆盖,见上。app-shell 一侧的用例不写死 scope 清单,而是从
GetMetaItemLayeredResponseSchema读 options 迭代,spec 新增的 scope 一存在就自动进入覆盖。反向验证(先书面预判,commit 后变异,
git checkout --还原,⛔ stash)三条变异全部与预判一致,无一条需要改写预判:
overlayScope退回string | null(保留别名)type-check红:TS2578(@ts-expect-error失效)+_fieldIsAlias的satisfies失败;vitest 仍绿tsc恰好两条错误:metadata-client.overlayScope.test.ts(54,74): error TS1360: Type 'string | null' does not satisfy the expected type '"org" | "env" | null'与(58,1): error TS2578: Unused '@ts-expect-error' directive;同一文件 vitest 2 passedCONSOLE_VALUE_ZH.layer里的...LAYER_SCOPE_ZHno entry for overlay scope 'org',两条渲染用例读到裸英文徽标AssertionError: CONSOLE_VALUE_ZH.layer has no entry for overlay scope 'org': expected 'org' not to be 'org';两条渲染用例栽在queryByText(scope)上 —— 裸值确实在屏幕上layered.overlayScope ?? …原值直出overlayScope=org/=env两条① 的预判方向值得单独说明:它不是「钉子变红」的常规方向。派生钉是编译期的,所以退回收窄后
vitest会一直绿 —— 只有type-check会红,实测也正是如此(2 passed)。把它写成「测试变红」会是一份符合模板、读起来却像已验证的伪造,所以这里照实分开写。②③ 合起来把这个 bug 的两半分开钉住:③ 只动渲染(词表在,徽标不查表)→ 覆盖用例仍绿;② 只动词表(徽标查表,表里没有)→ 覆盖用例也红。两半各有各的红。
还原后三个文件回到 HEAD,
git status干净,重跑两文件 8 passed。顺手发现(已另立卡,未在本 PR 修复)
同一张
CONSOLE_VALUE_ZH表的邻组lock与MetadataAuditEntry.lockState完全错位(命中率 0/3:表里 4 条有 3 条对不上任何可能值,唯一对得上的none被调用点显式排除,而实际能送到的no-overlay/no-delete/full一条都没有):已按 Prime Directive #10 立为 #5004,未认领,未在本 PR 修复。它的修法与本卡不同(那套 4 态锁在本仓是手写 union,spec 没有可派生的 enum),所以不是本卡的子任务。Generated by Claude Code