From f57da4c74ee16300e735907bb22fa4aeffcdd284 Mon Sep 17 00:00:00 2001 From: HIsheR Date: Fri, 23 Oct 2026 18:46:53 +0800 Subject: [PATCH] =?UTF-8?q?iter(f03):=20=E6=96=B0=E5=A2=9E=E7=95=99?= =?UTF-8?q?=E7=97=95=E6=A0=BC=E5=BC=8F=E9=97=A8=E7=A6=81=20lint-iterations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 5 +++-- packages/tools/cli/bin/ua.js | 37 +++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 7696a19..e90901f 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,10 @@ "style": "node packages/tools/cli/bin/ua.js style", "index": "node packages/tools/cli/bin/ua.js index", "docs": "node packages/tools/cli/bin/ua.js index && node packages/tools/cli/bin/ua.js docs", - "check": "npm run build && npm run style && npm run typecheck && npm run smoke && npm run index && npm run verify", + "check": "npm run build && npm run style && npm run typecheck && npm run smoke && npm run index && npm run verify && npm run lint-iterations", "ua": "node packages/tools/cli/bin/ua.js", - "verify": "node packages/tools/cli/bin/ua.js verify" + "verify": "node packages/tools/cli/bin/ua.js verify", + "lint-iterations": "node packages/tools/cli/bin/ua.js lint-iterations" }, "devDependencies": { "typescript": "^5.6.0" diff --git a/packages/tools/cli/bin/ua.js b/packages/tools/cli/bin/ua.js index bedf6ea..ff28511 100644 --- a/packages/tools/cli/bin/ua.js +++ b/packages/tools/cli/bin/ua.js @@ -693,6 +693,38 @@ function cmdCommitLint(args) { console.log(`commit-lint OK (${type}${scope ? `(${scope})` : ''})`); } +/*** 迭代留痕校验:.ua/iterations.log 每行 JSON 字段与格式必须符合 ITERATION.md 规范 ***/ +function cmdLintIterations() { + const file = path.join(INDEX_DIR, 'iterations.log'); + if (!fs.existsSync(file)) return console.log('lint-iterations OK (no log)'); + const lines = fs.readFileSync(file, 'utf8').split(/\r?\n/).filter((l) => l.trim()); + const issues = []; + lines.forEach((line, i) => { + const tag = `iterations.log:${i + 1}`; + let o; + try { o = JSON.parse(line); } catch { issues.push(`${tag} 不是合法 JSON`); return; } + const keys = Object.keys(o); + if (keys.length !== 5 || !['ts', 'id', 'result', 'commit', 'summary'].every((k) => keys.includes(k))) { + issues.push(`${tag} 字段必须恰为 {ts,id,result,commit,summary}(当前 ${keys.join(',')})`); + } + if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+08:00$/.test(String(o.ts ?? '')) || Number.isNaN(Date.parse(o.ts))) { + issues.push(`${tag} ts 须为可解析的本地 +08:00 ISO 时间`); + } + if (!/^[a-z]\d{2,}$/.test(String(o.id ?? ''))) issues.push(`${tag} id 须为 backlog 条目 id`); + if (o.result !== 'done' && o.result !== 'aborted') issues.push(`${tag} result 须为 done|aborted`); + const commit = String(o.commit ?? ''); + if (o.result === 'done' && !/^[0-9a-f]{7,40}$/.test(commit)) issues.push(`${tag} done 条目 commit 须为 7-40 位哈希`); + if (o.result === 'aborted' && commit && !/^[0-9a-f]{7,40}$/.test(commit)) issues.push(`${tag} aborted 条目 commit 须为空或哈希`); + if (!String(o.summary ?? '').trim()) issues.push(`${tag} summary 不能为空`); + }); + if (issues.length) { + for (const it of issues) console.error('lint-iterations: ' + it); + console.error(`${issues.length} violation(s)`); + process.exit(1); + } + console.log(`lint-iterations OK (${lines.length} entries)`); +} + // ---- 入口 ---- const [cmd, ...args] = process.argv.slice(2); @@ -722,10 +754,13 @@ switch (cmd) { case 'commit-lint': cmdCommitLint(args); break; + case 'lint-iterations': + cmdLintIterations(); + break; case 'mcp': cmdMcp(); break; default: - console.log('usage: ua |docs|style|verify|commit-lint|mcp>'); + console.log('usage: ua |docs|style|verify|commit-lint|lint-iterations|mcp>'); process.exit(cmd ? 1 : 0); }