fix(w05): 评审回来的一条 Critical、一条 Important、两条 Minor

Critical:S1 数据工单的 gateLedgers 带着 parts.chip_type,而这张件没有任何手段
写它(选类型走 data-options 下拉现读,不是编辑表)—— mine:parts.chip_type 永远
是空数组,desk.ts 的 emptyGate 永远判「这一单还一行都没有」,确认钮永久按不下去,
三张真被编辑的表填得再全也一样。删掉 parts.chip_type,只留真被这张件写过的三张;
注释写明这是永久决定,不是等 Task 7b 补了新建类型的编辑表就该还原(主路是从下拉
挑已有类型,那时这张表天然零行,「零行=没干完」这条闸的前提对它压根不成立)。
新增 apps/api 侧回归探针(调用生产 emptyGate,不抄实现):证明旧配置确实会永久
卡死、新配置在同一份数据下真的放行。

Important:BIND_COMMON 的序号计数器规则只认 .tbl-wrap,而三张表的外壳是
.jc-tbl-wrap(继承自已删除的旧 P08_MASTER_CREATE 同一个坑)—— 两个 class 对不上,
「#」列在只读态与编辑态下逐行空白。补上 .jc-tbl-wrap,钉一条结构测试。

Minor:①block③ 的注释「只两栏可改」没说全 —— internal_pn/param_name 也走 cell()
(主键格必挂 data-col 是既定约定,行的钥匙),补全这句话;②data-class-of="gate_reason"
在中文自由文本上永远算不出 v-token,是源码里躺着的一个永远不起作用的属性——删掉,
显隐只留 :has(:empty) 一处,brief 给的那条断言同步改成断真实机制。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
xcs
2026-08-29 19:11:03 +08:00
co-authored by Claude Opus 5
parent fdcd1f88a7
commit 40daa6c9f7
5 changed files with 128 additions and 17 deletions
@@ -308,6 +308,43 @@ describe('闸看哪几张表:卡上绑的优先,没绑回落到必交付物声
expect(emptyGate({}, [{ domain: 'a', object: 'b' }], [])).toBeUndefined();
});
/*
* **W05 `input-supplement` 回归网**(2026-08-29 评审 Critical:确认钮永久按不下去)。
*
* 那张数据工单卡的 `gateLedgers` 一度带着 `parts.chip_type`,而件上没有任何
* `data-edit="parts.chip_type"`(选类型走的是 `data-options` 下拉现读,不写这张表)——
* `mine:parts.chip_type` 因而永远是空数组,`emptyGate` 永远判「这一单还一行都没有」,
* 人把另外三张表填得再全,提交钮也按不下去。这条钉死两件事:
* ①**复现旧配置确实会永久卡死**(不是猜的);②**修完(去掉 chip_type)之后闸真的开得了**——
* 同一份 `mine`(chip_type 全程零行,profile/type_param/parameter 都已填好)在新
* `gateLedgers` 下必须放行。ledger 的 domain/object/label 抄自
* `activities/part-substitution-assessment/src/manifest.ts` 的声明原文。
*/
it('W05 input-supplement:gateLedgers 若带 parts.chip_type 会永久卡死(它没有编辑手段可写)——去掉之后,三张真被编辑的表填齐即放行', () => {
const watched = [
{ domain: 'parts', object: 'chip_type', label: '芯片类型字典' },
{ domain: 'parts', object: 'type_param', label: '芯片类型核心参数字段' },
{ domain: 'parts', object: 'profile', label: '芯片主数据(P-08)' },
{ domain: 'parts', object: 'parameter', label: '标准化参数集(P-08 子表)' },
];
// parts.chip_type 从未被这张件写过 —— 无论闸怎么配,它在 mine 里恒是空数组
const filledExceptChipType = {
'mine:parts.type_param': [{ type_id: '1', param_name: '输入电压范围' }],
'mine:parts.profile': [{ internal_pn: 'LM2596S-5.0' }, { internal_pn: 'XL2596S-5.0' }],
'mine:parts.parameter': [{ internal_pn: 'LM2596S-5.0', param_name: '输入电压范围' }],
};
// 旧配置(已删除,这里复现只为证明它确实会卡死):chip_type 也在闸的名单里
const buggyDeclared = ['parts.chip_type', 'parts.type_param', 'parts.profile', 'parts.parameter'];
const stuck = emptyGate(filledExceptChipType, watched, buggyDeclared);
expect(stuck?.ready).toBe(false);
expect(stuck?.why).toContain('芯片类型字典');
expect(stuck?.why).toContain('这张卡就是来建它的');
// 修完(case-model.ts 现在的真实值):chip_type 不列入闸的名单
const fixedDeclared = ['parts.type_param', 'parts.profile', 'parts.parameter'];
expect(emptyGate(filledExceptChipType, watched, fixedDeclared)).toBeUndefined();
});
it('优先级:绑的表 > 卡自报 > 交付物;空串与空白不算数', () => {
expect(gateAddressesOf([table('quote_intake.record')], [{ ledgers: ['a.b'] }], ['parts.profile'])).toEqual(['quote_intake.record']);
expect(gateAddressesOf(undefined, [{ ledgers: ['a.b'] }], ['', ' '])).toEqual(['a.b']);