agentic-flow-kit 1.5.0 → 1.7.0
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/modules/hosts/claude/commands/wf-build.md +7 -0
- package/modules/hosts/claude/commands/wf-design.md +6 -0
- package/modules/hosts/claude/commands/wf-new-task.md +2 -0
- package/modules/hosts/codex/commands/wf-build.md +7 -0
- package/modules/hosts/codex/commands/wf-design.md +6 -0
- package/modules/hosts/codex/commands/wf-new-task.md +2 -0
- package/modules/hosts/cursor/commands/wf-build.md +7 -0
- package/modules/hosts/cursor/commands/wf-design.md +6 -0
- package/modules/hosts/cursor/commands/wf-new-task.md +2 -0
- package/modules/hosts/opencode/commands/wf-build.md +7 -0
- package/modules/hosts/opencode/commands/wf-design.md +6 -0
- package/modules/hosts/opencode/commands/wf-new-task.md +2 -0
- package/modules/hosts/trae/commands/wf-build.md +7 -0
- package/modules/hosts/trae/commands/wf-design.md +6 -0
- package/modules/hosts/trae/commands/wf-new-task.md +2 -0
- package/package.json +1 -1
- package/src/run-tests.mjs +16 -9
- package/src/sync-hosts.mjs +100 -62
- package/src/sync-hosts.test.mjs +39 -0
- package/templates/AGENTS.md +2 -1
- package/templates/_agents/commands/build.md +7 -0
- package/templates/_agents/commands/design.md +6 -0
- package/templates/_agents/commands/new-task.md +2 -0
- package/templates/_agents/scripts/check-ledger-invariant.mjs +18 -0
- package/templates/_agents/scripts/check-ledger-invariant.test.mjs +16 -0
- package/templates/_agents/scripts/check-loop.mjs +122 -28
- package/templates/_agents/scripts/check-loop.test.mjs +121 -0
- package/templates/_agents/scripts/check-stage-index.mjs +40 -0
- package/templates/_agents/scripts/check-stage-index.test.mjs +77 -0
- package/templates/_agents/scripts/confirm-doc.mjs +52 -3
- package/templates/_agents/scripts/confirm-doc.test.mjs +51 -1
- package/templates/_agents/scripts/edit-face-check.mjs +107 -0
- package/templates/_agents/scripts/edit-face-check.test.mjs +98 -0
- package/templates/_agents/scripts/fill-intent.mjs +32 -3
- package/templates/_agents/scripts/fill-intent.test.mjs +13 -0
- package/templates/_agents/scripts/gen-workflow-metrics.mjs +52 -1
- package/templates/_agents/scripts/policy.mjs +62 -0
- package/templates/_agents/scripts/verify-wiki-consistency.test.mjs +81 -0
- package/templates/_agents/scripts/verify.mjs +11 -3
- package/templates/_agents/scripts/verify.test.mjs +30 -3
- package/templates/_agents/scripts/wiki-search.test.mjs +58 -0
- package/templates/_agents/scripts-test-exempt.txt +2 -3
- package/templates/_github/workflows/kit-ci.yml +21 -1
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// verify-wiki-consistency.test.mjs — wiki 三方一致性验证(2026-10-09 engine-quality-round2 A2 补测)
|
|
2
|
+
// 此前 exempt 表登记「无覆盖」——本套件补齐后移出豁免。三场景:三方一致(绿)/ 磁盘多文件未登记(红)/
|
|
3
|
+
// drafts-archive 协议目录缺失(红)。脚本自足(仅 node 标准库 + cwd 相对读 wiki/),fixture 子进程跑真脚本。
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
|
|
10
|
+
const SRC = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
let pass = 0, fail = 0;
|
|
12
|
+
const check = (name, cond, detail = '') => {
|
|
13
|
+
if (cond) { pass++; console.log(`PASS ${name}`); }
|
|
14
|
+
else { fail++; console.log(`FAIL ${name}${detail ? `——${detail}` : ''}`); }
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function mkWiki({ extraDiskFile = false, noArchive = false } = {}) {
|
|
18
|
+
const fx = fs.mkdtempSync(path.join(os.tmpdir(), 'vwc-'));
|
|
19
|
+
fs.mkdirSync(path.join(fx, 'wiki', '主题A'), { recursive: true });
|
|
20
|
+
fs.writeFileSync(path.join(fx, 'wiki', '主题A', '文档1.md'), '# 文档1\n\n无外链。\n');
|
|
21
|
+
if (extraDiskFile) fs.writeFileSync(path.join(fx, 'wiki', '主题A', '文档2.md'), '# 文档2\n');
|
|
22
|
+
if (!noArchive) fs.mkdirSync(path.join(fx, 'wiki', 'drafts-archive'), { recursive: true });
|
|
23
|
+
fs.writeFileSync(path.join(fx, 'wiki', 'INDEX.md'), [
|
|
24
|
+
'# Wiki 索引',
|
|
25
|
+
'',
|
|
26
|
+
'## 主题目录速览',
|
|
27
|
+
'',
|
|
28
|
+
'| 主题 | 文件数 | 用途 |',
|
|
29
|
+
'|---|---|---|',
|
|
30
|
+
'| 主题A | 1 | 测试主题 |',
|
|
31
|
+
'',
|
|
32
|
+
'## 映射表',
|
|
33
|
+
'',
|
|
34
|
+
'| 文件 | 用途 | 路径 |',
|
|
35
|
+
'|---|---|---|',
|
|
36
|
+
'| 文档1.md | 测试 | wiki/主题A/文档1.md |',
|
|
37
|
+
'',
|
|
38
|
+
`合计:**1 份**知识文档,**1 个主题**`,
|
|
39
|
+
'',
|
|
40
|
+
].join('\n'));
|
|
41
|
+
const DATA = {
|
|
42
|
+
topics: [{ name: '主题A', files: [{ file: '文档1.md', dir: '主题A' }] }],
|
|
43
|
+
summary: { total: 1, files: 1, topics: 1, archive: 0 },
|
|
44
|
+
};
|
|
45
|
+
fs.writeFileSync(path.join(fx, 'wiki', '知识沉淀总览.html'),
|
|
46
|
+
'<!doctype html><script>\nconst DATA = ' + JSON.stringify(DATA, null, 2) + ';\n</script>\n');
|
|
47
|
+
return fx;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const run = (fx) => spawnSync(process.execPath, [path.join(SRC, 'verify-wiki-consistency.mjs')], {
|
|
51
|
+
cwd: fx, encoding: 'utf8',
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
{
|
|
55
|
+
// ① 三方一致 → exit 0
|
|
56
|
+
const fx = mkWiki();
|
|
57
|
+
const r = run(fx);
|
|
58
|
+
check('① 三方一致 → exit 0', r.status === 0, `status=${r.status}\n${(r.stdout || '') + (r.stderr || '')}`.slice(0, 500));
|
|
59
|
+
fs.rmSync(fx, { recursive: true, force: true });
|
|
60
|
+
}
|
|
61
|
+
{
|
|
62
|
+
// ② 磁盘多文件未登记 → exit 1(合计行 fail-loud 最先拦——INDEX 合计 1 份 vs 磁盘 2 份;
|
|
63
|
+
// 该检查在脚本内先于三方文件比对出账,是同问题的合法拦截路径)
|
|
64
|
+
const fx = mkWiki({ extraDiskFile: true });
|
|
65
|
+
const r = run(fx);
|
|
66
|
+
const out = (r.stdout || '') + (r.stderr || '');
|
|
67
|
+
check('② 磁盘多文件未登记 → exit 1 且合计行拦(INDEX 1 份 vs 磁盘 2 份)',
|
|
68
|
+
r.status === 1 && out.includes('INDEX 合计行与磁盘不一致'), `status=${r.status}\n${out.slice(0, 500)}`);
|
|
69
|
+
fs.rmSync(fx, { recursive: true, force: true });
|
|
70
|
+
}
|
|
71
|
+
{
|
|
72
|
+
// ③ drafts-archive 协议目录缺失 → exit 1(门禁明示失败而非崩栈,2026-09-24 口径)
|
|
73
|
+
const fx = mkWiki({ noArchive: true });
|
|
74
|
+
const r = run(fx);
|
|
75
|
+
const out = (r.stdout || '') + (r.stderr || '');
|
|
76
|
+
check('③ drafts-archive 缺失 → exit 1 且明示', r.status === 1 && out.includes('drafts-archive'), `status=${r.status}\n${out.slice(0, 400)}`);
|
|
77
|
+
fs.rmSync(fx, { recursive: true, force: true });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log(`\n合计: PASS ${pass} / FAIL ${fail}`);
|
|
81
|
+
process.exit(fail ? 1 : 0);
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
// 一行机器事实 {ts, exitCode:0, suite, passed?, failed?, runId?}——append-only 事实记录,不属裁决
|
|
10
10
|
// (窗口判定/门禁在消费方 confirm-doc 与 check-loop 检查 8);消费语义 = 「按门跑了 verify 且全绿」,
|
|
11
11
|
// 裸跑 npm test 不落账(对齐引导)。npm test 步骤输出从 inherit 改 pipe 捕获后回放(延迟显示,
|
|
12
|
-
//
|
|
12
|
+
// 关单场景可接受)——仅 best-effort 抓编排器权威汇总行 `[run-tests] 合计: SUITES n / FAILED m`,
|
|
13
|
+
// 抓不到则省略计数键(宁缺勿错:exitCode 才是硬事实,计数缺失不阻断)。
|
|
13
14
|
// 装户仓(根目录无 package.json,工程在子目录)下默认 npm test 恒 ENOENT(exit 4058)→ **显式跳过步骤 1**
|
|
14
15
|
// 且**不落测试绿凭证**(凭证语义 = 测试真跑绿,跳过即无凭证,防假绿),并提示用
|
|
15
16
|
// --test-cmd "<项目测试命令>" 指定;显式 --test-cmd 不受此判定影响(2026-10-07 papercut)。
|
|
@@ -84,8 +85,15 @@ try {
|
|
|
84
85
|
if (skipTest) {
|
|
85
86
|
console.log('[verify] ⚠️ 步骤 1 已跳过——本次不落测试绿凭证(凭证语义 = 测试真跑绿)');
|
|
86
87
|
} else {
|
|
87
|
-
|
|
88
|
+
// 计数行**只认编排器权威前缀**(2026-10-09 审查 P0-2):此前是 pop 通配「合计: PASS n / FAIL n」,
|
|
89
|
+
// 而全仓 45+ 套件每个都打印自己那行同形文本 → 取到的是字母序最后一个套件的场景数
|
|
90
|
+
// (本仓实况:workflows-check.test.mjs 的 15),落进「一行机器事实」的 passed 字段。
|
|
91
|
+
// 改为精确匹配 run-tests.mjs 的专属行 + 套件口径:嵌套套件同形行不再被误取,
|
|
92
|
+
// 非本编排器口径的 --test-cmd(装户项目自有测试命令)抓不到即省略计数键,不回退到错的事实。
|
|
93
|
+
const m = [...testStdout.matchAll(/\[run-tests\] 合计: SUITES (\d+) \/ FAILED (\d+)/g)].pop();
|
|
88
94
|
const entry = { ts: new Date().toISOString(), exitCode: 0, suite: 'npm test' };
|
|
95
|
+
// passed/failed 口径 = **套件**(非断言、非场景):凭证明此记录的是「跑过多少套、挂了几套」。
|
|
96
|
+
// 步骤 1 红已在上面 fail-fast 退出,故落账行 failed 恒为 0。
|
|
89
97
|
if (m) { entry.passed = Number(m[1]); entry.failed = Number(m[2]); }
|
|
90
98
|
if (process.env.PIPELINE_RUN_ID) entry.runId = process.env.PIPELINE_RUN_ID;
|
|
91
99
|
// doc 键:未传 --doc 时**不写该键**(不是写 null)——消费方按 doc 精确匹配,undefined 与 null 都恒不命中,
|
|
@@ -93,7 +101,7 @@ try {
|
|
|
93
101
|
if (docRel) entry.doc = docRel;
|
|
94
102
|
fs.mkdirSync(path.join(ROOT, '.agents'), { recursive: true });
|
|
95
103
|
fs.appendFileSync(path.join(ROOT, '.agents', 'verifications.jsonl'), `${JSON.stringify(entry)}\n`);
|
|
96
|
-
console.log(`[verify] 🧾 测试绿凭证已落账 verifications.jsonl${entry.passed !== undefined ?
|
|
104
|
+
console.log(`[verify] 🧾 测试绿凭证已落账 verifications.jsonl${entry.passed !== undefined ? `(套件 ${entry.passed} / FAIL ${entry.failed})` : ''}${docRel ? ` 绑定本单 ${docRel}` : '(未带 --doc:不给任何单背书)'}`);
|
|
97
105
|
}
|
|
98
106
|
} catch (e) {
|
|
99
107
|
console.error(`[verify] ⚠️ 凭证落账失败(${e.code || e.message})——不阻断,关单门前置将按无凭证告警`);
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
// 断言:①绿路径(假过 + 空夹具闭环)exit 0;②步骤 1 假败 exit 1 且不出全绿;
|
|
4
4
|
// ③步骤 2 配对断裂夹具 exit 1(verify 自身标记断言——孙进程 stdio inherit 输出不进本测试管道);
|
|
5
5
|
// ④⑤⑥凭证落账三态(绿落 / check-loop 红不落 / 无汇总行省略计数键);
|
|
6
|
-
// ⑦装户仓(cwd 无 package.json)未给 --test-cmd → 跳过步骤 1、不落凭证、仍跑步骤 2、exit 0
|
|
6
|
+
// ⑦装户仓(cwd 无 package.json)未给 --test-cmd → 跳过步骤 1、不落凭证、仍跑步骤 2、exit 0;
|
|
7
|
+
// ⑧--doc 绑定五态;⑨⑩嵌套套件同形汇总行不得被误取(计数口径=编排器权威套件数,2026-10-09 审查 P0-2 回归锁)。
|
|
7
8
|
// 用法:node templates/_agents/scripts/verify.test.mjs
|
|
8
9
|
import fs from 'node:fs';
|
|
9
10
|
import os from 'node:os';
|
|
@@ -86,7 +87,7 @@ const run = (argv, opts = {}) => spawnSync(process.execPath, [VERIFY, ...argv],
|
|
|
86
87
|
// ---- 场景 4:绿路径凭证落账(2026-10-07 verify-evidence)——全绿后 fixture 台账出合法绿行 ----
|
|
87
88
|
{
|
|
88
89
|
const root = mkfix();
|
|
89
|
-
const r = run(['--test-cmd', `node ${mkCmd(0, '合计:
|
|
90
|
+
const r = run(['--test-cmd', `node ${mkCmd(0, '[run-tests] 合计: SUITES 3 / FAILED 0\n')}`], { env: { ...process.env, CHECK_LOOP_ROOT: root } });
|
|
90
91
|
const vp = path.join(root, '.agents', 'verifications.jsonl');
|
|
91
92
|
const line = fs.existsSync(vp) ? JSON.parse(fs.readFileSync(vp, 'utf8').trim()) : null;
|
|
92
93
|
check('场景 4:全绿 → verifications.jsonl 落绿行(exitCode 0 + 计数捕获)',
|
|
@@ -146,7 +147,7 @@ const run = (argv, opts = {}) => spawnSync(process.execPath, [VERIFY, ...argv],
|
|
|
146
147
|
|
|
147
148
|
const rootA = mkfix();
|
|
148
149
|
const rel = 'workflow/intents/2026-10-08-demo.md';
|
|
149
|
-
const rA = run(['--test-cmd', `node ${mkCmd(0, '合计:
|
|
150
|
+
const rA = run(['--test-cmd', `node ${mkCmd(0, '[run-tests] 合计: SUITES 3 / FAILED 0\n')}`, '--doc', rel],
|
|
150
151
|
{ env: { ...process.env, CHECK_LOOP_ROOT: rootA } });
|
|
151
152
|
const eA = readLast(rootA);
|
|
152
153
|
check('场景 8①:带 --doc → 末行含 doc 且为 posix 相对路径',
|
|
@@ -185,6 +186,32 @@ const run = (argv, opts = {}) => spawnSync(process.execPath, [VERIFY, ...argv],
|
|
|
185
186
|
fs.rmSync(bare, { recursive: true, force: true });
|
|
186
187
|
}
|
|
187
188
|
|
|
189
|
+
// ---- 场景 9 / 10:嵌套套件同形汇总行不得被误取(2026-10-09 审查 P0-2 回归锁)----
|
|
190
|
+
// 缺陷复现:旧判据 pop 通配「合计: PASS n / FAIL n」,而全仓 45+ 套件每个都打印自己那行同形文本,
|
|
191
|
+
// 取到的是字母序最后一个套件的场景数(本仓实况 workflows-check.test.mjs = 15),落进凭证 passed。
|
|
192
|
+
// ⑨ 权威行与同形行并存 → 取权威套件数;⑩ 仅有同形行 → 省略计数键(宁缺勿错,不回退到错的事实)。
|
|
193
|
+
{
|
|
194
|
+
const root9 = mkfix();
|
|
195
|
+
const r9 = run(['--test-cmd', `node ${mkCmd(0, '合计: PASS 15 / FAIL 0\n[run-tests] 合计: SUITES 45 / FAILED 0\n')}`],
|
|
196
|
+
{ env: { ...process.env, CHECK_LOOP_ROOT: root9 } });
|
|
197
|
+
const vp9 = path.join(root9, '.agents', 'verifications.jsonl');
|
|
198
|
+
const l9 = fs.existsSync(vp9) ? JSON.parse(fs.readFileSync(vp9, 'utf8').trim()) : null;
|
|
199
|
+
check('场景 9:嵌套套件同形行不被误取,计数取编排器权威套件数',
|
|
200
|
+
r9.status === 0 && l9 !== null && l9.passed === 45 && l9.failed === 0,
|
|
201
|
+
`line=${JSON.stringify(l9)}`);
|
|
202
|
+
fs.rmSync(root9, { recursive: true, force: true });
|
|
203
|
+
|
|
204
|
+
const root10 = mkfix();
|
|
205
|
+
const r10 = run(['--test-cmd', `node ${mkCmd(0, '合计: PASS 15 / FAIL 0\n')}`],
|
|
206
|
+
{ env: { ...process.env, CHECK_LOOP_ROOT: root10 } });
|
|
207
|
+
const vp10 = path.join(root10, '.agents', 'verifications.jsonl');
|
|
208
|
+
const l10 = fs.existsSync(vp10) ? JSON.parse(fs.readFileSync(vp10, 'utf8').trim()) : null;
|
|
209
|
+
check('场景 10:仅有嵌套同形行 → 绿行落盘但省略计数键(不回退到错的事实)',
|
|
210
|
+
r10.status === 0 && l10 !== null && l10.exitCode === 0 && !('passed' in l10) && !('failed' in l10),
|
|
211
|
+
`line=${JSON.stringify(l10)}`);
|
|
212
|
+
fs.rmSync(root10, { recursive: true, force: true });
|
|
213
|
+
}
|
|
214
|
+
|
|
188
215
|
console.log(`\n合计: PASS ${pass} / FAIL ${fail}`);
|
|
189
216
|
if (fail) {
|
|
190
217
|
console.log('\n用法:node templates/_agents/scripts/verify.test.mjs');
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// wiki-search.test.mjs — wiki 检索转发壳(2026-10-09 engine-quality-round2 A2 补测)
|
|
2
|
+
// 壳语义三断言:①默认 -n 20 + --scope wiki 追加(固定语料=wiki 活跃层);②用户显式 -n 不被覆盖;
|
|
3
|
+
// ③exit 码透传(kb-search 非零 → 壳非零)。fixture 复制壳 + kb-search 及其依赖(workflow-enums/
|
|
4
|
+
// 配置文件),wiki 语料一文件命中(kb-search.test 场景 1 已覆盖 kb-search 本体判定,此处只测壳)。
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import os from 'node:os';
|
|
8
|
+
import { spawnSync } from 'node:child_process';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
|
|
11
|
+
const SRC = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
let pass = 0, fail = 0;
|
|
13
|
+
const check = (name, cond, detail = '') => {
|
|
14
|
+
if (cond) { pass++; console.log(`PASS ${name}`); }
|
|
15
|
+
else { fail++; console.log(`FAIL ${name}${detail ? `——${detail}` : ''}`); }
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const COPIED = ['wiki-search.mjs', 'kb-search.mjs', 'workflow-enums.mjs'];
|
|
19
|
+
function mkFixture() {
|
|
20
|
+
const fx = fs.mkdtempSync(path.join(os.tmpdir(), 'ws-'));
|
|
21
|
+
fs.mkdirSync(path.join(fx, '.agents', 'scripts'), { recursive: true });
|
|
22
|
+
for (const f of COPIED) fs.copyFileSync(path.join(SRC, f), path.join(fx, '.agents', 'scripts', f));
|
|
23
|
+
for (const f of ['workflow-enums.txt', 'workflow-modules.txt'])
|
|
24
|
+
fs.copyFileSync(path.join(SRC, '..', f), path.join(fx, '.agents', f));
|
|
25
|
+
fs.mkdirSync(path.join(fx, 'wiki', '测试主题'), { recursive: true });
|
|
26
|
+
fs.writeFileSync(path.join(fx, 'wiki', '测试主题', '说明.md'), '# 说明\n\n编码模板的领域知识条目。\n');
|
|
27
|
+
fs.mkdirSync(path.join(fx, 'workflow', 'intents'), { recursive: true });
|
|
28
|
+
fs.writeFileSync(path.join(fx, 'workflow', 'intents', '2026-10-09-a.md'),
|
|
29
|
+
'---\n状态: approved\n级别: L1\n日期: 2026-10-09\n模块: pipeline\n---\n# INTENT\n\n编码模板的 workflow 侧条目。\n');
|
|
30
|
+
return fx;
|
|
31
|
+
}
|
|
32
|
+
const run = (fx, args) => spawnSync(process.execPath, [path.join(fx, '.agents', 'scripts', 'wiki-search.mjs'), ...args], {
|
|
33
|
+
cwd: fx, encoding: 'utf8',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
{
|
|
37
|
+
const fx = mkFixture();
|
|
38
|
+
const r = run(fx, ['编码模板']);
|
|
39
|
+
const out = (r.stdout || '') + (r.stderr || '');
|
|
40
|
+
check('① 默认形态命中 wiki 语料且不含 workflow 语料(--scope wiki 固定)',
|
|
41
|
+
r.status === 0 && out.includes('wiki/测试主题/说明.md') && !out.includes('workflow/intents'), out.slice(0, 400));
|
|
42
|
+
fs.rmSync(fx, { recursive: true, force: true });
|
|
43
|
+
}
|
|
44
|
+
{
|
|
45
|
+
const fx = mkFixture();
|
|
46
|
+
const r = run(fx, ['-n', '0', '编码模板']);
|
|
47
|
+
check('② 显式 -n 透传(0=不限,不因壳默认覆盖报错)且命中', r.status === 0 && (r.stdout || '').includes('说明.md'), (r.stdout || '').slice(0, 300));
|
|
48
|
+
fs.rmSync(fx, { recursive: true, force: true });
|
|
49
|
+
}
|
|
50
|
+
{
|
|
51
|
+
const fx = mkFixture();
|
|
52
|
+
const r = run(fx, []); // 无词 → kb-search 用法错误 exit 1
|
|
53
|
+
check('③ exit 码透传(无词 → 壳非零)', (r.status ?? 1) !== 0, `status=${r.status}`);
|
|
54
|
+
fs.rmSync(fx, { recursive: true, force: true });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
console.log(`\n合计: PASS ${pass} / FAIL ${fail}`);
|
|
58
|
+
process.exit(fail ? 1 : 0);
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
# 格式:<脚本名> | <豁免理由> | <覆盖证据(套件名;无自动化覆盖如实写「无」)>
|
|
3
3
|
# 语义:登记 ≠ 免责——须写明理由与覆盖证据;无覆盖的登记是「工具/启动器豁免」,后续批次评估补测。
|
|
4
4
|
# 新增引擎脚本默认必须带同名 .test.mjs(README 规范条目);确属库件/工具才登记本文件。
|
|
5
|
+
# 2026-10-09 A2 评估:verify-wiki-consistency 已移出(补测 3 场景);wiki-search 已移出(转发壳,判定单源 kb-search.test 覆盖)。
|
|
5
6
|
check-metric-claims.mjs | 经 check-loop.mjs 消费 | 由 check-loop.test.mjs 间接覆盖其调用路径
|
|
6
|
-
ensure-board.mjs |
|
|
7
|
+
ensure-board.mjs | 看板启动器 | 永久工具豁免(需真实端口/进程环境,自动化覆盖无性价比——2026-10-09 engine-quality-round2 A2 评估定性)
|
|
7
8
|
policy.mjs | 库件 | 被 check-loop.test.mjs 直接 import 覆盖
|
|
8
9
|
stage-gates.mjs | 库件 | 被 check-loop.test.mjs 直接 import 覆盖(fill-*/confirm-doc.test 消费其行为)
|
|
9
|
-
verify-wiki-consistency.mjs | wiki 一致性校验工具 | 无(工具豁免,后续批次评估补测)
|
|
10
|
-
wiki-search.mjs | 检索库件 | 无(工具豁免,后续批次评估补测)
|
|
11
10
|
# 2026-10-08 engine-quality-batch:pipeline-run 三层拆分的库件层——纯函数/存储层由 pipeline-run.test.mjs 全量覆盖(断言零改动全过),单建同名测试属形式主义(plan 偏离留痕 ①)
|
|
12
11
|
pipeline-run-docs.mjs | 库件(文档纯函数层,2026-10-08 engine-quality-batch 拆分) | 由 pipeline-run.test.mjs 全量覆盖(断言零改动 29 组全过)
|
|
13
12
|
pipeline-run-run.mjs | 库件(run 存储层,同上拆分) | 由 pipeline-run.test.mjs 全量覆盖(含 PIPELINE_RUN_* 注入契约场景)
|
|
@@ -23,6 +23,26 @@ jobs:
|
|
|
23
23
|
node-version: 22 # 按项目 engines 调整(kit 引擎要求 >=18)
|
|
24
24
|
- name: 安装依赖(有根 package.json 才跑;私有源在此加 registry 配置)
|
|
25
25
|
if: hashFiles('package.json') != ''
|
|
26
|
-
run: npm ci || npm install
|
|
26
|
+
run: npm ci || npm install || npm install --registry=https://registry.npmjs.org # 末级官方源兜底(2026-10-09 engine-quality-round3 W3:镜像故障不阻塞 CI)
|
|
27
27
|
- name: 远端门(测试 + 闭环校验)
|
|
28
28
|
run: node .agents/scripts/verify.mjs
|
|
29
|
+
- name: CI 红回溯点名(2026-10-09 engine-quality-round2 B3:红时列出最近关单,复核优先)
|
|
30
|
+
if: failure()
|
|
31
|
+
run: |
|
|
32
|
+
node -e "
|
|
33
|
+
try {
|
|
34
|
+
const fs = require('fs');
|
|
35
|
+
const p = '.agents/confirmations.jsonl';
|
|
36
|
+
if (!fs.existsSync(p)) process.exit(0);
|
|
37
|
+
const cut = Date.now() - 7 * 864e5;
|
|
38
|
+
const rows = fs.readFileSync(p, 'utf8').split(/\r?\n/).filter(Boolean)
|
|
39
|
+
.map((l) => { try { return JSON.parse(l); } catch { return null; } })
|
|
40
|
+
.filter((e) => e && e.ts && ['done', 'closed'].includes(e.stage) && Date.parse(e.ts) >= cut)
|
|
41
|
+
.map((e) => e.doc);
|
|
42
|
+
const uniq = [...new Set(rows)];
|
|
43
|
+
if (uniq.length) {
|
|
44
|
+
console.log('⚠️ CI 红——最近 7 天关单文档(优先复核是否本批引入):');
|
|
45
|
+
for (const d of uniq) console.log(' - ' + d);
|
|
46
|
+
} else console.log('最近 7 天无关单记录');
|
|
47
|
+
} catch { /* 回溯是辅助面,读不到不添乱 */ }
|
|
48
|
+
"
|