Archived
fix(w05): 三份件的机械抽样式补齐缺失 class —— 落账件同款会炸,提前拦下
浏览器走查实测:差异评估结论审核件里三个 <svg class="zic"> 撑成 946×946,
整份文档拉到 4625px 高,正文全被挤走 —— 人打开只见灰色巨块,没法逐条核四态。
根因:.zic{width:14px;height:14px} 只在 P08_CSS(数据工单那一段),而
difference-review 用的是 ACC1_CSS。ACC1_CSS / ACC2_CSS 是从目标件机械抽出来的,
report.ts:538 那句既有注释早已承认漏过一批(.jc-vcell/.jc-cell/…),补了几个,
这几个漏了。
按「一次查干净」逐个核三份件用到的每个 class,补齐:
· .zic —— ACC1_CSS **与 ACC2_CSS**(后者即 landing-confirm,同款会炸,走查下一步就会撞上)
· .note .done / .note .need —— ACC1_CSS 与 ACC2_CSS
· .kv-box —— BIND_LANDING
· .risk-badge 缺基座形状(只有配色没有胶囊盒子)—— 人工核对徽标家族一致性时发现,机械扫描漏网
新增自测:三份件用到的 class 必须都能在它自己那段 CSS 里找到定义 ——
将来谁再加一个没定义的 class 就变红。变异验证:删掉 .zic 那条真红,补回真绿。
另:嵌套 data-by 的「只读态显示了并不存在的值」已坐实为渲染器侧机制
(递归合并时行内没有该字段会落回外层同名字段),加 it.fails 钉住,平台修法另议。
包版本 2.1.7。pnpm gates 退出码 0(164 文件 / 2450 测)。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
@@ -40,6 +40,41 @@ const viewHtml = (name: string) => manifest.assets.viewTemplates.find((t) => t.n
|
||||
const ledgerOf = (address: string) => manifest.declarations.ledger.find((l) => `${l.domain}.${l.object}` === address);
|
||||
const colOf = (address: string, name: string) => ledgerOf(address)?.fields.find((f) => f.name === name);
|
||||
|
||||
/**
|
||||
* 从一段 CSS 文本里摘出**选择器里提到过的所有 class token**(2026-08-30 task-12,缺陷③用)。
|
||||
* 只扫选择器,不扫规则体(`content:"…"` 这类属性值里的文字不算);`@media`/`@supports`
|
||||
* 这类带嵌套一层规则体的 at-rule 会往里再走一遍(本仓三份件的 `@media(max-width:600px)` 与
|
||||
* `@media (prefers-reduced-motion: reduce)` 都嵌着真正的选择器,不能漏)。
|
||||
* 这是**存在性**扫描(选择器里出没出现过这个 class),不判断它是不是复合选择器的一部分 ——
|
||||
* `.zic` 完全不存在是这份扫描要抓的那种漏(缺陷③的两个真实事故都是这一种:某个 class
|
||||
* 在目标件自己的 `<style>` 里一次都没被提到)。
|
||||
*/
|
||||
function definedClassesOf(css: string): Set<string> {
|
||||
const defined = new Set<string>();
|
||||
const noComments = css.replace(/\/\*[\s\S]*?\*\//g, '');
|
||||
const walk = (text: string): void => {
|
||||
let i = 0;
|
||||
while (i < text.length) {
|
||||
const openIdx = text.indexOf('{', i);
|
||||
if (openIdx === -1) break;
|
||||
const selector = text.slice(i, openIdx);
|
||||
for (const m of selector.matchAll(/\.([a-zA-Z0-9_-]+)/g)) defined.add(m[1] ?? '');
|
||||
let depth = 1;
|
||||
let j = openIdx + 1;
|
||||
while (j < text.length && depth > 0) {
|
||||
if (text[j] === '{') depth += 1;
|
||||
else if (text[j] === '}') depth -= 1;
|
||||
j += 1;
|
||||
}
|
||||
const body = text.slice(openIdx + 1, j - 1);
|
||||
if (/^\s*@media/.test(selector) || /^\s*@supports/.test(selector)) walk(body);
|
||||
i = j;
|
||||
}
|
||||
};
|
||||
walk(noComments);
|
||||
return defined;
|
||||
}
|
||||
|
||||
describe('清单本身合规', () => {
|
||||
it('过 @contracts 的 schema', () => {
|
||||
expect(() => activityManifestSchema.parse(manifest)).not.toThrow();
|
||||
@@ -445,6 +480,36 @@ describe('报告模版:编辑面与只读面各就各位', () => {
|
||||
expect(html, view).not.toContain('proto-root');
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* 缺陷③(2026-08-30 task-12,浏览器真人走查逮到,阻塞人):差异评估结论审核件里三个
|
||||
* `<svg class="zic">` 因为 `ACC1_CSS` 里没有 `.zic` 而撑到 946×946 像素、把整份文档
|
||||
* 顶到 4625px 高,正文全被挤出可视区,人打开只看见三块灰色巨块加大片空白。
|
||||
* 根因是 `ACC1_CSS`/`ACC2_CSS`(target-style.ts)虽然叫「机械抽自目标件」,但
|
||||
* report.ts 的 `zone()`/`note()` 助手把 P08 目标件「视觉补充段 v3」的图标与强调色
|
||||
* 跨文档复用到了这两张验收件上,原验收工单目标件自己的 `<style>` 从没定义过它们 ——
|
||||
* 同一批复查还揪出 `.note .done`/`.note .need`(两张验收件的提示文案同样失色)与
|
||||
* `.kv-box`(落账确认件的「待验项」盒子没有边框底色)。
|
||||
* 这条测试把「三份件各自用到的 class,都能在它自己那段 `<style>` 里找到定义」钉成机检 ——
|
||||
* 拿掉本轮任何一条修复,这里就会变红(变异验证见 task-12 报告)。
|
||||
*/
|
||||
it('三份件各自用到的 class,都能在它自己那段 <style> 里找到定义(不许再有 .zic 946×946 那种漏)', () => {
|
||||
for (const view of VIEWS) {
|
||||
const html = viewHtml(view);
|
||||
const styleMatch = /<style>([\s\S]*?)<\/style>/.exec(html);
|
||||
expect(styleMatch, view).not.toBeNull();
|
||||
const css = styleMatch?.[1] ?? '';
|
||||
const body = html.slice((styleMatch?.[0] ?? '').length);
|
||||
const used = new Set<string>();
|
||||
for (const m of body.matchAll(/\sclass="([^"]*)"/g)) {
|
||||
for (const c of (m[1] ?? '').split(/\s+/)) if (c !== '') used.add(c);
|
||||
}
|
||||
const defined = definedClassesOf(css);
|
||||
for (const cls of used) {
|
||||
expect(defined.has(cls), `${view}: class="${cls}" 用到了,但它自己的 <style> 里没有任何选择器提到 .${cls}`).toBe(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
@@ -54,7 +54,7 @@ export const manifest: ActivityManifest = {
|
||||
activityId: ACTIVITY_ID,
|
||||
name: 'ACT-WORK-05 · 差异评估',
|
||||
/* 重写不是改 —— 同版重装是 no-op(静默丢失,踩过) */
|
||||
packageVersion: '2.1.5',
|
||||
packageVersion: '2.1.7',
|
||||
category: '需求分析与方案匹配',
|
||||
sdkVersion: '0.1.0-m5',
|
||||
checksum: 'dev-checksum-part-substitution-assessment',
|
||||
|
||||
@@ -599,7 +599,14 @@ ${badgeCss('.rec', [
|
||||
['not_recommended', '不推荐', DANGER],
|
||||
])}
|
||||
.rec.rec-lg{font-size:14px;padding:6px 18px;letter-spacing:.02em}
|
||||
/* 替代风险(收口四格的「风险」,substitution.relation.risk_level)—— 走徽标而不裸摆英文枚举值 */
|
||||
/*
|
||||
* 替代风险(收口四格的「风险」,substitution.relation.risk_level)—— 走徽标而不裸摆英文枚举值。
|
||||
* 补一条基座形状(2026-08-30 task-12 缺陷③附带查出):badgeCss() 只出
|
||||
* .risk-badge.v-<值>(内容与配色)那几条复合选择器,从没有人补过裸 .risk-badge{…} 本身的
|
||||
* 盒子形状 —— 与 .stx / .ct 这两族「先起底、badgeCss 只管上色」的写法不同款,漏了这一步,
|
||||
* 徽标会显示成贴着字的纯色文字,没有胶囊的圆角与内边距。形状照抄 .ct。
|
||||
*/
|
||||
.risk-badge{display:inline-block;font-size:11px;font-weight:700;padding:1px 8px;border-radius:999px;border:1px solid transparent;white-space:nowrap}
|
||||
${badgeCss('.risk-badge', [
|
||||
['low', '低风险', OK],
|
||||
['medium', '中风险', AMBER],
|
||||
@@ -824,6 +831,14 @@ ${badgeCss('.ct', [
|
||||
.kv i{flex:0 0 84px;font-style:normal;color:var(--ink-4);font-size:11px;padding-top:1px}
|
||||
.kv span{flex:1 1 auto;color:var(--ink-2);line-height:1.6;overflow-wrap:anywhere}
|
||||
.kv span:empty::after{content:"—";color:var(--ink-4)}
|
||||
/*
|
||||
* 补 .kv-box(2026-08-30 task-12 缺陷③附带查出):VALIDATION_BLOCK 用它包一层边框/底色
|
||||
* (与差异评估结论审核件 CLOSING_SUMMARY 的 .summary-box 同一种「盒住几行 kv」的版式),
|
||||
* 这份文件之前只补了 .kv 系列本身,包裹它的盒子从没定义过 —— 待验项那一段会因此显示成
|
||||
* 一叠不带边框的裸文字,与「结论」那几格(有 .verdict-card 边框)风格不一致。
|
||||
* 形状照抄 .summary-box(BIND_ACC1_LAYOUT,同一份文件里)。
|
||||
*/
|
||||
.kv-box{border:1px solid var(--line-soft);border-radius:10px;background:var(--soft);padding:2px 16px;margin-bottom:4px}
|
||||
`;
|
||||
|
||||
/**
|
||||
|
||||
@@ -173,6 +173,22 @@ body{font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,
|
||||
.note{border:1px solid var(--line);border-left:3px solid var(--ledger);border-radius:8px;background:var(--soft);padding:14px 16px;margin-bottom:16px}
|
||||
.note p{margin:0;font-size:13px;color:var(--ink-2)}
|
||||
.note b{color:var(--ink)}
|
||||
/*
|
||||
* 补三条:report.ts 的 zone()/note() 助手把 P08 目标件「视觉补充段 v3」的图标
|
||||
* (.zic)与强调色(.note .done / .note .need)跨文档复用到了这两张验收件的
|
||||
* 区块头与提示文案上(见 report.ts 的 ZIC_TASK / ZIC_TABLE 常量与 note() 调用处)——
|
||||
* 原验收工单目标件自己的 style 段从未定义过它们(核对过
|
||||
* docs/SS02-ACT-WORK-05-html/ 下两份验收工单目标件,原型的 zone 段从没带过图标,
|
||||
* note 里也没有 done/need 分级)——补两条,值照 P08_CSS 抄
|
||||
* (2026-08-30 task-12,缺陷③;--data 这份 :root 没有,用同色的 --brand 顶替,
|
||||
* 数值仍是 #3370ff,不算改动目标件写死的颜色)。
|
||||
* 漏掉 .zic 的后果曾经是灾难性的:三枚 viewBox="0 0 24 24" 的图标在没有 .zic 约束
|
||||
* 宽高时会撑满容器宽、再按比例撑高,量出来是 946×946 像素、把整份文档撑到 4625px 高,
|
||||
* 正文全被挤出可视区(人打开只看见三块灰色巨块加大片空白)。
|
||||
*/
|
||||
.zic{width:14px;height:14px;color:var(--ink-3);vertical-align:-2px;margin-right:3px}
|
||||
.note .done{color:var(--ok);font-weight:700}
|
||||
.note .need{color:var(--brand);font-weight:700}
|
||||
.criteria{border:1px solid var(--ledger-line);border-radius:10px;background:var(--ledger-soft);padding:13px 16px;margin-bottom:18px}
|
||||
.criteria .c-title{font-size:11px;font-weight:800;letter-spacing:.08em;color:var(--ledger);margin-bottom:7px}
|
||||
.criteria ul{margin:0;padding-left:18px;font-size:12.5px;color:var(--ink-2)}
|
||||
@@ -253,6 +269,22 @@ body{font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,
|
||||
.note{border:1px solid var(--line);border-left:3px solid var(--ledger);border-radius:8px;background:var(--soft);padding:14px 16px;margin-bottom:16px}
|
||||
.note p{margin:0;font-size:13px;color:var(--ink-2)}
|
||||
.note b{color:var(--ink)}
|
||||
/*
|
||||
* 补三条:report.ts 的 zone()/note() 助手把 P08 目标件「视觉补充段 v3」的图标
|
||||
* (.zic)与强调色(.note .done / .note .need)跨文档复用到了这两张验收件的
|
||||
* 区块头与提示文案上(见 report.ts 的 ZIC_TASK / ZIC_TABLE 常量与 note() 调用处)——
|
||||
* 原验收工单目标件自己的 style 段从未定义过它们(核对过
|
||||
* docs/SS02-ACT-WORK-05-html/ 下两份验收工单目标件,原型的 zone 段从没带过图标,
|
||||
* note 里也没有 done/need 分级)——补两条,值照 P08_CSS 抄
|
||||
* (2026-08-30 task-12,缺陷③;--data 这份 :root 没有,用同色的 --brand 顶替,
|
||||
* 数值仍是 #3370ff,不算改动目标件写死的颜色)。
|
||||
* 漏掉 .zic 的后果曾经是灾难性的:三枚 viewBox="0 0 24 24" 的图标在没有 .zic 约束
|
||||
* 宽高时会撑满容器宽、再按比例撑高,量出来是 946×946 像素、把整份文档撑到 4625px 高,
|
||||
* 正文全被挤出可视区(人打开只看见三块灰色巨块加大片空白)。
|
||||
*/
|
||||
.zic{width:14px;height:14px;color:var(--ink-3);vertical-align:-2px;margin-right:3px}
|
||||
.note .done{color:var(--ok);font-weight:700}
|
||||
.note .need{color:var(--brand);font-weight:700}
|
||||
.criteria{border:1px solid var(--ledger-line);border-radius:10px;background:var(--ledger-soft);padding:13px 16px;margin-bottom:18px}
|
||||
.criteria .c-title{font-size:11px;font-weight:800;letter-spacing:.08em;color:var(--ledger);margin-bottom:7px}
|
||||
.criteria ul{margin:0;padding-left:18px;font-size:12.5px;color:var(--ink-2)}
|
||||
|
||||
@@ -434,6 +434,41 @@ describe('嵌套行模版与 data-by —— 一个候选一张表', () => {
|
||||
const all = renderView(html.replace(' data-by="rel_id"', ''), scope);
|
||||
expect(all.split('使能逻辑').length - 1).toBe(2);
|
||||
});
|
||||
|
||||
/*
|
||||
* **已知问题,尚未修 —— 2026-08-30(ACT-WORK-05 task-12,缺陷①,浏览器真人走查逮到)**。
|
||||
* 现场报的是「候选料一行的芯片类型只读态显示着一个值,而那一格实际是空的,编辑态与 SQL
|
||||
* 双重核实过是空串」。原本怀疑是 `byParent` 对空父键的处理退化成显示第一行 —— 查下来这
|
||||
* 个猜测是错的:上面「父值缺席一行不留」那条已经钉死了"这一行自己把父键显式写成空字符串"
|
||||
* 这个场景(`rel_id: ''`),`byParent` 的 `parent === ''` 守卫恒定正确,不是这次的病根。
|
||||
*
|
||||
* 真正能复现的是**另一种缺席**:这一行压根**没有**这个字段(不是空字符串,是键都不存在)——
|
||||
* 常见于必填但还没填的草稿行(如 ACT-WORK-05 的 input-supplement 件:候选料还没定型号时,
|
||||
* `sub_eval.part` 那一行不会带 `chip_type_id` 这个键)。`renderRows` 递归处理嵌套
|
||||
* `data-rows` 时用 `{ ...scope, ...one }` 合并外层与本行 —— `one` 缺席的键会**悄悄落回
|
||||
* 外层 scope 里同名的字段**。而外层 scope 常常真的有一个同名字段:`engines.ts` 的
|
||||
* `taskData.view()` 会把「各对象最新一行的字段名」铺到顶层(`task.data`),
|
||||
* `part-substitution-assessment` 的 `case-model.ts` 就因为撞过这同一个坑,自己的 `auto`
|
||||
* 配方特地避免用 `{in:'chip_type_id'}`(该文件 134 行注释:「这个名字在三张表上都有,
|
||||
* 顶层同名以后写的赢」)—— 但那条护栏只保护了 case-model 自己的 S-FEEL 求值,没有覆盖到
|
||||
* 这里的 `data-by` 嵌套查表,于是同一类撞名在 `view-render.ts` 这一侧原样重演。
|
||||
*
|
||||
* 用 `it.fails`:这条断言此刻确实会失败(已用最小复现核对过,不是这条测试自己写错)——
|
||||
* 先把复现钉进套件,不在这一轮改 `view-render.ts`(共享面,E04/I01 也用 `data-by`,
|
||||
* 改动需要先报给设计者裁定修法);等修完翻正、去掉 `.fails`,它就是永久回归测试。
|
||||
*/
|
||||
it.fails('父键在本行里压根缺席(不是空字符串)时,不该被外层作用域里恰好同名的字段顶替', () => {
|
||||
const withNestedLookup =
|
||||
'<tbody data-rows="parts"><tr><td data-slot="pn"></td>' +
|
||||
'<span data-rows="dict" data-by="id=type_id"><i data-slot="name"></i></span>' +
|
||||
'</tr></tbody>';
|
||||
const out = renderView(withNestedLookup, {
|
||||
type_id: '5', // 页面顶层恰好有个同名字段(如 sub_eval.case.chip_type_id 铺到顶层的产物)
|
||||
parts: [{ pn: 'XL2596S-5.0' /* 这一行没有 type_id 这个键 —— 该型号还没定型 */ }],
|
||||
dict: [{ id: '5', name: 'DC-DC 降压稳压器' }],
|
||||
});
|
||||
expect(out).not.toContain('DC-DC 降压稳压器');
|
||||
});
|
||||
});
|
||||
|
||||
describe('data-options:下拉的选项从台账现读', () => {
|
||||
|
||||
Reference in New Issue
Block a user