发现于 #3832 实施时的反向验证(变异 ③)。观察类:今天零个 假臂 —— #3832 落地的五处臂都逐一对 spec / 渲染器实测过 —— 所以这是门禁强度的空缺,不是活缺陷。
机制 apps/console/src/__tests__/registry-inputs-spec-parity.test.ts 两个方向都只比键名 (它自己的文件头写着这一条)。类型的宽窄它看不见。
在 #3832 之前这条空缺的后果有限:type 只能承载一个粗类型,写窄是唯一的失败模式,而写窄只会产生噪音 (对合法值报警)。#3832 之后 type 可以承载臂数组,于是多出一个反向 失败模式:多写一条 spec 不接受的臂,门就会放行 spec 拒绝的值 —— 从「门比契约窄」变成「门比契约宽」,而后者不再是噪音,是漏报。
实测(变异 ③,两半答案不同,这正是本卡的判据) 在 PR(#3832 )的分支上,分别给一个标本加一条 spec 不接受的假臂:
③a — element:text_input.defaultValue 加 'object' 臂(spec 在该键上拒绝映射,实测):packages/components/src/__tests__/text-input-inputs-spec-parity.test.ts钉红 ,AssertionError: expected [ 'number', 'object', 'string' ] to deeply equal [ 'number', 'string' ]
—— 因为那个 per-block 测试是把臂当集合 对 spec 的裁决逐条比对的。③b — page:card.title 加 'number' 臂(spec 拒绝数字标题):Test Files 93 passed | Tests 856 passed —— 全绿 ,无一处门禁察觉。同一次变异下门的行为实测:
declared arms: ["string","object","number"]
string -> CLEAN
i18n map -> CLEAN
number (假臂 —— spec 拒绝) -> CLEAN ← 漏报
boolean (不命中任何臂) -> warning/type-mismatch: prop "title" expected a string or an object or a number
array (不命中任何臂) -> warning/type-mismatch: 同上
即:「联合不是放行一切」这半成立(不命中任何臂照旧报告),而「臂对齐契约」这半今天靠的是 per-block 纪律,不是门禁 —— 有 per-block 测试的键(#3832 五个标本都有)红,没有的键全绿。
可能的方向(不预设结论) (a) 给 registry-inputs-spec-parity 加类型维度:对每个键,用一组探针值(字符串 / 数字 / 布尔 / 数组 / 对象 / null)分别问 spec 的 safeParse 与 checkType,断言两边的 accept 集合相等。derived,不是手工清单;和现有的键名方向共用 covered 集与豁免纪律。代价:探针值的粒度是粗类型级,enum 臂与嵌套形状要么豁免要么另立判据(成员形状是 PR fix(plugin-detail): record:highlights 的 fields 声明补上 readonly,让 manifest 能被作者读到 (#3407) #3795 那条 open question)。(b) 只做单向:断言声明的臂集合 ⊆ spec 接受的形状(禁假臂),不管窄。便宜一半,消掉的正是本卡这个新失败模式。(c) 留在 per-block 纪律,把「新增/修改臂必须带 per-block spec 断言」写进 AGENTS.md。最便宜,但依赖作者记得 —— parity 门只比键名,retiredKey() 墓碑会被判成「spec 接受」—— 今天 pin 版零墓碑所以休眠,spec pin 一升就是门里的假绿(page:card.body 是现成标本) #3809 的墓碑盲区同一根因。倾向 (b) 起步:它对齐的是「declared = enforced」,而且假臂是新引入的那个方向,窄化至少还是响的(噪音),宽化是静默的。
关联:#3832 / 本卡的 PR、#3809 (同一个「只比键名」根因的墓碑盲区)、#3795 (成员形状的 open question)、#3797 / #3808 (键名两个方向)、AGENTS.md #0.1
发现于 #3832 实施时的反向验证(变异 ③)。观察类:今天零个假臂 —— #3832 落地的五处臂都逐一对 spec / 渲染器实测过 —— 所以这是门禁强度的空缺,不是活缺陷。
机制
apps/console/src/__tests__/registry-inputs-spec-parity.test.ts两个方向都只比键名(它自己的文件头写着这一条)。类型的宽窄它看不见。在 #3832 之前这条空缺的后果有限:
type只能承载一个粗类型,写窄是唯一的失败模式,而写窄只会产生噪音(对合法值报警)。#3832 之后type可以承载臂数组,于是多出一个反向失败模式:多写一条 spec 不接受的臂,门就会放行 spec 拒绝的值 —— 从「门比契约窄」变成「门比契约宽」,而后者不再是噪音,是漏报。实测(变异 ③,两半答案不同,这正是本卡的判据)
在 PR(#3832)的分支上,分别给一个标本加一条 spec 不接受的假臂:
element:text_input.defaultValue加'object'臂(spec 在该键上拒绝映射,实测):packages/components/src/__tests__/text-input-inputs-spec-parity.test.ts钉红,AssertionError: expected [ 'number', 'object', 'string' ] to deeply equal [ 'number', 'string' ]—— 因为那个 per-block 测试是把臂当集合对 spec 的裁决逐条比对的。
page:card.title加'number'臂(spec 拒绝数字标题):Test Files 93 passed | Tests 856 passed—— 全绿,无一处门禁察觉。同一次变异下门的行为实测:
即:「联合不是放行一切」这半成立(不命中任何臂照旧报告),而「臂对齐契约」这半今天靠的是 per-block 纪律,不是门禁 —— 有 per-block 测试的键(#3832 五个标本都有)红,没有的键全绿。
可能的方向(不预设结论)
registry-inputs-spec-parity加类型维度:对每个键,用一组探针值(字符串 / 数字 / 布尔 / 数组 / 对象 / null)分别问 spec 的safeParse与checkType,断言两边的 accept 集合相等。derived,不是手工清单;和现有的键名方向共用covered集与豁免纪律。代价:探针值的粒度是粗类型级,enum臂与嵌套形状要么豁免要么另立判据(成员形状是 PR fix(plugin-detail): record:highlights 的 fields 声明补上 readonly,让 manifest 能被作者读到 (#3407) #3795 那条 open question)。retiredKey()墓碑会被判成「spec 接受」—— 今天 pin 版零墓碑所以休眠,spec pin 一升就是门里的假绿(page:card.body是现成标本) #3809 的墓碑盲区同一根因。倾向 (b) 起步:它对齐的是「declared = enforced」,而且假臂是新引入的那个方向,窄化至少还是响的(噪音),宽化是静默的。
关联:#3832 / 本卡的 PR、#3809(同一个「只比键名」根因的墓碑盲区)、#3795(成员形状的 open question)、#3797 / #3808(键名两个方向)、AGENTS.md #0.1