iter(g163): 留痕校验补编号与时间线约束
This commit is contained in:
@@ -27,7 +27,7 @@
|
|||||||
- `apps/demo`(@ui-arch/demo):交互演示,产物 apps/demo/dist/demo.html
|
- `apps/demo`(@ui-arch/demo):交互演示,产物 apps/demo/dist/demo.html
|
||||||
- `apps/docs`:文档站,纯产物(ua docs 生成)
|
- `apps/docs`:文档站,纯产物(ua docs 生成)
|
||||||
|
|
||||||
**门禁一条龙**:`npm run check` = build + demo + style + typecheck + smoke + index + verify,提交前必过。`ua verify` 校验清单 ↔ 文档 ↔ 演示一致(docs.components 的 id 必须在 api.exports 有对应条目、props 必须带 n/t/d、每个文档字段名必须在组件实现源码里真实出现(改名后忘改文档会直接报错)、反向也校验 `src/types.ts` 里 `<组件>Props`(可用清单的 `propsType` 覆盖接口名,`false` 表示无入参)每个字段都在文档 props 表登记(新加字段忘了写文档会报错)、demo 锚点必须在 `apps/demo/main.ts` 中存在,且该组件必须被 demo 真实调用 `id(` / `id.`),检查皮肤包样式表无硬编码颜色(`#hex` 一律禁止,必须走 `--ua-*` 令牌)与令牌引用有效(`var(--ua-*)` 必须是 tokens.css 已定义或组件自设的变量)、核对每个 `--ua-color-*` 都在 `[data-theme='dark']` 里有覆盖值且暗色块没有凭空多出的变量名(新增颜色令牌漏写暗色值直接报错),核对 README 的 `<!-- ua:components -->` 区块与「分组:」行都覆盖清单组件集合(漏写/多写/重复都报错),并交叉校验 backlog 的 done 条目与 `.ua/iterations.log` 的 done 留痕一一对应。
|
**门禁一条龙**:`npm run check` = build + demo + style + typecheck + smoke + index + verify,提交前必过。`ua verify` 校验清单 ↔ 文档 ↔ 演示一致(docs.components 的 id 必须在 api.exports 有对应条目、props 必须带 n/t/d、每个文档字段名必须在组件实现源码里真实出现(改名后忘改文档会直接报错)、反向也校验 `src/types.ts` 里 `<组件>Props`(可用清单的 `propsType` 覆盖接口名,`false` 表示无入参)每个字段都在文档 props 表登记(新加字段忘了写文档会报错)、demo 锚点必须在 `apps/demo/main.ts` 中存在,且该组件必须被 demo 真实调用 `id(` / `id.`),检查皮肤包样式表无硬编码颜色(`#hex` 一律禁止,必须走 `--ua-*` 令牌)与令牌引用有效(`var(--ua-*)` 必须是 tokens.css 已定义或组件自设的变量)、核对每个 `--ua-color-*` 都在 `[data-theme='dark']` 里有覆盖值且暗色块没有凭空多出的变量名(新增颜色令牌漏写暗色值直接报错),核对 README 的 `<!-- ua:components -->` 区块与「分组:」行都覆盖清单组件集合(漏写/多写/重复都报错),并交叉校验 backlog 的 done 条目与 `.ua/iterations.log` 的 done 留痕一一对应(另外查 backlog id 不重复、g 编号严格递增、同一 id 只有一条 done 留痕,且留痕 ts 带 +08:00、时间线不回退、不落在未来——都是过去手工插条目/手写时间戳踩过的坑)。
|
||||||
|
|
||||||
**变更日志**:`CHANGELOG.md` 由 git 提交史生成(`npm run changelog`),**不进 `npm run check`**——它包含每个提交自身,提交后必然与历史差一条,只能作为「发版前刷新」步骤;`npm run changelog:check` 用于确认是否漂移。
|
**变更日志**:`CHANGELOG.md` 由 git 提交史生成(`npm run changelog`),**不进 `npm run check`**——它包含每个提交自身,提交后必然与历史差一条,只能作为「发版前刷新」步骤;`npm run changelog:check` 用于确认是否漂移。
|
||||||
|
|
||||||
|
|||||||
@@ -1030,12 +1030,31 @@ function checkIterations() {
|
|||||||
const items = JSON.parse(fs.readFileSync(backlogPath, 'utf8')).items ?? [];
|
const items = JSON.parse(fs.readFileSync(backlogPath, 'utf8')).items ?? [];
|
||||||
const ids = new Set(items.map((i) => i.id));
|
const ids = new Set(items.map((i) => i.id));
|
||||||
const done = new Set(); // 有 done 留痕的条目 id
|
const done = new Set(); // 有 done 留痕的条目 id
|
||||||
|
const seen = new Set(); // 条目 id 去重集合:复制粘贴条目忘了改 id 时会撞上
|
||||||
|
for (const item of items) {
|
||||||
|
if (seen.has(item.id)) issues.push(`backlog: id ${item.id} 重复(多半是复制条目时忘了改 id)`);
|
||||||
|
seen.add(item.id);
|
||||||
|
}
|
||||||
|
const seq = items.map((i) => (typeof i.id === 'string' && /^g\d+$/.test(i.id) ? Number(i.id.slice(1)) : NaN)); // g 编号序列
|
||||||
|
for (let k = 1; k < seq.length; k++) {
|
||||||
|
if (Number.isNaN(seq[k]) || Number.isNaN(seq[k - 1])) continue; // 非 g 编号的条目不参与顺序校验
|
||||||
|
if (seq[k] <= seq[k - 1]) issues.push(`backlog: ${items[k].id} 排在 ${items[k - 1].id} 后面(编号必须严格递增,逆序一般是插错了位置)`);
|
||||||
|
}
|
||||||
|
let prevTs = ''; // 上一条留痕时间,用于单调性比对
|
||||||
for (const line of fs.readFileSync(logPath, 'utf8').split(/\r?\n/)) {
|
for (const line of fs.readFileSync(logPath, 'utf8').split(/\r?\n/)) {
|
||||||
if (!line.trim()) continue; // 空行跳过
|
if (!line.trim()) continue; // 空行跳过
|
||||||
let row;
|
let row;
|
||||||
try { row = JSON.parse(line); } catch { continue; } // 行格式归 lint-iterations 管,这里不重复报
|
try { row = JSON.parse(line); } catch { continue; } // 行格式归 lint-iterations 管,这里不重复报
|
||||||
if (!ids.has(row.id)) issues.push(`iterations.log: 留痕 id ${row.id} 在 backlog 中不存在`);
|
if (!ids.has(row.id)) issues.push(`iterations.log: 留痕 id ${row.id} 在 backlog 中不存在`);
|
||||||
if (row.result === 'done') done.add(row.id);
|
if (row.result === 'done') {
|
||||||
|
if (done.has(row.id)) issues.push(`iterations.log: ${row.id} 有两条 done 留痕(一条迭代只该留一次痕迹)`);
|
||||||
|
done.add(row.id);
|
||||||
|
}
|
||||||
|
const ts = String(row.ts ?? '');
|
||||||
|
if (!/\+08:00$/.test(ts)) issues.push(`iterations.log: ${row.id} 的 ts 缺少 +08:00 偏移(必须取 date 实测的本地时间)`);
|
||||||
|
else if (prevTs && ts < prevTs) issues.push(`iterations.log: ${row.id} 的 ts 早于上一条(时间倒流,多半是手写或按 UTC 换算)`);
|
||||||
|
else if (Date.parse(ts) > Date.now() + 300000) issues.push(`iterations.log: ${row.id} 的 ts 落在未来(超出 5 分钟容差)`);
|
||||||
|
prevTs = ts;
|
||||||
}
|
}
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
if (item.status === 'done' && !done.has(item.id)) issues.push(`backlog: ${item.id} 已置 done 但 iterations.log 无 done 留痕`);
|
if (item.status === 'done' && !done.has(item.id)) issues.push(`backlog: ${item.id} 已置 done 但 iterations.log 无 done 留痕`);
|
||||||
@@ -1219,7 +1238,7 @@ function cmdVerify() {
|
|||||||
console.error(`${issues.length} violation(s)`);
|
console.error(`${issues.length} violation(s)`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
console.log('verify OK (manifest ↔ docs ↔ demo 一致,文档 props ↔ 入参接口双向一致,皮肤无硬编码颜色与未定义令牌、暗色令牌齐全,README 组件清单与分组同步,backlog ↔ 留痕一致,组合配方结构完整,MCP 工具清单三处一致)');
|
console.log('verify OK (manifest ↔ docs ↔ demo 一致,文档 props ↔ 入参接口双向一致,皮肤无硬编码颜色与未定义令牌、暗色令牌齐全,README 组件清单与分组同步,backlog ↔ 留痕一致(编号严格递增、无重复 id、留痕时间线单调且带 +08:00),组合配方结构完整,MCP 工具清单三处一致)');
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** 校验 git 提交信息是否符合 COMMITTING.md 规范(参数为 message 文件路径或直接 message 文本),违规退出码 1 ***/
|
/*** 校验 git 提交信息是否符合 COMMITTING.md 规范(参数为 message 文件路径或直接 message 文本),违规退出码 1 ***/
|
||||||
|
|||||||
Reference in New Issue
Block a user