@qilitt-mickey/vue3-temp-skill 1.1.53 → 1.1.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +2 -2
- package/package.json +1 -1
- package/parser/fixtures/changeset-fullpage-restore.json +1 -1
- package/parser/fixtures/changeset-neg6-g9-identity.json +1 -1
- package/parser/fixtures/changeset-neg7-g10-dims.json +1 -1
- package/parser/fixtures/changeset-neg8-g11-oracle.json +1 -1
- package/parser/fixtures/changeset-registry-gap.json +1 -1
- package/parser/fixtures/changeset-v32-fields.json +1 -1
- package/parser/fixtures/verify/actual-clean.json +29 -0
- package/parser/fixtures/verify/actual.json +7 -1
- package/parser/fixtures/verify/oracle.json +13 -3
- package/parser/fixtures/verify/snapshot-latest.json +87 -0
- package/parser/index.js +137 -8
- package/parser/regression.js +53 -11
- package/references/design-apply.md +24 -433
- package/references/steps/maintenance.md +16 -0
- package/references/steps/regression.md +47 -0
- package/references/steps/step0-preflight.md +51 -0
- package/references/steps/step1-match.md +58 -0
- package/references/steps/step1b-audit.md +23 -0
- package/references/steps/step1c-registry.md +29 -0
- package/references/steps/step2-structure.md +68 -0
- package/references/steps/step3-style.md +27 -0
- package/references/steps/step3b-shell.md +56 -0
- package/references/steps/step3c-priority.md +26 -0
- package/references/steps/step4-l3.md +13 -0
- package/references/steps/step5-gates.md +44 -0
- package/references/steps/step6-verify.md +58 -0
- package/parser/fixtures/verify//350/277/220/345/215/225/347/256/241/347/220/206-/351/222/264/350/223/235.html +0 -44
package/SKILL.md
CHANGED
|
@@ -51,7 +51,7 @@ tags: [vue3, typescript, element-plus, pinia, vite, unocss, crud, demo, code-qua
|
|
|
51
51
|
| `files-and-editors` | 附件、预览、下载、富文本、二维码 | `file-management.md`、`download-export.md`、`rich-text.md`、`qrcode-barcode.md` |
|
|
52
52
|
| `realtime-and-mobile` | WebSocket、移动端、微信、验证码、动效 | 对应专项 reference |
|
|
53
53
|
| `data-compare` | 编辑页变更对比和汇总 | `data-compare.md` |
|
|
54
|
-
| `design-apply` | 设计变更集落地:语义 ID
|
|
54
|
+
| `design-apply` | 设计变更集落地:语义 ID 三级匹配、结构/形态/两层样式转换、锚点与结构施工、覆写提权、渲染态验收、合规闸门。**主入口 `design-apply.md`(红线+契约+步骤索引),各步骤拆分册 `references/steps/`——做到哪一步读哪一份,禁止一次全部通读** | `design-apply.md`、`steps/`、`mapping/`、`parser/` |
|
|
55
55
|
|
|
56
56
|
## 工作流
|
|
57
57
|
|
|
@@ -92,7 +92,7 @@ tags: [vue3, typescript, element-plus, pinia, vite, unocss, crud, demo, code-qua
|
|
|
92
92
|
|
|
93
93
|
- 对照 `references/code-quality.md` 检查类型、组件复用、API、路由、权限、安全、异常反馈、响应式和可演进性。
|
|
94
94
|
- 在目标项目执行 `pnpm lint:fix`、`pnpm type-check`;改动构建配置时再执行 `pnpm build`。
|
|
95
|
-
- **设计文件落地时追加渲染态验收**:变更集含 `oracle`(或效果图带 `data-sid`)时,跑 `node parser/index.js verify`——抽取项目同名 `data-sid` 元素的 computed style,与效果图期望值逐点 diff
|
|
95
|
+
- **设计文件落地时追加渲染态验收**:变更集含 `oracle`(或效果图带 `data-sid`)时,跑 `node parser/index.js verify`——抽取项目同名 `data-sid` 元素的 computed style,与效果图期望值逐点 diff;缺口按四类归因(选择器落空 / 权重被压 / 语境错位 / **存在性缺口 G12**:`oracle.absent` 点位仍可见)修正,禁止用 `!important` 掩盖。多轮迭代场景每轮动工前先 `--snapshot` 导快照、交付前 `--baseline` 比对——上轮命中本轮退步 = 基线漂移,先回归上轮修法。申报过的固有残差(`oracle.residual_accepted`)不计缺口。
|
|
96
96
|
- **设计文件落地时核对登记表覆盖率**:变更集带登记表(`coverage.registry` 或 `--registry`)时,跑 `node parser/index.js match <cs.json> --registry <restore-registry.json>`——`C = A` 且无缺口才可交付;parser **当场以登记表为权威重算**,设计侧自报的 `coverage` 数字不参与。单独核验登记表对词典的通道就绪度用 `node parser/index.js registry <restore-registry.json>`(硬缺口 exit 1、通道缺口列出待补字段)。
|
|
97
97
|
- 最终说明:命中能力组、修改文件、功能验证、命令结果、未解决问题和下一步。
|
|
98
98
|
|
package/package.json
CHANGED
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
{ "id": "admin.content.pagination", "style": { "active_bg": "钴蓝-5", "active_color": "#FFFFFF" } }
|
|
82
82
|
],
|
|
83
83
|
"oracle": {
|
|
84
|
-
"mockup": "verify
|
|
84
|
+
"mockup": "verify/回归夹具-列表页-钴蓝.html",
|
|
85
85
|
"sids": [
|
|
86
86
|
"admin.layout.sidebar",
|
|
87
87
|
"admin.layout.sidebar.logo_area",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
{ "id": "admin.content.pagination", "style": { "active_bg": "橘橙-5", "active_color": "#FFFFFF" } }
|
|
18
18
|
],
|
|
19
19
|
"oracle": {
|
|
20
|
-
"mockup": "verify
|
|
20
|
+
"mockup": "verify/回归夹具-列表页-钴蓝.html",
|
|
21
21
|
"theme_mapping": { "from": "钴蓝", "to": "橘橙" },
|
|
22
22
|
"sids": [
|
|
23
23
|
"admin.layout.sidebar.menu",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
{ "id": "admin.content.pagination", "style": { "active_bg": "钴蓝-5", "active_color": "#FFFFFF" } }
|
|
36
36
|
],
|
|
37
37
|
"oracle": {
|
|
38
|
-
"mockup": "verify
|
|
38
|
+
"mockup": "verify/回归夹具-列表页-钴蓝.html",
|
|
39
39
|
"sids": [
|
|
40
40
|
"admin.content.table.header_cell",
|
|
41
41
|
"admin.content.table.cell",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
{ "id": "admin.content.pagination", "style": { "active_bg": "橘橙-5", "active_color": "#FFFFFF" } }
|
|
36
36
|
],
|
|
37
37
|
"oracle": {
|
|
38
|
-
"mockup": "verify
|
|
38
|
+
"mockup": "verify/回归夹具-列表页-钴蓝.html",
|
|
39
39
|
"sids": [
|
|
40
40
|
"admin.content.table.header_cell",
|
|
41
41
|
"admin.content.table.cell",
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "verify 夹具 · 项目实测(clean 全绿对照,与 oracle.json + actual.json 三件配套):全部属性命中、absent 点位已收口(display:none)、申报残差点位允许与效果图不同。供 U 步正例(exit 0)与 V 步快照基线段使用。",
|
|
3
|
+
"sids": {
|
|
4
|
+
"admin.content.table.header_cell": {
|
|
5
|
+
"padding": "16px",
|
|
6
|
+
"font-weight": "500",
|
|
7
|
+
"text-align": "left",
|
|
8
|
+
"background-color": "#fafafa",
|
|
9
|
+
"white-space": "nowrap"
|
|
10
|
+
},
|
|
11
|
+
"admin.content.table.cell_amount": {
|
|
12
|
+
"text-align": "right"
|
|
13
|
+
},
|
|
14
|
+
"admin.layout.tabs.item_active": {
|
|
15
|
+
"font-weight": "500",
|
|
16
|
+
"color": "#245bff"
|
|
17
|
+
},
|
|
18
|
+
"admin.layout.header.user_profile": {
|
|
19
|
+
"gap": "12px"
|
|
20
|
+
},
|
|
21
|
+
"admin.layout.header.breadcrumb": {
|
|
22
|
+
"display": "none"
|
|
23
|
+
},
|
|
24
|
+
"admin.layout.sidebar.logo_area": {
|
|
25
|
+
"background-color": "#10214d"
|
|
26
|
+
},
|
|
27
|
+
"$note": "logo_area 背景与效果图期望 #0B1A3C 不同 = 已申报固有残差(oracle.residual_accepted 命中后不计缺口,U 步正例演示)"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$comment": "verify 夹具 · 项目实测(含 3
|
|
2
|
+
"$comment": "verify 夹具 · 项目实测(含 3 类故意缺口 + 1 类存在性缺口,用于回归):header_cell 字重被压 + 对齐语境错位 + white-space 未取到(期望值加密后暴露「属性所在规则未命中」新形态);cell_amount 选择器落空;user_profile gap 语境错位;breadcrumb 仍渲染可见(G12 存在性缺口故意展示)。logo_area 命中 = L 夹具不制造残差缺口、保持归因计数稳定;残差演示见 actual-clean.json 的 U 步。",
|
|
3
3
|
"sids": {
|
|
4
4
|
"admin.content.table.header_cell": {
|
|
5
5
|
"padding": "16px",
|
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
},
|
|
14
14
|
"admin.layout.header.user_profile": {
|
|
15
15
|
"gap": "8px"
|
|
16
|
+
},
|
|
17
|
+
"admin.layout.header.breadcrumb": {
|
|
18
|
+
"display": "flex"
|
|
19
|
+
},
|
|
20
|
+
"admin.layout.sidebar.logo_area": {
|
|
21
|
+
"background-color": "#0b1a3c"
|
|
16
22
|
}
|
|
17
23
|
}
|
|
18
24
|
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$comment": "verify 夹具 · 效果图期望(computed style
|
|
2
|
+
"$comment": "verify 夹具 · 效果图期望(computed style 转储外形)。真实抽取由浏览器链路完成,本文件仅用于回归。顶层扩展字段:absent=效果图上不存在的出厂点位(G12 存在性闸断言目标,2026-09-21 面包屑事故机制化);residual_accepted=申报过的固有残差(命中豁免不计缺口,reason 必填)。header_cell 增补 white-space 期望 = 列头截断防线的期望值加密(oracle 无期望值的属性验了等于没验)。",
|
|
3
3
|
"sids": {
|
|
4
|
+
"admin.layout.sidebar.logo_area": {
|
|
5
|
+
"background-color": "#0B1A3C"
|
|
6
|
+
},
|
|
4
7
|
"admin.content.table.header_cell": {
|
|
5
8
|
"padding": "16px",
|
|
6
9
|
"font-weight": "500",
|
|
7
10
|
"text-align": "left",
|
|
8
|
-
"background-color": "#FAFAFA"
|
|
11
|
+
"background-color": "#FAFAFA",
|
|
12
|
+
"white-space": "nowrap"
|
|
9
13
|
},
|
|
10
14
|
"admin.content.table.cell_amount": {
|
|
11
15
|
"text-align": "right"
|
|
@@ -17,5 +21,11 @@
|
|
|
17
21
|
"admin.layout.header.user_profile": {
|
|
18
22
|
"gap": "12px"
|
|
19
23
|
}
|
|
20
|
-
}
|
|
24
|
+
},
|
|
25
|
+
"absent": [
|
|
26
|
+
"admin.layout.header.breadcrumb"
|
|
27
|
+
],
|
|
28
|
+
"residual_accepted": [
|
|
29
|
+
{ "sid": "admin.layout.sidebar.logo_area", "reason": "出厂 logo 图形属业务资产,效果图占位图形与实际 logo 不逐像素比对(restore-checklist §三:只还原载体模块)" }
|
|
30
|
+
]
|
|
21
31
|
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"generated_at": "2026-09-22T03:10:19.033Z",
|
|
3
|
+
"oracle": "E:\\work\\Project\\Template\\skills\\vue3-temp-skill\\parser\\fixtures\\verify\\oracle.json",
|
|
4
|
+
"actual": "E:\\work\\Project\\Template\\skills\\vue3-temp-skill\\parser\\fixtures\\verify\\actual-clean.json",
|
|
5
|
+
"points": {
|
|
6
|
+
"admin.layout.sidebar.logo_area": {
|
|
7
|
+
"verdict": "miss",
|
|
8
|
+
"props": {
|
|
9
|
+
"background-color": {
|
|
10
|
+
"expected": "#0B1A3C",
|
|
11
|
+
"actual": "#10214d",
|
|
12
|
+
"verdict": "residual",
|
|
13
|
+
"kind": "权重被压"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"admin.content.table.header_cell": {
|
|
18
|
+
"verdict": "hit",
|
|
19
|
+
"props": {
|
|
20
|
+
"padding": {
|
|
21
|
+
"expected": "16px",
|
|
22
|
+
"actual": "16px",
|
|
23
|
+
"verdict": "hit"
|
|
24
|
+
},
|
|
25
|
+
"font-weight": {
|
|
26
|
+
"expected": "500",
|
|
27
|
+
"actual": "500",
|
|
28
|
+
"verdict": "hit"
|
|
29
|
+
},
|
|
30
|
+
"text-align": {
|
|
31
|
+
"expected": "left",
|
|
32
|
+
"actual": "left",
|
|
33
|
+
"verdict": "hit"
|
|
34
|
+
},
|
|
35
|
+
"background-color": {
|
|
36
|
+
"expected": "#FAFAFA",
|
|
37
|
+
"actual": "#fafafa",
|
|
38
|
+
"verdict": "hit"
|
|
39
|
+
},
|
|
40
|
+
"white-space": {
|
|
41
|
+
"expected": "nowrap",
|
|
42
|
+
"actual": "nowrap",
|
|
43
|
+
"verdict": "hit"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"admin.content.table.cell_amount": {
|
|
48
|
+
"verdict": "hit",
|
|
49
|
+
"props": {
|
|
50
|
+
"text-align": {
|
|
51
|
+
"expected": "right",
|
|
52
|
+
"actual": "right",
|
|
53
|
+
"verdict": "hit"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"admin.layout.tabs.item_active": {
|
|
58
|
+
"verdict": "hit",
|
|
59
|
+
"props": {
|
|
60
|
+
"font-weight": {
|
|
61
|
+
"expected": "500",
|
|
62
|
+
"actual": "500",
|
|
63
|
+
"verdict": "hit"
|
|
64
|
+
},
|
|
65
|
+
"color": {
|
|
66
|
+
"expected": "#245BFF",
|
|
67
|
+
"actual": "#245bff",
|
|
68
|
+
"verdict": "hit"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"admin.layout.header.user_profile": {
|
|
73
|
+
"verdict": "hit",
|
|
74
|
+
"props": {
|
|
75
|
+
"gap": {
|
|
76
|
+
"expected": "12px",
|
|
77
|
+
"actual": "12px",
|
|
78
|
+
"verdict": "hit"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"admin.layout.header.breadcrumb": {
|
|
83
|
+
"verdict": "absent-hit",
|
|
84
|
+
"evidence": "display=none visibility=-"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
package/parser/index.js
CHANGED
|
@@ -19,12 +19,12 @@
|
|
|
19
19
|
* node parser/index.js list [--scene admin] [--verbose] # 列出全部语义 ID(--verbose 含字段与形态)
|
|
20
20
|
* node parser/index.js query <semantic.id> # 查询单个 ID 映射详情
|
|
21
21
|
* node parser/index.js audit --project <dir> [<cs…>] # 挂载点存在性 + 死通道静态审计()
|
|
22
|
-
* node parser/index.js verify --oracle <o> --actual <a> # 渲染态逐点验收:computed style 期望 vs 实测 diff
|
|
22
|
+
* node parser/index.js verify --oracle <o> --actual <a> # 渲染态逐点验收:computed style 期望 vs 实测 diff(G12 存在性闸 + 残差豁免 + 快照回归)
|
|
23
23
|
* node parser/index.js registry <registry.json> # 登记表通道核验:登记项 sid / dims 是否在词典有落点()
|
|
24
24
|
* node parser/watch.js # 开发态监听 mapping/ 自动重编译(§3.4.3)
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
import { readFileSync, statSync, readdirSync, existsSync } from "node:fs";
|
|
27
|
+
import { readFileSync, writeFileSync, statSync, readdirSync, existsSync } from "node:fs";
|
|
28
28
|
import path from "node:path";
|
|
29
29
|
import { fileURLToPath } from "node:url";
|
|
30
30
|
import { buildMappingDataset } from "./loader.js";
|
|
@@ -110,7 +110,9 @@ function printHelp() {
|
|
|
110
110
|
node parser/index.js match --batch <dir> 项目级批量匹配:目录下全部变更集 + 跨文件汇总
|
|
111
111
|
node parser/index.js audit --project <dir> [<cs.json…>] 挂载点存在性 + 死通道静态审计(需目标项目源码)
|
|
112
112
|
node parser/index.js verify --oracle <o.json> --actual <a.json> [--sids a,b]
|
|
113
|
-
渲染态逐点验收:computed style 期望 vs 实测 diff
|
|
113
|
+
渲染态逐点验收:computed style 期望 vs 实测 diff(oracle.absent 存在性闸 + residual_accepted 残差豁免)
|
|
114
|
+
node parser/index.js verify --oracle <o.json> --actual <a.json> --baseline <snap.json> [--snapshot <out.json>]
|
|
115
|
+
快照回归:与上轮快照比对基线漂移(上轮命中本轮退步 = 红);--snapshot 导出本轮快照
|
|
114
116
|
node parser/index.js verify --oracle <o.json> --resolve [--sids a,b]
|
|
115
117
|
实测端定位计划:sid → 词典 selector(项目页不打 data-sid 时按此抽取)
|
|
116
118
|
node parser/index.js verify … --from-theme 钴蓝 --to-theme 橘橙
|
|
@@ -1852,7 +1854,16 @@ function findStructEvidence(probe, sources) {
|
|
|
1852
1854
|
*
|
|
1853
1855
|
* CLI:node parser/index.js verify --oracle <oracle.json> --actual <actual.json> [--sids a,b,c]
|
|
1854
1856
|
* 实测端定位计划:node parser/index.js verify --oracle <oracle.json> --resolve [--sids a,b,c]
|
|
1855
|
-
*
|
|
1857
|
+
* 快照回归:… --snapshot <path> 导出本次实测 + 判定结果为快照(json),供下轮 --baseline 比对
|
|
1858
|
+
* … --baseline <path> 与上一轮快照比对:上轮命中、本轮缺口的属性 = 基线漂移(红),治「上一轮改对的这一轮漂回」
|
|
1859
|
+
* 退出码:存在缺口(含存在性缺口 G12 / 基线漂移)→ 1;全部命中 → 0。
|
|
1860
|
+
*
|
|
1861
|
+
* G12 存在性闸(oracle.absent):效果图上**不存在的**出厂点位(面包屑 / 设置铃铛等收口项)
|
|
1862
|
+
* 列入 oracle.absent: ["sid", …],verify 逐个断言「实测转储中不存在,或 display:none / visibility:hidden」。
|
|
1863
|
+
* 治「该消失的没消失」——这类缺口在效果图上无可见元素 → 无 data-sid → 无期望值,逐点 diff 永远验不到。
|
|
1864
|
+
*
|
|
1865
|
+
* 残差豁免(oracle.residual_accepted):机制推算值 / 业务资产等**申报过的固有残差**点位不计缺口,
|
|
1866
|
+
* 每条 { "sid": …, "reason": … },reason 必填——防止把真缺口装进接受清单蒙混过关。
|
|
1856
1867
|
*/
|
|
1857
1868
|
function cmdVerify(args, dataset) {
|
|
1858
1869
|
// 几何 / 盒模型属性:值对不上 = 语境错位(宿主盒 / 内层盒模型 / 层叠上下文不同)
|
|
@@ -1879,6 +1890,8 @@ function cmdVerify(args, dataset) {
|
|
|
1879
1890
|
const oraclePath = get("--oracle");
|
|
1880
1891
|
const actualPath = get("--actual");
|
|
1881
1892
|
const resolveOnly = args.includes("--resolve");
|
|
1893
|
+
const snapshotPath = get("--snapshot");
|
|
1894
|
+
const baselinePath = get("--baseline");
|
|
1882
1895
|
if (!oraclePath || (!resolveOnly && !actualPath)) {
|
|
1883
1896
|
console.error("✖ 缺少参数:node parser/index.js verify --oracle <oracle.json> --actual <actual.json> [--sids a,b,c]");
|
|
1884
1897
|
console.error(" 或输出实测端定位计划:node parser/index.js verify --oracle <oracle.json> --resolve [--sids a,b,c]");
|
|
@@ -1914,6 +1927,14 @@ function cmdVerify(args, dataset) {
|
|
|
1914
1927
|
};
|
|
1915
1928
|
|
|
1916
1929
|
const oracle = readDump(oraclePath, "效果图期望");
|
|
1930
|
+
// oracle 顶层扩展字段(readDump 摊平后丢顶层,这里从原始文件补读)
|
|
1931
|
+
const oracleRaw = JSON.parse(readFileSync(path.resolve(oraclePath), "utf-8"));
|
|
1932
|
+
const absentSids = Array.isArray(oracleRaw.absent) ? oracleRaw.absent.filter(s => typeof s === "string" && s.trim()) : [];
|
|
1933
|
+
const residualAccepted = Array.isArray(oracleRaw.residual_accepted)
|
|
1934
|
+
? oracleRaw.residual_accepted
|
|
1935
|
+
.map(r => (typeof r === "string" ? { sid: r, reason: "(未填 reason)" } : r))
|
|
1936
|
+
.filter(r => r && typeof r.sid === "string" && r.sid.trim())
|
|
1937
|
+
: [];
|
|
1917
1938
|
|
|
1918
1939
|
let sids = Object.keys(oracle);
|
|
1919
1940
|
if (sidFilter)
|
|
@@ -2009,11 +2030,21 @@ function cmdVerify(args, dataset) {
|
|
|
2009
2030
|
console.log(`效果图基线:${path.resolve(oraclePath)}`);
|
|
2010
2031
|
console.log(`项目实测:${path.resolve(actualPath)}`);
|
|
2011
2032
|
console.log(`比对点位:${sids.length}${sidFilter ? `(--sids 过滤后)` : "(oracle 全量)"}`);
|
|
2033
|
+
if (absentSids.length > 0)
|
|
2034
|
+
console.log(`存在性闸(G12):${absentSids.length} 点位应已收口 —— ${absentSids.join("、")}`);
|
|
2035
|
+
if (residualAccepted.length > 0)
|
|
2036
|
+
console.log(`残差豁免:${residualAccepted.length} 点位已申报接受 —— ${residualAccepted.map(r => r.sid).join("、")}`);
|
|
2037
|
+
|
|
2038
|
+
// 残差豁免索引:sid → reason。命中豁免的属性缺口降级为「已申报残差」提示行,不进 missProps。
|
|
2039
|
+
const residualMap = new Map(residualAccepted.map(r => [r.sid.trim(), r.reason || "(未填 reason)"]));
|
|
2012
2040
|
|
|
2013
2041
|
let hitProps = 0;
|
|
2014
2042
|
let missProps = 0;
|
|
2043
|
+
let residualProps = 0;
|
|
2015
2044
|
const attr = { selector_miss: 0, weight_override: 0, context_mismatch: 0 };
|
|
2016
2045
|
const clean = [];
|
|
2046
|
+
// 快照收集:sid → { 属性: { expected, actual, verdict } };verdict: hit | miss | residual | absent-hit | absent-miss | selector-miss
|
|
2047
|
+
const snapshot = { generated_at: new Date().toISOString(), oracle: path.resolve(oraclePath), actual: path.resolve(actualPath), points: {} };
|
|
2017
2048
|
|
|
2018
2049
|
for (const sid of sids) {
|
|
2019
2050
|
const exp = oracle[sid];
|
|
@@ -2026,6 +2057,7 @@ function cmdVerify(args, dataset) {
|
|
|
2026
2057
|
console.log(`\n✘ ${sid}\n - 项目页面未抽取到该点位 —— 选择器落空(挂载点缺失 / 元素未渲染 / data-sid 未标注)`);
|
|
2027
2058
|
attr.selector_miss++;
|
|
2028
2059
|
missProps++;
|
|
2060
|
+
snapshot.points[sid] = { verdict: "selector-miss" };
|
|
2029
2061
|
continue;
|
|
2030
2062
|
}
|
|
2031
2063
|
const act = {};
|
|
@@ -2033,16 +2065,19 @@ function cmdVerify(args, dataset) {
|
|
|
2033
2065
|
act[normProp(k)] = v;
|
|
2034
2066
|
|
|
2035
2067
|
const lines = [];
|
|
2068
|
+
const snapProps = {};
|
|
2036
2069
|
for (const [propRaw, expVal] of Object.entries(exp)) {
|
|
2037
2070
|
const prop = normProp(propRaw);
|
|
2038
2071
|
if (!(prop in act)) {
|
|
2039
2072
|
lines.push(` - ${prop}: 期望 ${expVal} / 实测未取到 —— 选择器落空(该属性所在规则未命中)`);
|
|
2040
2073
|
attr.selector_miss++;
|
|
2074
|
+
snapProps[prop] = { expected: expVal, actual: null, verdict: "selector-miss" };
|
|
2041
2075
|
continue;
|
|
2042
2076
|
}
|
|
2043
2077
|
const expNorm = themeAlias ? themeAlias(expVal) : expVal;
|
|
2044
2078
|
if (normValue(expNorm) === normValue(act[prop])) {
|
|
2045
2079
|
hitProps++;
|
|
2080
|
+
snapProps[prop] = { expected: expVal, actual: act[prop], verdict: "hit" };
|
|
2046
2081
|
continue;
|
|
2047
2082
|
}
|
|
2048
2083
|
const isGeom = GEOMETRY_PROPS.has(prop);
|
|
@@ -2051,6 +2086,14 @@ function cmdVerify(args, dataset) {
|
|
|
2051
2086
|
attr.context_mismatch++;
|
|
2052
2087
|
else
|
|
2053
2088
|
attr.weight_override++;
|
|
2089
|
+
snapProps[prop] = { expected: expVal, actual: act[prop], verdict: "miss", kind };
|
|
2090
|
+
if (residualMap.has(sid)) {
|
|
2091
|
+
// 申报过的固有残差:降级为提示行,不进缺口计数
|
|
2092
|
+
residualProps++;
|
|
2093
|
+
snapProps[prop].verdict = "residual";
|
|
2094
|
+
lines.push(` ○ ${prop}: 期望 ${expVal} / 实得 ${act[prop]} —— 已申报固有残差(${residualMap.get(sid)}),不计缺口`);
|
|
2095
|
+
continue;
|
|
2096
|
+
}
|
|
2054
2097
|
lines.push(` - ${prop}: 期望 ${expVal} / 实得 ${act[prop]} —— ${kind}(${
|
|
2055
2098
|
isGeom ? "几何值对不上:宿主盒 / 内层盒模型或层叠上下文不同" : "同权重被组件 scoped / 行内样式反压,或令牌未生效"
|
|
2056
2099
|
})`);
|
|
@@ -2059,21 +2102,107 @@ function cmdVerify(args, dataset) {
|
|
|
2059
2102
|
|
|
2060
2103
|
if (lines.length === 0) {
|
|
2061
2104
|
clean.push(sid);
|
|
2105
|
+
snapshot.points[sid] = { verdict: "hit", props: snapProps };
|
|
2062
2106
|
}
|
|
2063
2107
|
else {
|
|
2064
2108
|
console.log(`\n✘ ${sid}`);
|
|
2065
2109
|
for (const l of lines)
|
|
2066
2110
|
console.log(l);
|
|
2111
|
+
snapshot.points[sid] = { verdict: "miss", props: snapProps };
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
// ---- G12 存在性闸:oracle.absent 点位必须「不存在或已隐藏」----
|
|
2116
|
+
let absentMiss = 0;
|
|
2117
|
+
if (absentSids.length > 0) {
|
|
2118
|
+
console.log(`\n── 存在性闸(G12):以下点位效果图上不存在,页面必须收口 ──`);
|
|
2119
|
+
for (const sid of absentSids) {
|
|
2120
|
+
if (sidFilter && !sidFilter.has(sid))
|
|
2121
|
+
continue;
|
|
2122
|
+
const actRaw = actual[sid];
|
|
2123
|
+
let gone = true;
|
|
2124
|
+
let evidence = "页面无此元素(未抽取到)";
|
|
2125
|
+
if (actRaw && typeof actRaw === "object") {
|
|
2126
|
+
const disp = normValue(actRaw["display"] ?? actRaw["display_style"] ?? "");
|
|
2127
|
+
const vis = normValue(actRaw["visibility"] ?? "");
|
|
2128
|
+
if (disp === "none" || vis === "hidden") {
|
|
2129
|
+
evidence = `display=${disp || "-"} visibility=${vis || "-"}`;
|
|
2130
|
+
}
|
|
2131
|
+
else {
|
|
2132
|
+
gone = false;
|
|
2133
|
+
evidence = `仍渲染可见(display=${disp || "?"})`;
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
if (gone) {
|
|
2137
|
+
console.log(` ✔ ${sid} —— 已收口(${evidence})`);
|
|
2138
|
+
snapshot.points[sid] = { verdict: "absent-hit", evidence };
|
|
2139
|
+
}
|
|
2140
|
+
else {
|
|
2141
|
+
absentMiss++;
|
|
2142
|
+
console.log(` ✘ ${sid} —— ${evidence}:效果图无此元素,应 visible:false 收口(G12 存在性缺口)`);
|
|
2143
|
+
snapshot.points[sid] = { verdict: "absent-miss", evidence };
|
|
2144
|
+
}
|
|
2067
2145
|
}
|
|
2068
2146
|
}
|
|
2069
2147
|
|
|
2070
2148
|
if (clean.length > 0)
|
|
2071
2149
|
console.log(`\n✔ 逐点命中:${clean.join("、")}`);
|
|
2072
2150
|
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
if (
|
|
2076
|
-
|
|
2151
|
+
// ---- 快照回归:--snapshot 导出 / --baseline 漂移比对 ----
|
|
2152
|
+
let driftCount = 0;
|
|
2153
|
+
if (baselinePath) {
|
|
2154
|
+
let baseline;
|
|
2155
|
+
try {
|
|
2156
|
+
baseline = JSON.parse(readFileSync(path.resolve(baselinePath), "utf-8"));
|
|
2157
|
+
}
|
|
2158
|
+
catch (e) {
|
|
2159
|
+
console.error(`✖ 基线快照读取失败 ${baselinePath}:${e.message}`);
|
|
2160
|
+
process.exit(1);
|
|
2161
|
+
}
|
|
2162
|
+
const basePoints = baseline.points || {};
|
|
2163
|
+
const drifts = [];
|
|
2164
|
+
for (const [sid, cur] of Object.entries(snapshot.points)) {
|
|
2165
|
+
const prev = basePoints[sid];
|
|
2166
|
+
if (!prev)
|
|
2167
|
+
continue;
|
|
2168
|
+
// 点位级漂移:上轮命中/收口、本轮缺口
|
|
2169
|
+
const prevOk = prev.verdict === "hit" || prev.verdict === "absent-hit";
|
|
2170
|
+
const curOk = cur.verdict === "hit" || cur.verdict === "absent-hit";
|
|
2171
|
+
if (prevOk && !curOk) {
|
|
2172
|
+
drifts.push(`${sid}(${prev.verdict} → ${cur.verdict})`);
|
|
2173
|
+
}
|
|
2174
|
+
else if (prevOk && curOk && prev.props && cur.props) {
|
|
2175
|
+
for (const [prop, pv] of Object.entries(prev.props)) {
|
|
2176
|
+
if (pv.verdict === "hit" && cur.props[prop] && cur.props[prop].verdict !== "hit" && cur.props[prop].verdict !== "residual")
|
|
2177
|
+
drifts.push(`${sid}.${prop}(hit → ${cur.props[prop].verdict})`);
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
driftCount = drifts.length;
|
|
2182
|
+
if (driftCount > 0) {
|
|
2183
|
+
console.log(`\n✘ 基线漂移 ${driftCount} 处(上一轮已命中、本轮退步——每轮动工前先跑 verify 存快照,交付前比对):`);
|
|
2184
|
+
for (const d of drifts)
|
|
2185
|
+
console.log(` - ${d}`);
|
|
2186
|
+
}
|
|
2187
|
+
else {
|
|
2188
|
+
console.log(`\n✔ 基线快照比对:无漂移(上轮命中点位本轮全部保持)`);
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
if (snapshotPath) {
|
|
2192
|
+
try {
|
|
2193
|
+
writeFileSync(path.resolve(snapshotPath), JSON.stringify(snapshot, null, 2), "utf-8");
|
|
2194
|
+
console.log(`\n快照已导出:${path.resolve(snapshotPath)}(下轮验证带 --baseline <本文件> 比对漂移)`);
|
|
2195
|
+
}
|
|
2196
|
+
catch (e) {
|
|
2197
|
+
console.error(`✖ 快照写入失败 ${snapshotPath}:${e.message}`);
|
|
2198
|
+
process.exitCode = 1;
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
|
|
2202
|
+
console.log(`\n汇总:${sids.length} 点位 · ${hitProps} 属性命中 · ${missProps} 缺口${residualProps > 0 ? ` · ${residualProps} 已申报残差` : ""}${absentSids.length > 0 ? ` · 存在性 ${absentSids.length - absentMiss}/${absentSids.length} 收口` : ""}`);
|
|
2203
|
+
console.log(`归因:选择器落空 ${attr.selector_miss} / 权重被压 ${attr.weight_override} / 语境错位 ${attr.context_mismatch}${absentSids.length > 0 ? ` / 存在性缺口 ${absentMiss}` : ""}${driftCount > 0 ? ` / 基线漂移 ${driftCount}` : ""}`);
|
|
2204
|
+
if (missProps > 0 || absentMiss > 0 || driftCount > 0) {
|
|
2205
|
+
console.log(`处置口径:按归因修正 —— 选择器落空补挂载点(属能力缺口,走 structure_ops / 补通道并申报);权重被压按 priority 声明提权(禁止 !important 掩盖);语境错位校准 layer(host / inner);存在性缺口补 visible:false 收口并按词典 priority 提权;基线漂移逐条回归上轮修法。`);
|
|
2077
2206
|
process.exitCode = 1;
|
|
2078
2207
|
}
|
|
2079
2208
|
else {
|
package/parser/regression.js
CHANGED
|
@@ -24,10 +24,12 @@
|
|
|
24
24
|
* I build --dry 产物链自检 → exit 0 且输出含 dry-run
|
|
25
25
|
* J match 三声明维度 → exit 0 且输出含「结构施工 structure_ops」
|
|
26
26
|
* K audit 挂载点 / 死通道审计 → exit 1(夹具项目故意存在挂载点缺失 / 死通道)
|
|
27
|
-
* L verify 渲染态逐点验收 → exit 1
|
|
27
|
+
* L verify 渲染态逐点验收 → exit 1 且归因各计其数(夹具含故意缺口:三类渲染缺口 + G12 存在性缺口)
|
|
28
28
|
* M match 登记表覆盖率细闸 → exit 1(夹具故意欠声明两条恒存在子项)
|
|
29
29
|
* N registry 登记表通道核验 → exit 0 且输出含「登记表通道核验」
|
|
30
30
|
* O registry 硬缺口负例 → exit 1(引用了不存在且未标 pending 的点位)
|
|
31
|
+
* U verify 残差豁免 + 收口正例(clean 夹具)→ exit 0 且输出含「已申报残差」「已收口」(申报点位不计缺口、收口点位绿灯)
|
|
32
|
+
* V verify 快照回归(snapshot → baseline)→ clean 快照为基线、缺口夹具为实测 → exit 1 且输出含「基线漂移」(上轮命中本轮退步必红)
|
|
31
33
|
*
|
|
32
34
|
* 任一步骤失败 → 本脚本 exit 1,并回显该步骤完整输出便于定位。
|
|
33
35
|
*/
|
|
@@ -131,14 +133,13 @@ const steps = [
|
|
|
131
133
|
},
|
|
132
134
|
{
|
|
133
135
|
key: "L",
|
|
134
|
-
name: "verify
|
|
136
|
+
name: "verify 渲染态逐点验收(夹具含三类渲染缺口 + G12 存在性缺口 → exit 1)",
|
|
135
137
|
script: INDEX,
|
|
136
138
|
args: ["verify", "--oracle", path.join(FIXTURES, "verify", "oracle.json"), "--actual", path.join(FIXTURES, "verify", "actual.json")],
|
|
137
|
-
expect: { code: 1, includes: "选择器落空
|
|
139
|
+
expect: { code: 1, includes: "选择器落空 2 / 权重被压 1 / 语境错位 2 / 存在性缺口 1" },
|
|
138
140
|
},
|
|
139
141
|
{
|
|
140
142
|
key: "M",
|
|
141
|
-
name: "match 登记表覆盖率细闸(夹具故意欠声明两条恒存在子项 → exit 1)",
|
|
142
143
|
script: INDEX,
|
|
143
144
|
args: ["match", "--registry", path.join(FIXTURES, "registry-sample.json"), path.join(FIXTURES, "changeset-registry-gap.json")],
|
|
144
145
|
expect: { code: 1, includes: ["登记表覆盖率", "缺口 2"] },
|
|
@@ -192,19 +193,60 @@ const steps = [
|
|
|
192
193
|
args: ["match", path.join(FIXTURES, "changeset-neg8-g11-oracle.json")],
|
|
193
194
|
expect: { code: 1, includes: ["G11", "主题错配"] },
|
|
194
195
|
},
|
|
196
|
+
{
|
|
197
|
+
key: "U",
|
|
198
|
+
name: "verify 残差豁免 + 收口正例(clean 夹具:申报点位不计缺口、absent 已收口 → exit 0)",
|
|
199
|
+
script: INDEX,
|
|
200
|
+
args: ["verify", "--oracle", path.join(FIXTURES, "verify", "oracle.json"), "--actual", path.join(FIXTURES, "verify", "actual-clean.json")],
|
|
201
|
+
expect: { code: 0, includes: ["已申报固有残差", "已收口", "验收通过"] },
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
key: "V",
|
|
205
|
+
name: "verify 快照回归(clean 快照为基线 + L 缺口夹具为实测:上轮命中本轮退步 → exit 1 且含「基线漂移」)",
|
|
206
|
+
script: INDEX,
|
|
207
|
+
args: [], // 两段式:先 --snapshot 导出,再 --baseline 比对,见下方 custom 分支
|
|
208
|
+
expect: { code: 1, includes: "基线漂移" },
|
|
209
|
+
custom: "snapshot-drift",
|
|
210
|
+
},
|
|
195
211
|
];
|
|
196
212
|
|
|
197
213
|
const results = [];
|
|
198
214
|
let failed = 0;
|
|
199
215
|
|
|
200
|
-
console.log("=== A-
|
|
216
|
+
console.log("=== A-V 端到端一键回归 ===\n");
|
|
217
|
+
|
|
218
|
+
const SNAPSHOT_TMP = path.join(FIXTURES, "verify", "snapshot-latest.json");
|
|
201
219
|
|
|
202
220
|
for (const step of steps) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
[
|
|
206
|
-
|
|
207
|
-
|
|
221
|
+
// V 步两段式:① 用 clean 夹具导出「上一轮全绿」快照;② 用 L 缺口夹具 + --baseline 比对 → 应报基线漂移
|
|
222
|
+
const spawnArgs = step.custom === "snapshot-drift"
|
|
223
|
+
? []
|
|
224
|
+
: [step.script, ...step.args];
|
|
225
|
+
let status, stdout, stderr;
|
|
226
|
+
if (step.custom === "snapshot-drift") {
|
|
227
|
+
const s1 = spawnSync(process.execPath, [
|
|
228
|
+
INDEX, "verify",
|
|
229
|
+
"--oracle", path.join(FIXTURES, "verify", "oracle.json"),
|
|
230
|
+
"--actual", path.join(FIXTURES, "verify", "actual-clean.json"),
|
|
231
|
+
"--snapshot", SNAPSHOT_TMP,
|
|
232
|
+
], { cwd: ROOT, encoding: "utf-8" });
|
|
233
|
+
if (s1.status !== 0 || !existsSync(SNAPSHOT_TMP)) {
|
|
234
|
+
status = s1.status;
|
|
235
|
+
stdout = s1.stdout ?? "";
|
|
236
|
+
stderr = `快照导出段失败(exit ${s1.status}):快照回归前置不成立\n${s1.stderr ?? ""}`;
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
({ status, stdout, stderr } = spawnSync(process.execPath, [
|
|
240
|
+
INDEX, "verify",
|
|
241
|
+
"--oracle", path.join(FIXTURES, "verify", "oracle.json"),
|
|
242
|
+
"--actual", path.join(FIXTURES, "verify", "actual.json"),
|
|
243
|
+
"--baseline", SNAPSHOT_TMP,
|
|
244
|
+
], { cwd: ROOT, encoding: "utf-8" }));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
({ status, stdout, stderr } = spawnSync(process.execPath, spawnArgs, { cwd: ROOT, encoding: "utf-8" }));
|
|
249
|
+
}
|
|
208
250
|
const output = `${stdout ?? ""}${stderr ?? ""}`;
|
|
209
251
|
const code = status ?? -1;
|
|
210
252
|
|
|
@@ -234,4 +276,4 @@ if (failed > 0) {
|
|
|
234
276
|
console.error(`\n✖ ${failed} 步未通过,见上方详情`);
|
|
235
277
|
process.exit(1);
|
|
236
278
|
}
|
|
237
|
-
console.log("\n✔ A-
|
|
279
|
+
console.log("\n✔ A-W 全部通过,mapping/parser 可提交或发版");
|