iter(g186): 固化视觉回归抽查清单

This commit is contained in:
HIsheR
2026-10-26 17:47:50 +08:00
parent 85ae90a5af
commit 5908202cd7
7 changed files with 110 additions and 7 deletions
+48 -1
View File
@@ -1235,6 +1235,52 @@ function checkPropsDoc(pkg, comps) {
return issues;
}
/*** 视觉回归抽查清单校验:ITERATION.md 的清单必须六个高频组件齐全、每条带合法维度标签、条目数达标 ***/
function checkVisualChecklist() {
const DIMS = ['边框', '阴影', '过渡', '焦点环', '暗色']; // 五个必覆盖维度
const REQUIRED = ['button', 'textField', 'select', 'modal', 'dataTable', 'toast']; // 六个代表组件
const MIN_PER_GROUP = 3; // 每个组件至少三条可勾选要点
const MIN_TOTAL = 18; // 清单总条目下限
const file = path.join(ROOT, 'ITERATION.md');
if (!fs.existsSync(file)) return ['视觉回归抽查清单:找不到 ITERATION.md'];
const lines = fs.readFileSync(file, 'utf8').replace(/\r\n/g, '\n').split('\n');
const start = lines.findIndex((l) => /^##\s+视觉回归抽查清单/.test(l));
if (start === -1) return ['视觉回归抽查清单:ITERATION.md 缺少《视觉回归抽查清单》章节'];
const rest = lines.slice(start + 1);
const stop = rest.findIndex((l) => /^##\s+/.test(l)); // 下一个二级标题即章节结束
const body = stop === -1 ? rest : rest.slice(0, stop);
const issues = [];
const groups = new Map(); // 组件 id → 该段的维度标签列表
let current = null;
for (const line of body) {
const head = line.match(/^###\s+(\S+)/);
if (head) {
current = head[1];
if (!groups.has(current)) groups.set(current, []);
continue;
}
const item = line.match(/^-\s+\[[ x]\]\s+【([^】]+)】/);
if (!item) continue;
if (!current) {
issues.push(`视觉回归抽查清单:条目「${line.trim().slice(0, 24)}」没有归到任何 ### 组件段下`);
continue;
}
if (!DIMS.includes(item[1])) issues.push(`视觉回归抽查清单:${current} 的条目维度「${item[1]}」不在五类之内(${DIMS.join(' / ')})`);
groups.get(current).push(item[1]);
}
for (const id of REQUIRED) {
const items = groups.get(id);
if (!items) issues.push(`视觉回归抽查清单:缺少 ### ${id} 段`);
else if (items.length < MIN_PER_GROUP) issues.push(`视觉回归抽查清单:${id} 只有 ${items.length} 条要点,至少 ${MIN_PER_GROUP} 条`);
}
const total = [...groups.values()].reduce((sum, items) => sum + items.length, 0);
if (total < MIN_TOTAL) issues.push(`视觉回归抽查清单:合计 ${total} 条要点,至少 ${MIN_TOTAL} 条`);
const covered = new Set([...groups.values()].flat());
const missed = DIMS.filter((d) => !covered.has(d));
if (missed.length) issues.push(`视觉回归抽查清单:维度未覆盖 ${missed.join(' / ')}(五类都要有)`);
return issues;
}
/*** 清单一致性校验:组件文档块的 id / props 说明 / demo 锚点必须与代码和演示产物对齐 ***/
function cmdVerify() {
const issues = [];
@@ -1290,13 +1336,14 @@ function cmdVerify() {
checkDarkTokens(issues);
checkLlmsFull(pkgs, issues);
issues.push(...checkIterations());
issues.push(...checkVisualChecklist());
if (issues.length) {
for (const it of issues) console.error('verify: ' + it);
console.error(`${issues.length} violation(s)`);
process.exit(1);
}
console.log('verify OK (manifest ↔ docs ↔ demo 一致,文档 props ↔ 入参接口双向一致,皮肤无硬编码颜色与未定义令牌、暗色令牌齐全,README 组件清单与分组同步,backlog ↔ 留痕一致(编号严格递增、无重复 id、留痕时间线单调且带 +08:00),组合配方结构完整,MCP 工具清单三处一致)');
console.log('verify OK (manifest ↔ docs ↔ demo 一致,文档 props ↔ 入参接口双向一致,皮肤无硬编码颜色与未定义令牌、暗色令牌齐全,README 组件清单与分组同步,backlog ↔ 留痕一致(编号严格递增、无重复 id、留痕时间线单调且带 +08:00),组合配方结构完整,MCP 工具清单三处一致,视觉回归抽查清单六组件 × 五维度齐全)');
}
/*** 校验 git 提交信息是否符合 COMMITTING.md 规范(参数为 message 文件路径或直接 message 文本),违规退出码 1 ***/