发现于 #5004 的实施(PR #5023 )。不在那个 PR 内修复 —— 超出该卡完成范围(#5004 的范围是审计面板的 zh 词表 + 键绑定 + 死条目),按 Prime Directive #10 立卡,未认领。
打 finding 标签:今天四个状态齐全、两份副本内容一致,用户现在碰不到 。这是休眠的漂移面,不是现行缺陷。定级交 PM triage。
事实(逐条 file:line,基线 origin/main = b46cf11d6) ADR-0010 §3.6 的四态锁 none / no-overlay / no-delete / full 在本仓被逐字写了两遍 ,两处都是手写字面 union,彼此无任何门禁比对:
packages/data-objectstack/src/metadata-client.ts:298 —— MetadataLayered:lock?: 'none' | 'no-overlay' | 'no-delete' | 'full';packages/data-objectstack/src/metadata-client.ts:340 —— MetadataAuditEntry:lockState: 'none' | 'no-overlay' | 'no-delete' | 'full' | null;同一文件、相隔 42 行,一个 optional 一个 nullable,除此之外完全相同。没有一处从另一处派生,@objectstack/spec 也没有对应的 z.enum 可派生 —— 所以只给其中一处加第五态,编译器不会有任何意见。
第三处枚举点是消费侧,且没有穷尽检查 :packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx:1935-1937 的锁定横幅标题是三条彼此独立的 &&,没有 else / 没有兜底:
{layered?.lock === 'full' && t('engine.edit.lockFull', locale)}
{layered?.lock === 'no-overlay' && t('engine.edit.lockNoOverlay', locale)}
{layered?.lock === 'no-delete' && t('engine.edit.lockNoDelete', locale)}
开关它的是 :1439 的 isLocked = layered?.lock && layered.lock !== 'none' —— 对 none 以外的任何值都为真 。
后果(第五态落地那天) 只给两份 union 之一加值:另一处编译照绿,漂移无声开始; ResourceEditPage 的 isLocked 判真 → 琥珀色横幅正常展开(图标 + 边框 + 可选的 lockReason),但三条 && 全假 → 标题那个 div 渲染成空 。用户看到一条没有说明文字的锁定横幅;对照:PR fix(metadata-admin): 审计面板锁状态列补齐中文,词表键绑 lockState union (#5004) #5023 之后,审计面板那侧的 LOCK_STATE_ZH 键绑了 MetadataAuditEntry['lockState'],同样的第五态会让 type-check 变红并点名缺失的标签。所以本仓现在对同一套词表有两种截然不同的严格度 —— 一处编译期报错,一处界面上静默留白。 建议(交维护者定,不预设) 两条路,取舍是归属权问题,不是实现细节:
A. 归 @objectstack/spec :spec 出 z.enum,两份 union 改为派生,消费侧的键表照 LAYER_SCOPE_ZH / LOCK_STATE_ZH 绑到 spec,跨仓漂移一并关闭。长期正确,但需要 spec 侧动作与跨仓协调。B. 留在本仓收敛为一份 :抽一个导出的 MetadataLockState 类型别名,两处引用它;ResourceEditPage 的三分支改成键绑的查表(或补一条 satisfies 穷尽的 switch),让第五态在编译期就红。成本小得多,但如果 spec 终究该拥有这个词表,这是一次中途站。无论走哪条,ResourceEditPage 横幅那三条 && 的穷尽性都值得单独补上 —— 它是唯一一处「新状态 = 界面空白」而非「编译期报错」的读点。
相关同族:#4984 (date-range 词表的两份未钉副本)、#4972 (ComponentInput 的三份结构副本)—— 都是「声明了 N 份、没有门禁比对」的同一失败类。
发现于 #5004 的实施(PR #5023)。不在那个 PR 内修复 —— 超出该卡完成范围(#5004 的范围是审计面板的 zh 词表 + 键绑定 + 死条目),按 Prime Directive #10 立卡,未认领。
打
finding标签:今天四个状态齐全、两份副本内容一致,用户现在碰不到。这是休眠的漂移面,不是现行缺陷。定级交 PM triage。事实(逐条 file:line,基线
origin/main=b46cf11d6)ADR-0010 §3.6 的四态锁
none/no-overlay/no-delete/full在本仓被逐字写了两遍,两处都是手写字面 union,彼此无任何门禁比对:packages/data-objectstack/src/metadata-client.ts:298——MetadataLayered:lock?: 'none' | 'no-overlay' | 'no-delete' | 'full';packages/data-objectstack/src/metadata-client.ts:340——MetadataAuditEntry:lockState: 'none' | 'no-overlay' | 'no-delete' | 'full' | null;同一文件、相隔 42 行,一个 optional 一个 nullable,除此之外完全相同。没有一处从另一处派生,
@objectstack/spec也没有对应的z.enum可派生 —— 所以只给其中一处加第五态,编译器不会有任何意见。第三处枚举点是消费侧,且没有穷尽检查:
packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx:1935-1937的锁定横幅标题是三条彼此独立的&&,没有 else / 没有兜底:开关它的是
:1439的isLocked = layered?.lock && layered.lock !== 'none'—— 对none以外的任何值都为真。后果(第五态落地那天)
ResourceEditPage的isLocked判真 → 琥珀色横幅正常展开(图标 + 边框 + 可选的lockReason),但三条&&全假 → 标题那个 div 渲染成空。用户看到一条没有说明文字的锁定横幅;LOCK_STATE_ZH键绑了MetadataAuditEntry['lockState'],同样的第五态会让type-check变红并点名缺失的标签。所以本仓现在对同一套词表有两种截然不同的严格度 —— 一处编译期报错,一处界面上静默留白。建议(交维护者定,不预设)
两条路,取舍是归属权问题,不是实现细节:
@objectstack/spec:spec 出z.enum,两份 union 改为派生,消费侧的键表照LAYER_SCOPE_ZH/LOCK_STATE_ZH绑到 spec,跨仓漂移一并关闭。长期正确,但需要 spec 侧动作与跨仓协调。MetadataLockState类型别名,两处引用它;ResourceEditPage的三分支改成键绑的查表(或补一条satisfies穷尽的 switch),让第五态在编译期就红。成本小得多,但如果 spec 终究该拥有这个词表,这是一次中途站。无论走哪条,
ResourceEditPage横幅那三条&&的穷尽性都值得单独补上 —— 它是唯一一处「新状态 = 界面空白」而非「编译期报错」的读点。相关同族:#4984(date-range 词表的两份未钉副本)、#4972(
ComponentInput的三份结构副本)—— 都是「声明了 N 份、没有门禁比对」的同一失败类。