agentic-flow-kit 1.7.15 → 1.8.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/README.md +50 -8
- package/modules/hosts/claude/commands/wf-build.md +1 -1
- package/modules/hosts/claude/commands/wf-design.md +5 -4
- package/modules/hosts/claude/commands/wf-maintain.md +1 -1
- package/modules/hosts/claude/commands/wf-new-task.md +47 -25
- package/modules/hosts/claude/commands/wf-plan.md +1 -1
- package/modules/hosts/claude/commands/wf-review.md +2 -2
- package/modules/hosts/claude/commands/wf-sync-hosts.md +7 -13
- package/modules/hosts/claude/commands/wf-test.md +2 -2
- package/modules/hosts/codex/commands/wf-build.md +1 -1
- package/modules/hosts/codex/commands/wf-design.md +5 -4
- package/modules/hosts/codex/commands/wf-maintain.md +1 -1
- package/modules/hosts/codex/commands/wf-new-task.md +47 -25
- package/modules/hosts/codex/commands/wf-plan.md +1 -1
- package/modules/hosts/codex/commands/wf-review.md +2 -2
- package/modules/hosts/codex/commands/wf-sync-hosts.md +7 -13
- package/modules/hosts/codex/commands/wf-test.md +2 -2
- package/modules/hosts/cursor/commands/wf-build.md +1 -1
- package/modules/hosts/cursor/commands/wf-design.md +5 -4
- package/modules/hosts/cursor/commands/wf-maintain.md +1 -1
- package/modules/hosts/cursor/commands/wf-new-task.md +47 -25
- package/modules/hosts/cursor/commands/wf-plan.md +1 -1
- package/modules/hosts/cursor/commands/wf-review.md +2 -2
- package/modules/hosts/cursor/commands/wf-sync-hosts.md +7 -13
- package/modules/hosts/cursor/commands/wf-test.md +2 -2
- package/modules/hosts/opencode/commands/wf-build.md +1 -1
- package/modules/hosts/opencode/commands/wf-design.md +5 -4
- package/modules/hosts/opencode/commands/wf-maintain.md +1 -1
- package/modules/hosts/opencode/commands/wf-new-task.md +47 -25
- package/modules/hosts/opencode/commands/wf-plan.md +1 -1
- package/modules/hosts/opencode/commands/wf-review.md +2 -2
- package/modules/hosts/opencode/commands/wf-sync-hosts.md +7 -13
- package/modules/hosts/opencode/commands/wf-test.md +2 -2
- package/modules/hosts/trae/commands/wf-build.md +1 -1
- package/modules/hosts/trae/commands/wf-design.md +5 -4
- package/modules/hosts/trae/commands/wf-maintain.md +1 -1
- package/modules/hosts/trae/commands/wf-new-task.md +47 -25
- package/modules/hosts/trae/commands/wf-plan.md +1 -1
- package/modules/hosts/trae/commands/wf-review.md +2 -2
- package/modules/hosts/trae/commands/wf-sync-hosts.md +7 -13
- package/modules/hosts/trae/commands/wf-test.md +2 -2
- package/package.json +1 -1
- package/src/cli.mjs +47 -4
- package/src/doctor.mjs +48 -3
- package/src/executor.mjs +262 -0
- package/src/executor.test.mjs +295 -0
- package/src/fresh-init.test.mjs +1 -1
- package/src/init.mjs +27 -3
- package/src/profiles.mjs +10 -3
- package/src/render.mjs +3 -3
- package/src/rule-budget.test.mjs +2 -2
- package/src/stack-profile.test.mjs +6 -0
- package/src/sync-hosts.mjs +37 -43
- package/src/sync-hosts.test.mjs +67 -56
- package/src/validate.mjs +6 -1
- package/src/validate.test.mjs +1 -1
- package/templates/.gitattributes +12 -1
- package/templates/AGENTS.md +3 -3
- package/templates/_agents/commands/build.md +1 -1
- package/templates/_agents/commands/design.md +5 -4
- package/templates/_agents/commands/maintain.md +1 -1
- package/templates/_agents/commands/new-task.md +47 -25
- package/templates/_agents/commands/plan.md +1 -1
- package/templates/_agents/commands/review.md +2 -2
- package/templates/_agents/commands/sync-hosts.md +7 -13
- package/templates/_agents/commands/test.md +2 -2
- package/templates/_agents/control-surfaces.txt +1 -1
- package/templates/_agents/engine-lock.json +334 -0
- package/templates/_agents/hooks/check-index-incremental.mjs +48 -0
- package/templates/_agents/hooks/check-pairing-incremental.mjs +66 -0
- package/templates/_agents/hooks/check-wiki-ledger.mjs +30 -0
- package/templates/_agents/lane-surfaces.txt +5 -2
- package/templates/_agents/level-hints.txt +30 -0
- package/templates/_agents/loop/papercut-debt/DESIGN.md +112 -0
- package/templates/_agents/mechanism-registry.md +27 -0
- package/templates/_agents/rule-budgets.txt +2 -2
- package/templates/_agents/scripts/agg-delegations.cjs +21 -3
- package/templates/_agents/scripts/agg-delegations.test.mjs +5 -3
- package/templates/_agents/scripts/agg-gate-stats.mjs +3 -3
- package/templates/_agents/scripts/agg-judgments.mjs +80 -0
- package/templates/_agents/scripts/agg-judgments.test.mjs +45 -0
- package/templates/_agents/scripts/archive-sweep.mjs +233 -0
- package/templates/_agents/scripts/archive-sweep.test.mjs +129 -0
- package/templates/_agents/scripts/check-attribution.mjs +37 -0
- package/templates/_agents/scripts/check-convergence.mjs +119 -0
- package/templates/_agents/scripts/check-engine-integrity.mjs +59 -127
- package/templates/_agents/scripts/check-engine-integrity.test.mjs +33 -45
- package/templates/_agents/scripts/check-funnel.mjs +56 -0
- package/templates/_agents/scripts/check-hygiene.mjs +5 -5
- package/templates/_agents/scripts/check-index-incremental.test.mjs +73 -0
- package/templates/_agents/scripts/check-lane-surface.mjs +74 -25
- package/templates/_agents/scripts/check-lane-surface.test.mjs +71 -5
- package/templates/_agents/scripts/check-ledger-invariant.mjs +427 -78
- package/templates/_agents/scripts/check-ledger-invariant.test.mjs +438 -4
- package/templates/_agents/scripts/check-loop-rev.test.mjs +13 -8
- package/templates/_agents/scripts/check-loop-unit.test.mjs +8 -0
- package/templates/_agents/scripts/check-loop.mjs +264 -75
- package/templates/_agents/scripts/check-loop.test.mjs +587 -61
- package/templates/_agents/scripts/check-metric-claims.mjs +4 -2
- package/templates/_agents/scripts/check-prejudgment.mjs +59 -0
- package/templates/_agents/scripts/check-registry.mjs +45 -0
- package/templates/_agents/scripts/check-scope.mjs +96 -0
- package/templates/_agents/scripts/check-scope.test.mjs +96 -0
- package/templates/_agents/scripts/check-scripts-syntax.mjs +61 -0
- package/templates/_agents/scripts/check-scripts-syntax.test.mjs +72 -0
- package/templates/_agents/scripts/check-spec-verif.mjs +47 -0
- package/templates/_agents/scripts/compact-ledgers.mjs +97 -0
- package/templates/_agents/scripts/compact-ledgers.test.mjs +162 -0
- package/templates/_agents/scripts/confirm-doc.mjs +136 -31
- package/templates/_agents/scripts/confirm-doc.test.mjs +180 -4
- package/templates/_agents/scripts/doctor.test.mjs +44 -1
- package/templates/_agents/scripts/edit-face-check.mjs +5 -3
- package/templates/_agents/scripts/edit-face-check.test.mjs +5 -4
- package/templates/_agents/scripts/fill-intent.mjs +7 -5
- package/templates/_agents/scripts/fill-intent.test.mjs +19 -1
- package/templates/_agents/scripts/fill-plan.mjs +10 -6
- package/templates/_agents/scripts/fill-plan.test.mjs +19 -3
- package/templates/_agents/scripts/fill-spec.mjs +6 -3
- package/templates/_agents/scripts/fill-spec.test.mjs +12 -4
- package/templates/_agents/scripts/fixture-scripts.mjs +153 -0
- package/templates/_agents/scripts/fixture-scripts.test.mjs +112 -0
- package/templates/_agents/scripts/gate-checklist.mjs +10 -9
- package/templates/_agents/scripts/gate-checklist.test.mjs +2 -11
- package/templates/_agents/scripts/gate-seg.mjs +1 -1
- package/templates/_agents/scripts/gen-effective-policy.mjs +107 -0
- package/templates/_agents/scripts/gen-effective-policy.test.mjs +100 -0
- package/templates/_agents/scripts/gen-verify-evidence.mjs +107 -0
- package/templates/_agents/scripts/gen-verify-evidence.test.mjs +84 -0
- package/templates/_agents/scripts/gen-workflow-dashboard.mjs +16 -14
- package/templates/_agents/scripts/gen-workflow-dashboard.test.mjs +2 -1
- package/templates/_agents/scripts/gen-workflow-metrics.mjs +12 -36
- package/templates/_agents/scripts/git-core.mjs +45 -0
- package/templates/_agents/scripts/git-merge-jsonl.mjs +63 -0
- package/templates/_agents/scripts/git-merge-jsonl.test.mjs +125 -0
- package/templates/_agents/scripts/judgment-ledger.mjs +65 -0
- package/templates/_agents/scripts/judgment-ledger.test.mjs +129 -0
- package/templates/_agents/scripts/kb-search.mjs +19 -4
- package/templates/_agents/scripts/kb-search.test.mjs +33 -0
- package/templates/_agents/scripts/lane-surface-audit.mjs +143 -0
- package/templates/_agents/scripts/lane-surface-audit.test.mjs +93 -0
- package/templates/_agents/scripts/ledger-doc-chain.test.mjs +166 -0
- package/templates/_agents/scripts/ledger-loader.mjs +108 -0
- package/templates/_agents/scripts/ledger-loader.test.mjs +131 -0
- package/templates/_agents/scripts/ledger-prune.mjs +96 -0
- package/templates/_agents/scripts/ledger-prune.test.mjs +120 -0
- package/templates/_agents/scripts/ledger-shards.mjs +307 -0
- package/templates/_agents/scripts/ledger-shards.test.mjs +339 -0
- package/templates/_agents/scripts/ledger-ts-monotonic.test.mjs +135 -0
- package/templates/_agents/scripts/level-criteria.mjs +231 -0
- package/templates/_agents/scripts/level-criteria.test.mjs +281 -0
- package/templates/_agents/scripts/papercut-debt-controller.mjs +141 -0
- package/templates/_agents/scripts/papercut-debt-controller.test.mjs +127 -0
- package/templates/_agents/scripts/papercut-debt-sensor.mjs +170 -0
- package/templates/_agents/scripts/papercut-debt-sensor.test.mjs +146 -0
- package/templates/_agents/scripts/pipeline-run.mjs +10 -24
- package/templates/_agents/scripts/pipeline-run.test.mjs +22 -42
- package/templates/_agents/scripts/policy.mjs +75 -0
- package/templates/_agents/scripts/resolve-merge-conflicts.mjs +110 -0
- package/templates/_agents/scripts/resolve-merge-conflicts.test.mjs +87 -0
- package/templates/_agents/scripts/rewrite-file.mjs +133 -0
- package/templates/_agents/scripts/rewrite-file.test.mjs +148 -0
- package/templates/_agents/scripts/rule-budget.mjs +83 -0
- package/templates/_agents/scripts/run-tests.mjs +99 -0
- package/templates/_agents/scripts/run-tests.test.mjs +105 -0
- package/templates/_agents/scripts/solidify-task.test.mjs +1 -1
- package/templates/_agents/scripts/stage-gates.mjs +38 -21
- package/templates/_agents/scripts/trust-mode.mjs +4 -4
- package/templates/_agents/scripts/trust-mode.test.mjs +1 -1
- package/templates/_agents/scripts/verify.mjs +135 -58
- package/templates/_agents/scripts/verify.test.mjs +156 -47
- package/templates/_agents/scripts/wf-journal.mjs +8 -4
- package/templates/_agents/scripts/wf-journal.test.mjs +1 -1
- package/templates/_agents/scripts/wiki-search.test.mjs +5 -6
- package/templates/_agents/scripts/workflow-board-server.test.mjs +2 -2
- package/templates/_agents/scripts/workflow-enums.test.mjs +14 -5
- package/templates/_agents/scripts/workflows-check.mjs +1 -1
- package/templates/_agents/scripts-test-exempt.txt +1 -0
- package/templates/_agents/skills/papercut-fix/SKILL.md +60 -0
- package/templates/_agents/skills/papercut-fix/references/response-template.md +81 -0
- package/templates/_agents/workflow-enums.txt +8 -1
- package/templates/_agents/workflows/pipeline-closing.md +4 -5
- package/templates/_githooks/pre-commit +109 -120
- package/templates/_githooks/pre-merge-commit +27 -23
- package/templates/_githooks/pre-push +7 -6
- package/templates/_github/workflows/kit-ci.yml +4 -3
- package/templates/workflow/README.md +23 -5
- package/templates/workflow/incidents/_TEMPLATE.md +4 -1
- package/templates/workflow/intents/_TEMPLATE.md +4 -2
- package/templates/workflow/papercuts.md +2 -0
- package/templates/workflow/plans/_TEMPLATE.md +10 -5
- package/templates/workflow/regression-checklist.md +1 -0
- package/templates/workflow/specs/_TEMPLATE.md +17 -1
- package/modules/hosts/claude/commands/wf-source-sync-check.md +0 -89
- package/modules/hosts/codex/commands/wf-source-sync-check.md +0 -89
- package/modules/hosts/cursor/commands/wf-source-sync-check.md +0 -89
- package/modules/hosts/opencode/commands/wf-source-sync-check.md +0 -89
- package/modules/hosts/trae/commands/wf-source-sync-check.md +0 -90
- package/templates/_agents/commands/source-sync-check.md +0 -103
- package/templates/_agents/hooks/check-pairing-incremental.sh +0 -43
- package/templates/_agents/hooks/check-wiki-ledger.sh +0 -18
- package/templates/_agents/scripts/check-ledger.mjs +0 -109
- package/templates/_agents/scripts/check-ledger.test.mjs +0 -199
- package/templates/_agents/scripts/rule-budget.sh +0 -74
- package/templates/_agents/scripts/source-sync-check.mjs +0 -156
- package/templates/_agents/scripts/source-sync-check.test.mjs +0 -286
- package/templates/_agents/workflows/source-sync-repair.md +0 -30
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// check-loop.test.mjs — 闭环门禁 fixture 套件(2026-09-26 check-loop-node:自 check-loop.test.sh 全量移植)
|
|
3
3
|
// 方法:现场构造临时 workflow 根(含枚举单源内联件),spawn node check-loop.mjs(CHECK_LOOP_ROOT 注入,
|
|
4
4
|
// git 模式场景改 cwd 注入),断言 exit code 与输出关键词——场景集与关键词断言沿 sh 版逐条移植。
|
|
5
|
-
// git 场景用 git init 现场建仓;rule-budget --staged
|
|
6
|
-
//
|
|
5
|
+
// git 场景用 git init 现场建仓;rule-budget --staged 两场景 node 直跑(2026-10-13 门禁去 sh 化——
|
|
6
|
+
// rule-budget.mjs 无 sh 依赖)。
|
|
7
7
|
// 用法:node templates/_agents/scripts/check-loop.test.mjs(npm test 随跑)
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import os from 'node:os';
|
|
@@ -28,7 +28,12 @@ const check = (desc, cond, detail = '') => {
|
|
|
28
28
|
else { fail++; console.log(`FAIL ${desc}${detail ? '\n ' + detail : ''}`); }
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
//
|
|
31
|
+
// 夹具闭包工具(2026-10-12 fixture-closure-rootfix):本文件此前 5 处手写依赖枚举,2026-10-12 那批
|
|
32
|
+
// 台账改动漏拷 ledger-loader.mjs 就假绿过(看板漂移门恒绿)。改为按入口自动求传递闭包——新增依赖自动跟随。
|
|
33
|
+
const { copyScriptClosure, copyAgentsFiles } = await import('./fixture-scripts.mjs');
|
|
34
|
+
|
|
35
|
+
// mkfix:空 fixture 根(四目录 + 枚举单源内联 10 键——fixture 自包含,测消费逻辑而非当前词表值;
|
|
36
|
+
// 2026-10-13 B6b-2 起 +incident.source.all/incident.env.all 两键:检查 24 归因枚举消费面)
|
|
32
37
|
const ENUMS_FIXTURE = [
|
|
33
38
|
'doc.status.all=draft approved done superseded cancelled',
|
|
34
39
|
'doc.status.confirmed=approved done superseded cancelled',
|
|
@@ -37,7 +42,9 @@ const ENUMS_FIXTURE = [
|
|
|
37
42
|
'doc.status.abandoned=superseded cancelled',
|
|
38
43
|
'incident.status.all=open fixed closed',
|
|
39
44
|
'incident.status.active=open',
|
|
40
|
-
'level.all=
|
|
45
|
+
'level.all=L1 L2 L3',
|
|
46
|
+
'incident.source.all=self user test monitor',
|
|
47
|
+
'incident.env.all=dev staging prod none',
|
|
41
48
|
].join('\n') + '\n';
|
|
42
49
|
|
|
43
50
|
const mkfix = () => {
|
|
@@ -69,9 +76,10 @@ ${refs ? ' - 是 → ../intents/2026-09-12-ghost.md\n' : ' - 否 → 理由:
|
|
|
69
76
|
`;
|
|
70
77
|
|
|
71
78
|
// run:默认 CHECK_LOOP_ROOT 注入(fixture 模式);{git:true} 时 cwd 注入(真 git 模式,测 tracked 过滤);
|
|
72
|
-
// opts.args
|
|
79
|
+
// opts.args 替换默认 `--all`(套件默认全量 = 分道前裸跑语义;2026-10-13 分道后裸跑=默认硬门,沿源仓
|
|
80
|
+
// check-loop.test.mjs 同款约定——硬门默认入口用 { args: [] },分道场景显式传 --hygiene);
|
|
73
81
|
// opts.env 增量覆盖子进程 env(CHECK_LOOP_GIT 替身注入用,2026-10-05-gitout-fail-open)
|
|
74
|
-
const run = (root, opts = {}) => spawnSync(process.execPath, [CHECK_LOOP, ...(opts.args
|
|
82
|
+
const run = (root, opts = {}) => spawnSync(process.execPath, [CHECK_LOOP, ...(opts.args ?? ['--all'])], {
|
|
75
83
|
...(opts.git
|
|
76
84
|
? { cwd: root, ...(opts.env ? { env: { ...process.env, ...opts.env } } : {}) }
|
|
77
85
|
: { cwd: ROOT_CWD, env: { ...process.env, ...(opts.env || {}), CHECK_LOOP_ROOT: root } }),
|
|
@@ -114,6 +122,14 @@ const gitCommitAll = (root, msg) => {
|
|
|
114
122
|
gitRetry(['add', '-A'], root);
|
|
115
123
|
gitRetry(['-c', 'user.email=t@t', '-c', 'user.name=t', 'commit', '-qm', msg], root);
|
|
116
124
|
};
|
|
125
|
+
// gitCommitAllAt:带 author/committer 日期的提交(B6b-2 §21/§3x 场景用——那两段锚取 git 首加日期,
|
|
126
|
+
// 夹具须钉死在锚日/锚后,套件跨日重跑才不翻面;iso 形如 '2026-10-13T01:00:00.000Z')
|
|
127
|
+
const gitCommitAllAt = (root, msg, iso) => {
|
|
128
|
+
gitRetry(['add', '-A'], root);
|
|
129
|
+
const env = { ...process.env, GIT_AUTHOR_DATE: iso, GIT_COMMITTER_DATE: iso };
|
|
130
|
+
const r = spawnSync(GIT, ['-c', 'user.email=t@t', '-c', 'user.name=t', 'commit', '-qm', msg], { cwd: root, encoding: 'utf8', env });
|
|
131
|
+
if (r.error || r.status !== 0) throw new Error(`fixture git 回溯提交失败: git commit(${iso})\n${(r.stderr || '').slice(0, 300)}`);
|
|
132
|
+
};
|
|
117
133
|
const rmfix = (root) => fs.rmSync(root, { recursive: true, force: true });
|
|
118
134
|
// writeLedger:fixture 台账辅助(检查 15 配对用;行 schema 同 confirm-doc.mjs appendLedger)
|
|
119
135
|
const writeLedger = (root, entries) => {
|
|
@@ -383,9 +399,8 @@ const mkConfirmedDoc = (root, rel, fmBody) => {
|
|
|
383
399
|
{
|
|
384
400
|
const T = mkfix();
|
|
385
401
|
fs.mkdirSync(path.join(T, '.agents', 'scripts'), { recursive: true });
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
fs.copyFileSync(path.join(SCRIPT_DIR, '..', 'workflow-modules.txt'), path.join(T, '.agents', 'workflow-modules.txt'));
|
|
402
|
+
copyScriptClosure(path.join(T, '.agents', 'scripts'), ['gen-workflow-index.mjs']);
|
|
403
|
+
copyAgentsFiles(path.join(T, '.agents'), ['workflow-modules.txt']);
|
|
389
404
|
w(T, 'workflow/intents/2026-09-22-mod-ok.md', INTENT('mod ok', '状态: approved\n级别: L1\n日期: 2026-09-22\n模块: pipeline', '\n## 验收标准(可测试)\n- [ ] 用例通过\n'));
|
|
390
405
|
w(T, 'workflow/plans/2026-09-22-mod-ok.md', PLAN('mod ok', '状态: approved\n级别: L1\n模块: pipeline'));
|
|
391
406
|
spawnSync(process.execPath, ['.agents/scripts/gen-workflow-index.mjs'], { cwd: T });
|
|
@@ -412,9 +427,8 @@ const mkConfirmedDoc = (root, rel, fmBody) => {
|
|
|
412
427
|
{
|
|
413
428
|
const T = mkfix();
|
|
414
429
|
fs.mkdirSync(path.join(T, '.agents', 'scripts'), { recursive: true });
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
fs.copyFileSync(path.join(SCRIPT_DIR, '..', 'workflow-modules.txt'), path.join(T, '.agents', 'workflow-modules.txt'));
|
|
430
|
+
copyScriptClosure(path.join(T, '.agents', 'scripts'), ['gen-workflow-index.mjs']);
|
|
431
|
+
copyAgentsFiles(path.join(T, '.agents'), ['workflow-modules.txt']);
|
|
418
432
|
w(T, 'workflow/intents/2026-09-22-drift.md', INTENT('drift', '状态: approved\n级别: L1\n日期: 2026-09-22\n模块: pipeline'));
|
|
419
433
|
w(T, 'workflow/plans/2026-09-22-drift.md', PLAN('drift', '状态: approved\n级别: L1\n模块: pipeline'));
|
|
420
434
|
spawnSync(process.execPath, ['.agents/scripts/gen-workflow-index.mjs'], { cwd: T });
|
|
@@ -430,10 +444,10 @@ const mkConfirmedDoc = (root, rel, fmBody) => {
|
|
|
430
444
|
const mkDashFix = () => {
|
|
431
445
|
const T = mkfix();
|
|
432
446
|
fs.mkdirSync(path.join(T, '.agents', 'scripts'), { recursive: true });
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
447
|
+
// 看板/索引生成器的台账读取依赖(ledger-loader 等)由闭包自动带入——此前手写枚举漏拷过,
|
|
448
|
+
// 表现是「import 失败 → 看板静默不生成 → 漂移门恒绿」,即假绿比红危险(2026-10-12 已修,见 fixture-scripts.mjs)
|
|
449
|
+
copyScriptClosure(path.join(T, '.agents', 'scripts'), ['gen-workflow-index.mjs', 'gen-workflow-dashboard.mjs']);
|
|
450
|
+
copyAgentsFiles(path.join(T, '.agents'), ['workflow-modules.txt']);
|
|
437
451
|
w(T, 'workflow/intents/2026-09-22-dash.md', INTENT('dash', '状态: approved\n级别: L1\n日期: 2026-09-22\n模块: pipeline'));
|
|
438
452
|
w(T, 'workflow/plans/2026-09-22-dash.md', PLAN('dash', '状态: approved\n级别: L1\n模块: pipeline'));
|
|
439
453
|
spawnSync(process.execPath, ['.agents/scripts/gen-workflow-index.mjs'], { cwd: T });
|
|
@@ -459,8 +473,8 @@ const mkConfirmedDoc = (root, rel, fmBody) => {
|
|
|
459
473
|
{
|
|
460
474
|
const T = mkfix();
|
|
461
475
|
fs.mkdirSync(path.join(T, '.agents', 'scripts'), { recursive: true });
|
|
462
|
-
fs.copyFileSync(path.join(SCRIPT_DIR, 'rule-budget.
|
|
463
|
-
|
|
476
|
+
fs.copyFileSync(path.join(SCRIPT_DIR, 'rule-budget.mjs'), path.join(T, '.agents', 'scripts', 'rule-budget.mjs'));
|
|
477
|
+
copyAgentsFiles(path.join(T, '.agents'), ['rule-budgets.txt']);
|
|
464
478
|
// 超限样本自校准:从拷入的预算表读 AGENTS.md 上限(预算可上调,硬编码样本会随上调失效——2026-10-06-v114-backflow-batch)
|
|
465
479
|
const agLimit = Number(fs.readFileSync(path.join(T, '.agents', 'rule-budgets.txt'), 'utf8').split(/\r?\n/).find((l) => l.startsWith('AGENTS.md ')).trim().split(' ').pop());
|
|
466
480
|
w(T, 'AGENTS.md', '# AGENTS 小体积 fixture\n');
|
|
@@ -479,29 +493,24 @@ const mkConfirmedDoc = (root, rel, fmBody) => {
|
|
|
479
493
|
rmfix(T);
|
|
480
494
|
}
|
|
481
495
|
|
|
482
|
-
// ---- 场景 30:rule-budget --staged 硬拦路径(
|
|
496
|
+
// ---- 场景 30:rule-budget --staged 硬拦路径(node 直跑——2026-10-13 去 sh 化后无 sh 依赖)----
|
|
483
497
|
{
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
gitRetry(['add', 'AGENTS.md'], T);
|
|
501
|
-
r = spawnSync('sh', ['.agents/scripts/rule-budget.sh', '--staged'], { cwd: T, encoding: 'utf8' });
|
|
502
|
-
check('rule-budget --staged:合规 → exit 0(不误拦)', r.status === 0, `${r.stdout}${r.stderr}`);
|
|
503
|
-
rmfix(T);
|
|
504
|
-
}
|
|
498
|
+
const T = mkfix();
|
|
499
|
+
fs.mkdirSync(path.join(T, '.agents', 'scripts'), { recursive: true });
|
|
500
|
+
fs.copyFileSync(path.join(SCRIPT_DIR, 'rule-budget.mjs'), path.join(T, '.agents', 'scripts', 'rule-budget.mjs'));
|
|
501
|
+
copyAgentsFiles(path.join(T, '.agents'), ['rule-budgets.txt']);
|
|
502
|
+
const agLimit = Number(fs.readFileSync(path.join(T, '.agents', 'rule-budgets.txt'), 'utf8').split(/\r?\n/).find((l) => l.startsWith('AGENTS.md ')).trim().split(' ').pop());
|
|
503
|
+
gitInit(T);
|
|
504
|
+
w(T, 'AGENTS.md', 'x'.repeat(agLimit + 512));
|
|
505
|
+
gitRetry(['add', 'AGENTS.md'], T);
|
|
506
|
+
let r = spawnSync(process.execPath, ['.agents/scripts/rule-budget.mjs', '--staged'], { cwd: T, encoding: 'utf8' });
|
|
507
|
+
check('rule-budget --staged:暂存超限 → exit 1(硬拦路径)',
|
|
508
|
+
r.status === 1 && `${r.stdout}${r.stderr}`.includes('常驻面超限:AGENTS.md'), `${r.stdout}${r.stderr}`);
|
|
509
|
+
w(T, 'AGENTS.md', '# 小体积\n');
|
|
510
|
+
gitRetry(['add', 'AGENTS.md'], T);
|
|
511
|
+
r = spawnSync(process.execPath, ['.agents/scripts/rule-budget.mjs', '--staged'], { cwd: T, encoding: 'utf8' });
|
|
512
|
+
check('rule-budget --staged:合规 → exit 0(不误拦)', r.status === 0, `${r.stdout}${r.stderr}`);
|
|
513
|
+
rmfix(T);
|
|
505
514
|
}
|
|
506
515
|
|
|
507
516
|
// ---- 场景 31/32/33:确认态留痕(检查 14)——无 approved 历史 WARN;先 approved 后 done 无 WARN;非 git 跳过 ----
|
|
@@ -740,7 +749,7 @@ const evidenceFixture = (slug, declared, createDeclared) => {
|
|
|
740
749
|
w(T, 'workflow/plans/2026-09-26-pe2.md', PLAN('pe2', '状态: done\n级别: L1\n模块: material', '\n## 改动方案\n\n- src/app.mjs:实现功能\n'));
|
|
741
750
|
fs.mkdirSync(path.join(T, 'notes'), { recursive: true });
|
|
742
751
|
w(T, 'notes/other.md', 'x\n');
|
|
743
|
-
gitCommitAll(T, 'docs(x): 演练穿越(
|
|
752
|
+
gitCommitAll(T, 'docs(x): 演练穿越(L1,无标记提交)');
|
|
744
753
|
const sha = shortSha(T);
|
|
745
754
|
w(T, 'workflow/intents/2026-09-26-pe2.md', INTENT('pe2', '状态: done\n级别: L1\n日期: 2026-09-26', `\n## 验收标准(可测试)\n- [x] 演练穿越(证据:${sha})\n`));
|
|
746
755
|
expectHard('过程证据:无标记无关提交 → 仍拦 证据无关', T, '证据无关');
|
|
@@ -765,7 +774,7 @@ const evidenceFixture = (slug, declared, createDeclared) => {
|
|
|
765
774
|
w(T, 'workflow/plans/2026-09-26-pe4.md', PLAN('pe4', '状态: done\n级别: L1\n模块: material', '\n## 改动方案\n\n- src/app.mjs:实现功能\n'));
|
|
766
775
|
fs.mkdirSync(path.join(T, 'notes'), { recursive: true });
|
|
767
776
|
w(T, 'notes/other.md', 'x\n');
|
|
768
|
-
gitCommitAll(T, 'docs(x): 演练穿越(
|
|
777
|
+
gitCommitAll(T, 'docs(x): 演练穿越(L1,无标记提交)');
|
|
769
778
|
const sha = shortSha(T);
|
|
770
779
|
w(T, 'workflow/intents/2026-09-26-pe4.md', INTENT('pe4', '状态: done\n级别: L1\n日期: 2026-09-26', `\n## 验收标准(可测试)\n- [x] 演练穿越(证据:${sha})\n`));
|
|
771
780
|
const r = run(T, { env: { CHECK_LOOP_GIT: 'definitely-not-git-xyz-2026' } });
|
|
@@ -799,7 +808,7 @@ const evidenceFixture = (slug, declared, createDeclared) => {
|
|
|
799
808
|
w(T, 'workflow/plans/2026-09-26-pe5.md', PLAN('pe5', '状态: done\n级别: L1\n模块: material', '\n## 改动方案\n\n- src/app.mjs:实现功能\n'));
|
|
800
809
|
fs.mkdirSync(path.join(T, 'notes'), { recursive: true });
|
|
801
810
|
w(T, 'notes/other.md', 'x\n');
|
|
802
|
-
gitCommitAll(T, 'docs(x): 演练穿越(
|
|
811
|
+
gitCommitAll(T, 'docs(x): 演练穿越(L1,无标记提交)');
|
|
803
812
|
const sha = shortSha(T);
|
|
804
813
|
w(T, 'workflow/intents/2026-09-26-pe5.md', INTENT('pe5', '状态: done\n级别: L1\n日期: 2026-09-26', `\n## 验收标准(可测试)\n- [x] 演练穿越(证据:${sha})\n`));
|
|
805
814
|
const r = run(T);
|
|
@@ -2409,17 +2418,8 @@ w(T, 'workflow/intents/2026-09-12-deep.md', INTENT('deep', '状态: draft\n级
|
|
|
2409
2418
|
}
|
|
2410
2419
|
}
|
|
2411
2420
|
|
|
2412
|
-
// ---- 风险泳道(2026-09-30 hybrid-governance-risk-lanes
|
|
2413
|
-
|
|
2414
|
-
// L0 intent 无 plan 无 spec → 协作道配对豁免(异步审计兜底)
|
|
2415
|
-
const T = mkfix();
|
|
2416
|
-
w(T, 'workflow/intents/2026-09-12-l0.md', INTENT('l0', '状态: approved\n级别: L0\nrisk_level: L0\n日期: 2026-09-12'));
|
|
2417
|
-
const r = run(T);
|
|
2418
|
-
check('风险泳道:L0 intent 缺 plan/spec → 配对豁免 exit 0',
|
|
2419
|
-
r.status === 0 && !outOf(r).includes('配对断裂'),
|
|
2420
|
-
`exit=${r.status}\n${outOf(r)}`);
|
|
2421
|
-
rmfix(T);
|
|
2422
|
-
}
|
|
2421
|
+
// ---- 风险泳道(2026-09-30 hybrid-governance-risk-lanes):红线判低 ----
|
|
2422
|
+
// (2026-10-13 L0 退役,自源仓收敛系列 1 回灌:原「L0 配对豁免」场景随级别退役删除。存量 intent 仍须 plan;L1 plan 可独立成入口。)
|
|
2423
2423
|
{
|
|
2424
2424
|
// L1 intent 仍须 plan(协作道不豁免 L1——防倒退回归)
|
|
2425
2425
|
const T1 = mkfix();
|
|
@@ -2427,7 +2427,7 @@ w(T, 'workflow/intents/2026-09-12-deep.md', INTENT('deep', '状态: draft\n级
|
|
|
2427
2427
|
expectHard('风险泳道:L1 intent 缺 plan 仍 hard 配对断裂', T1, '配对断裂');
|
|
2428
2428
|
rmfix(T1);
|
|
2429
2429
|
|
|
2430
|
-
//
|
|
2430
|
+
// L1 勾触达红线 → hard「红线判低」(级别与 risk_level 任一判定皆拦)
|
|
2431
2431
|
const T2 = mkfix();
|
|
2432
2432
|
w(T2, 'workflow/intents/2026-09-12-rl.md', INTENT('rl', '状态: approved\n级别: L1\nrisk_level: L1\n日期: 2026-09-12',
|
|
2433
2433
|
'\n## 触达红线(对照 AGENTS.md)\n- [x] 规则 / 契约变更(接口签名)→ 级别至少 L2\n'));
|
|
@@ -2566,6 +2566,45 @@ w(T, 'workflow/intents/2026-09-12-deep.md', INTENT('deep', '状态: draft\n级
|
|
|
2566
2566
|
check('加固门 H6:未知 CLI 参数 → exit 1 用法提示', r.status === 1 && /用法/.test(outOf(r)), outOf(r));
|
|
2567
2567
|
rmfix(T);
|
|
2568
2568
|
}
|
|
2569
|
+
{
|
|
2570
|
+
// H6b:--all 与 --hygiene 互斥组合 → 用法提示 exit 1(2026-10-13 分道,源仓 takeOpts 口径)
|
|
2571
|
+
const T = mkfix();
|
|
2572
|
+
const r = run(T, { args: ['--all', '--hygiene'] });
|
|
2573
|
+
check('分道 H6b:--all 与 --hygiene 同给 → exit 1 用法提示', r.status === 1 && /用法/.test(outOf(r)), outOf(r));
|
|
2574
|
+
rmfix(T);
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
// ---- 默认入口 vs 卫生入口 vs 全量(2026-10-13 B6b-1 分道;源仓 check-loop.test.mjs 同名场景移植)----
|
|
2578
|
+
{
|
|
2579
|
+
// 卫生违例(模板占位符残留,检查 2 hygiene 面)无硬违例:默认(裸跑=硬门)不出卫生面且 exit 0;
|
|
2580
|
+
// --hygiene 出卫生面且 exit 0;--all 两段都跑 = 分道前的裸跑全量。
|
|
2581
|
+
// (夹具给配对齐全的 intent+plan——kit 检查 1 对 plan 落单即拦配对断裂,无源仓的 L1 豁免。)
|
|
2582
|
+
const T = mkfix();
|
|
2583
|
+
w(T, 'workflow/intents/2026-10-13-ph.md', INTENT('ph', '状态: draft\n级别: L1\n日期: 2026-10-13'));
|
|
2584
|
+
w(T, 'workflow/plans/2026-10-13-ph.md', PLAN('ph', '状态: draft\n级别: L1', '\n## 确认与复核\n\n- 日期: YYYY-MM-DD\n'));
|
|
2585
|
+
const bare = run(T, { args: [] });
|
|
2586
|
+
check('分道:默认入口不跑卫生段——占位符不出「模板未填」且 exit 0',
|
|
2587
|
+
bare.status === 0 && !outOf(bare).includes('模板未填'), `exit=${bare.status}\n${outOf(bare)}`);
|
|
2588
|
+
const hy = run(T, { args: ['--hygiene'] });
|
|
2589
|
+
check('分道:--hygiene 只跑卫生段——同一占位符出「模板未填」且不报配对断裂',
|
|
2590
|
+
hy.status === 0 && outOf(hy).includes('模板未填') && !outOf(hy).includes('配对断裂'), `exit=${hy.status}\n${outOf(hy)}`);
|
|
2591
|
+
const all = run(T, { args: ['--all'] });
|
|
2592
|
+
check('分道:--all 两段都跑(等价分道前裸跑全量)——出「模板未填」且 exit 0',
|
|
2593
|
+
all.status === 0 && outOf(all).includes('模板未填'), `exit=${all.status}\n${outOf(all)}`);
|
|
2594
|
+
rmfix(T);
|
|
2595
|
+
// 硬违例(L1 approved intent 缺同名 plan = 配对断裂,检查 1 hard 面):默认入口与 --all 都拦;
|
|
2596
|
+
// --hygiene 只跑卫生段、不产 blocker(源仓同款语义:卫生段不进关单)。
|
|
2597
|
+
const T2 = mkfix();
|
|
2598
|
+
w(T2, 'workflow/intents/2026-10-13-gap.md', INTENT('gap', '状态: approved\n级别: L1\n日期: 2026-10-13'));
|
|
2599
|
+
const gapBare = run(T2, { args: [] });
|
|
2600
|
+
check('分道:默认入口仍拦配对断裂', gapBare.status === 1 && outOf(gapBare).includes('配对断裂'), `exit=${gapBare.status}\n${outOf(gapBare)}`);
|
|
2601
|
+
const gapAll = run(T2, { args: ['--all'] });
|
|
2602
|
+
check('分道:--all 同样拦配对断裂', gapAll.status === 1 && outOf(gapAll).includes('配对断裂'), `exit=${gapAll.status}\n${outOf(gapAll)}`);
|
|
2603
|
+
const gapHy = run(T2, { args: ['--hygiene'] });
|
|
2604
|
+
check('分道:--hygiene 不跑硬门——配对断裂不出、exit 0(卫生段不进关单)',
|
|
2605
|
+
gapHy.status === 0 && !outOf(gapHy).includes('配对断裂'), `exit=${gapHy.status}\n${outOf(gapHy)}`);
|
|
2606
|
+
rmfix(T2);
|
|
2607
|
+
}
|
|
2569
2608
|
|
|
2570
2609
|
// --- 20. 引擎脚本测试覆盖 [warning](2026-10-01 gate-script-test-coverage) ---
|
|
2571
2610
|
{
|
|
@@ -2882,7 +2921,8 @@ w(T, 'workflow/intents/2026-09-12-deep.md', INTENT('deep', '状态: draft\n级
|
|
|
2882
2921
|
!outOf(plain).includes('[gate-stats]'), outOf(plain));
|
|
2883
2922
|
const before = fs.existsSync(path.join(T, statsRel)) ? fs.readFileSync(path.join(T, statsRel), 'utf8') : '';
|
|
2884
2923
|
|
|
2885
|
-
|
|
2924
|
+
// --all 显式带上(2026-10-13 分道:ROI 采集要全段成本面;裸 --gate-stats 只记硬门段)
|
|
2925
|
+
const st = run(T, { args: ['--gate-stats', '--all'] });
|
|
2886
2926
|
check('门禁ROI:--gate-stats → 落盘 + stderr 摘要行', fs.existsSync(path.join(T, statsRel)) && outOf(st).includes('[gate-stats]'), outOf(st));
|
|
2887
2927
|
const lines = fs.readFileSync(path.join(T, statsRel), 'utf8').split('\n').filter(Boolean);
|
|
2888
2928
|
check('门禁ROI:--gate-stats 只 append 一行(每跑一次门禁 = 一条记录)',
|
|
@@ -2891,10 +2931,10 @@ w(T, 'workflow/intents/2026-09-12-deep.md', INTENT('deep', '状态: draft\n级
|
|
|
2891
2931
|
try { row = JSON.parse(lines[0]); } catch { /* 留给下一条断言报错 */ }
|
|
2892
2932
|
const ids = row && Array.isArray(row.segs) ? row.segs.map((s) => String(s.id)) : [];
|
|
2893
2933
|
const uniq = new Set(ids);
|
|
2894
|
-
check('门禁ROI:段 id 唯一且覆盖
|
|
2895
|
-
ids.length ===
|
|
2896
|
-
const expected = ['1','2','3','4','5','6','8','9','10','11','12','13','14','15','16','17','18','19','20'];
|
|
2897
|
-
check('门禁ROI:段 id 集合 ==
|
|
2934
|
+
check('门禁ROI:段 id 唯一且覆盖 26 个插桩点(14 主流程 + 5 hygiene 模块 + 3x/21-26 六段),无重复插桩',
|
|
2935
|
+
ids.length === 26 && uniq.size === 26, `ids=${ids.join(',')}`);
|
|
2936
|
+
const expected = ['1','2','3','3x','4','5','6','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26'];
|
|
2937
|
+
check('门禁ROI:段 id 集合 == 现有检查编号+3x(不新增/不遗漏检查项编号)',
|
|
2898
2938
|
expected.every((e) => uniq.has(e)) && uniq.size === expected.length, `got=${[...uniq].sort().join(',')}`);
|
|
2899
2939
|
check('门禁ROI:每段都有 label + 非负 ms(成本度量的地基)',
|
|
2900
2940
|
row && row.segs.every((s) => s.label && Number(s.ms) >= 0 && Number.isInteger(Number(s.ms))), JSON.stringify(row && row.segs.slice(0, 3)));
|
|
@@ -2919,8 +2959,11 @@ w(T, 'workflow/intents/2026-09-12-deep.md', INTENT('deep', '状态: draft\n级
|
|
|
2919
2959
|
writeLedger(T, [tampered, row2]);
|
|
2920
2960
|
let r = run(T);
|
|
2921
2961
|
let out = outOf(r);
|
|
2962
|
+
// 定位措辞随 2026-10-12 ledger-shard-write 由「第 N 行(物理)」改为 `rel:行号`——
|
|
2963
|
+
// 台账视图含主台账 + 分片后,单一行号跨来源无法定位,必须带来源。首断行号仍须精确指向第 1 行。
|
|
2922
2964
|
check('场景 21a 链断裂:中间行篡改 → hard 台账链断裂(指首断行号)',
|
|
2923
|
-
r.status !== 0 && out.includes('台账链断裂') &&
|
|
2965
|
+
r.status !== 0 && out.includes('台账链断裂') && /\.agents\/confirmations\.jsonl:1\b/.test(out),
|
|
2966
|
+
`exit=${r.status}\n${out.slice(-400)}`);
|
|
2924
2967
|
// 反证还原:篡改行恢复原 quote → 链复通(断言非恒绿)
|
|
2925
2968
|
writeLedger(T, [row1, row2]);
|
|
2926
2969
|
r = run(T);
|
|
@@ -3333,5 +3376,488 @@ w(T, 'workflow/intents/2026-09-12-deep.md', INTENT('deep', '状态: draft\n级
|
|
|
3333
3376
|
`exit=${r.status}\n${outOf(r).slice(0, 500)}`);
|
|
3334
3377
|
}
|
|
3335
3378
|
|
|
3379
|
+
// ==== B6b-2(2026-10-13 自源仓 check-loop.test.mjs 回灌):§21–§26 六段 + §3x + 检查 1 L1 plan 即入口 + 复核上下文 ====
|
|
3380
|
+
// 场景集与关键词断言沿源仓同段场景移植;锚日期换 kit 口径(v12:specVerifSince/attributionSince/
|
|
3381
|
+
// convergenceSince=2026-10-13,convergenceHardSince/reviewerSince/incidentRegrSince=2026-10-14)。
|
|
3382
|
+
|
|
3383
|
+
// ---- 检查 1:L1 plan 即入口(源仓 s01,2026-10-10 L1 收成一份 plan)----
|
|
3384
|
+
{
|
|
3385
|
+
const T = mkfix();
|
|
3386
|
+
w(T, 'workflow/plans/2026-10-13-solo.md', PLAN('solo', '状态: draft\n级别: L1'));
|
|
3387
|
+
expectOk('检查1 L1 plan 即入口:无同名 intent/incident → exit 0', T);
|
|
3388
|
+
rmfix(T);
|
|
3389
|
+
}
|
|
3390
|
+
{
|
|
3391
|
+
const T = mkfix();
|
|
3392
|
+
w(T, 'workflow/plans/2026-10-13-hi.md', PLAN('hi', '状态: draft\n级别: L2'));
|
|
3393
|
+
expectHard('检查1 L2 plan 落单 → hard 配对断裂', T, '配对断裂');
|
|
3394
|
+
rmfix(T);
|
|
3395
|
+
}
|
|
3396
|
+
{
|
|
3397
|
+
const T = mkfix();
|
|
3398
|
+
w(T, 'workflow/plans/2026-10-13-nolvl.md', PLAN('nolvl', '状态: draft'));
|
|
3399
|
+
expectHard('检查1 级别缺失 plan 落单 → 仍 hard(fail-closed)', T, '配对断裂');
|
|
3400
|
+
rmfix(T);
|
|
3401
|
+
}
|
|
3402
|
+
|
|
3403
|
+
// ---- 检查 1 扩展:独立复核上下文标识(policy v12 reviewerSince,advisory)----
|
|
3404
|
+
{
|
|
3405
|
+
const trio = (name, fm, body, pv = 12) => {
|
|
3406
|
+
const T = mkfix();
|
|
3407
|
+
w(T, '.agents/kit.json', `{"policyVersion":${pv}}\n`);
|
|
3408
|
+
w(T, `workflow/intents/${name}.md`, INTENT(name, '状态: draft\n级别: L1\n日期: 2026-10-13'));
|
|
3409
|
+
w(T, `workflow/specs/${name}.md`, SPEC(name, fm, body));
|
|
3410
|
+
w(T, `workflow/plans/${name}.md`, PLAN(name, '状态: draft\n级别: L1\n日期: 2026-09-01'));
|
|
3411
|
+
return T;
|
|
3412
|
+
};
|
|
3413
|
+
let T = trio('2026-10-14-rv1', '状态: draft\n级别: L1\n日期: 2026-10-14', '\n- 独立复核:通过\n');
|
|
3414
|
+
let r = run(T);
|
|
3415
|
+
check('检查1 复核上下文:锚后复核行缺标识 → WARN 且 exit 0',
|
|
3416
|
+
r.status === 0 && outOf(r).includes('复核上下文缺失'), `exit=${r.status}\n${outOf(r)}`);
|
|
3417
|
+
rmfix(T);
|
|
3418
|
+
T = trio('2026-10-14-rv2', '状态: draft\n级别: L1\n日期: 2026-10-14', '\n- 独立复核:通过(复核上下文:session-abc)\n');
|
|
3419
|
+
r = run(T);
|
|
3420
|
+
check('检查1 复核上下文:复核行带「复核上下文:<id>」→ 无告警',
|
|
3421
|
+
r.status === 0 && !outOf(r).includes('复核上下文缺失'), `exit=${r.status}\n${outOf(r)}`);
|
|
3422
|
+
rmfix(T);
|
|
3423
|
+
T = trio('2026-10-14-rv3', '状态: draft\n级别: L1\n日期: 2026-10-13', '\n- 独立复核:通过\n');
|
|
3424
|
+
r = run(T);
|
|
3425
|
+
check('检查1 复核上下文:锚前存量(<reviewerSince)豁免 → 无告警',
|
|
3426
|
+
r.status === 0 && !outOf(r).includes('复核上下文缺失'), `exit=${r.status}\n${outOf(r)}`);
|
|
3427
|
+
rmfix(T);
|
|
3428
|
+
T = trio('2026-10-14-rv4', '状态: draft\n级别: L1\n日期: 2026-10-14', '\n- 独立复核:通过\n', 11);
|
|
3429
|
+
r = run(T);
|
|
3430
|
+
check('检查1 复核上下文:v11 缺键 → 子检整体跳过',
|
|
3431
|
+
r.status === 0 && !outOf(r).includes('复核上下文缺失'), `exit=${r.status}\n${outOf(r)}`);
|
|
3432
|
+
rmfix(T);
|
|
3433
|
+
}
|
|
3434
|
+
|
|
3435
|
+
// ---- 检查 3x:incident 防复发验证证据门(hard;锚 incidentRegrSince=2026-10-14,git 建档日锚)----
|
|
3436
|
+
{
|
|
3437
|
+
const mkRegr = (verBody, { pv = 12, addIso = '2026-10-14T01:00:00.000Z', name = '2026-10-14-regr' } = {}) => {
|
|
3438
|
+
const T = mkfix();
|
|
3439
|
+
gitInit(T);
|
|
3440
|
+
w(T, '.agents/kit.json', `{"policyVersion":${pv}}\n`);
|
|
3441
|
+
const fm = '状态: fixed\n级别: L1\n发现: 2026-10-14\n流程: standard';
|
|
3442
|
+
const body = `# INCIDENT — ${name}\n\n1. 结构性修复\n - 修复 commit:abc1234\n - 是否需要新 intent:\n - 否 → 理由:单点修复\n\n2. 防复发验证\n${verBody}\n\n3. 规范条目\n - AGENTS.md 某节\n`;
|
|
3443
|
+
const noFp = `---\n状态: open\n级别: L1\n发现: 2026-10-14\n流程: standard\n---\n${body}`;
|
|
3444
|
+
const fp = computeFingerprint(noFp);
|
|
3445
|
+
w(T, `workflow/incidents/${name}.md`, `---\n${fm}\n确认指纹: ${fp.slice(0, 16)}\n---\n${body}`);
|
|
3446
|
+
w(T, `workflow/plans/${name}.md`, PLAN(name, '状态: draft\n级别: L1\n日期: 2026-09-01'));
|
|
3447
|
+
writeLedger(T, [{ ts: addIso, doc: `workflow/incidents/${name}.md`, stage: 'fixed', fingerprint: fp, prev: 'open', source: 'chat-delegated', batch: 't-3x', seq: 1, of: 1 }]);
|
|
3448
|
+
gitCommitAllAt(T, 'incident fixture', addIso);
|
|
3449
|
+
return T;
|
|
3450
|
+
};
|
|
3451
|
+
let T = mkRegr(' - 已人工验证,无自动化\n');
|
|
3452
|
+
let r = run(T, { git: true, args: [] });
|
|
3453
|
+
check('检查3x 锚后 fixed 无回归引用 → hard「回归证据缺失」',
|
|
3454
|
+
r.status === 1 && outOf(r).includes('回归证据缺失'), `exit=${r.status}\n${outOf(r)}`);
|
|
3455
|
+
rmfix(T);
|
|
3456
|
+
T = mkRegr(' - 回归用例 check-scope.test.mjs 已覆盖\n');
|
|
3457
|
+
r = run(T, { git: true });
|
|
3458
|
+
check('检查3x fixed 有 .test.mjs 引用 → 不拦',
|
|
3459
|
+
r.status === 0 && !outOf(r).includes('回归证据缺失'), `exit=${r.status}\n${outOf(r)}`);
|
|
3460
|
+
rmfix(T);
|
|
3461
|
+
T = mkRegr(' - 已人工验证,无自动化\n', { addIso: '2026-10-13T01:00:00.000Z', name: '2026-10-13-regr-pre' });
|
|
3462
|
+
r = run(T, { git: true, args: [] });
|
|
3463
|
+
check('检查3x 锚前存量(建档日 < incidentRegrSince)→ 豁免不拦',
|
|
3464
|
+
r.status === 0 && !outOf(r).includes('回归证据缺失'), `exit=${r.status}\n${outOf(r)}`);
|
|
3465
|
+
rmfix(T);
|
|
3466
|
+
T = mkRegr(' - 已人工验证,无自动化\n', { pv: 8, name: '2026-10-14-regr-v8' });
|
|
3467
|
+
r = run(T, { git: true, args: [] });
|
|
3468
|
+
check('检查3x 缺键(v8)→ 整体跳过不拦',
|
|
3469
|
+
r.status === 0 && !outOf(r).includes('回归证据缺失'), `exit=${r.status}\n${outOf(r)}`);
|
|
3470
|
+
rmfix(T);
|
|
3471
|
+
}
|
|
3472
|
+
|
|
3473
|
+
// ---- 检查 21:spec 验证落地核查(锚 specVerifSince=2026-10-13,git 建档日锚)----
|
|
3474
|
+
{
|
|
3475
|
+
const mkSpec = (lvl, verBody) => {
|
|
3476
|
+
const T = mkfix();
|
|
3477
|
+
gitInit(T);
|
|
3478
|
+
w(T, '.agents/kit.json', '{"policyVersion":12}\n');
|
|
3479
|
+
w(T, 'workflow/intents/2026-10-13-sp.md', INTENT('sp', '状态: draft\n级别: L1\n日期: 2026-10-13'));
|
|
3480
|
+
w(T, 'workflow/plans/2026-10-13-sp.md', PLAN('sp', '状态: draft\n级别: L1\n日期: 2026-09-01'));
|
|
3481
|
+
const fm = `状态: done\n级别: ${lvl}\n日期: 2026-10-13`;
|
|
3482
|
+
const body = `# SPEC — sp\n\n## 测试用例\n${verBody}\n`;
|
|
3483
|
+
// 指纹按 prev 态(approved)内容计算——检查 15 bindingSha256 以台账 prev 字段复原重算
|
|
3484
|
+
const fp = computeFingerprint(`---\n状态: approved\n级别: ${lvl}\n日期: 2026-10-13\n---\n${body}`);
|
|
3485
|
+
w(T, 'workflow/specs/2026-10-13-sp.md', `---\n${fm}\n确认指纹: ${fp.slice(0, 16)}\n---\n${body}`);
|
|
3486
|
+
writeLedger(T, [{ ts: '2026-10-13T01:00:00.000Z', doc: 'workflow/specs/2026-10-13-sp.md', stage: 'done', fingerprint: fp, prev: 'approved', source: 'chat-delegated', batch: 't-21', seq: 1, of: 1 }]);
|
|
3487
|
+
gitCommitAllAt(T, 'spec fixture', '2026-10-13T01:00:00.000Z');
|
|
3488
|
+
return T;
|
|
3489
|
+
};
|
|
3490
|
+
let T = mkSpec('L2', ' - 人工走查确认\n');
|
|
3491
|
+
let r = run(T, { git: true });
|
|
3492
|
+
check('检查21 锚后 L2 done spec 验证节无引用 → WARN「spec 验证落地」',
|
|
3493
|
+
r.status === 0 && outOf(r).includes('spec 验证落地'), `exit=${r.status}\n${outOf(r)}`);
|
|
3494
|
+
rmfix(T);
|
|
3495
|
+
T = mkSpec('L2', ' - `node .agents/scripts/verify.mjs` 全绿\n');
|
|
3496
|
+
r = run(T, { git: true });
|
|
3497
|
+
check('检查21 验证节有 verify.mjs 引用 → 无 WARN',
|
|
3498
|
+
r.status === 0 && !outOf(r).includes('spec 验证落地'), `exit=${r.status}\n${outOf(r)}`);
|
|
3499
|
+
rmfix(T);
|
|
3500
|
+
T = mkSpec('L1', ' - 人工走查确认\n');
|
|
3501
|
+
r = run(T, { git: true });
|
|
3502
|
+
check('检查21 L1 spec 不核查',
|
|
3503
|
+
r.status === 0 && !outOf(r).includes('spec 验证落地'), `exit=${r.status}\n${outOf(r)}`);
|
|
3504
|
+
rmfix(T);
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
// ---- 检查 22:机制使用量台账(格式校验 + 待退役强制处置期限;台账文件在场才启用)----
|
|
3508
|
+
{
|
|
3509
|
+
const REG = (rows) => ['# 机制使用量台账', '', '| 机制 | 门禁位置 | 触发统计口径 | 退役阈值 | 最近触发 | 状态 | 处置期限 |', '|---|---|---|---|---|---|---|', ...rows].join('\n');
|
|
3510
|
+
const T0 = mkfix();
|
|
3511
|
+
w(T0, 'workflow/intents/2026-09-12-r0.md', INTENT('r0', '状态: draft\n级别: L1\n日期: 2026-09-12'));
|
|
3512
|
+
w(T0, 'workflow/plans/2026-09-12-r0.md', PLAN('r0', '状态: draft\n级别: L1'));
|
|
3513
|
+
let r = run(T0);
|
|
3514
|
+
check('检查22 台账文件缺失 → 跳过不告警(装户无感)',
|
|
3515
|
+
r.status === 0 && !outOf(r).includes('机制台账'), outOf(r));
|
|
3516
|
+
rmfix(T0);
|
|
3517
|
+
|
|
3518
|
+
const T1 = mkfix();
|
|
3519
|
+
w(T1, '.agents/mechanism-registry.md', REG([
|
|
3520
|
+
'| 三件套配对 | check-loop 检查 3 | WARN 计数 | 6月=0 | 2026-10 | 现役 | — |',
|
|
3521
|
+
'| 某低频机制 | check-loop 检查 9 | WARN 计数 0/219 | =0 | 2026-10-13 | 待退役 | B7 批次 |',
|
|
3522
|
+
]));
|
|
3523
|
+
r = run(T1);
|
|
3524
|
+
check('检查22 合法台账(现役+待退役带期限)→ 不告警',
|
|
3525
|
+
r.status === 0 && !outOf(r).includes('机制台账'), outOf(r));
|
|
3526
|
+
rmfix(T1);
|
|
3527
|
+
|
|
3528
|
+
const T2 = mkfix();
|
|
3529
|
+
w(T2, '.agents/mechanism-registry.md', REG([
|
|
3530
|
+
'| 某机制 | check-loop 检查 9 | WARN 计数 | 6月=0 | 2026-10 | 待退役 | |',
|
|
3531
|
+
]));
|
|
3532
|
+
r = run(T2);
|
|
3533
|
+
check('检查22 待退役缺处置期限 → WARN 强制处置',
|
|
3534
|
+
r.status === 0 && outOf(r).includes('待退役但缺处置期限'), outOf(r));
|
|
3535
|
+
rmfix(T2);
|
|
3536
|
+
|
|
3537
|
+
const T3 = mkfix();
|
|
3538
|
+
w(T3, '.agents/mechanism-registry.md', REG([
|
|
3539
|
+
'| 某机制 | check-loop 检查 9 | WARN 计数 | 6月=0 | 2026-10 | 已删除 | — |',
|
|
3540
|
+
'| 列数不足 | check-loop | — | — | — | 现役 |',
|
|
3541
|
+
]));
|
|
3542
|
+
r = run(T3);
|
|
3543
|
+
check('检查22 状态非法 + 列数≠7 → 双 WARN(格式防线)',
|
|
3544
|
+
r.status === 0 && outOf(r).includes('状态非法') && outOf(r).includes('行列数'), outOf(r));
|
|
3545
|
+
rmfix(T3);
|
|
3546
|
+
}
|
|
3547
|
+
|
|
3548
|
+
// ---- 检查 23:判据级收敛对账(advisory 档 convergenceSince=2026-10-13 / hard 档 convergenceHardSince=2026-10-14)----
|
|
3549
|
+
{
|
|
3550
|
+
const TABLE = (rows) => [
|
|
3551
|
+
'## 验收判据',
|
|
3552
|
+
'',
|
|
3553
|
+
'| 判据(可观察行为) | 证据(测试文件 / 命令 / 证据文件) | 状态 |',
|
|
3554
|
+
'|---|---|---|',
|
|
3555
|
+
...rows,
|
|
3556
|
+
].join('\n');
|
|
3557
|
+
const GREEN_ROW = (iso) => [
|
|
3558
|
+
'# 验证绿凭证摘要',
|
|
3559
|
+
'',
|
|
3560
|
+
'| 提交(短SHA) | 时间(UTC) | 门数 | 套件计数 | 绑定单 | runId |',
|
|
3561
|
+
'|---|---|---|---|---|---|',
|
|
3562
|
+
`| \`abc1234\` | ${iso} | 7 | PASS 3 / FAIL 0 | — | — |`,
|
|
3563
|
+
'',
|
|
3564
|
+
].join('\n');
|
|
3565
|
+
const mkSpec = (body, date = '2026-10-14', lvl = 'L2', pv = 12) => {
|
|
3566
|
+
const T = mkfix();
|
|
3567
|
+
gitInit(T);
|
|
3568
|
+
w(T, '.agents/kit.json', `{"policyVersion":${pv}}\n`);
|
|
3569
|
+
w(T, 'workflow/intents/2026-10-13-sp.md', INTENT('sp', '状态: draft\n级别: L1\n日期: 2026-10-13'));
|
|
3570
|
+
w(T, 'workflow/plans/2026-10-13-sp.md', PLAN('sp', '状态: draft\n级别: L1\n日期: 2026-09-01'));
|
|
3571
|
+
const full = `# SPEC — sp\n\n${body}\n`;
|
|
3572
|
+
const fp = computeFingerprint(`---\n状态: approved\n级别: ${lvl}\n日期: ${date}\n---\n${full}`);
|
|
3573
|
+
// 两笔提交复刻真实流:approved 留痕(检查 14)→ 置 done + 台账
|
|
3574
|
+
w(T, 'workflow/specs/2026-10-13-sp.md', `---\n状态: approved\n级别: ${lvl}\n日期: ${date}\n确认指纹: ${fp.slice(0, 16)}\n---\n${full}`);
|
|
3575
|
+
gitCommitAll(T, 'spec approved');
|
|
3576
|
+
w(T, 'workflow/specs/2026-10-13-sp.md', `---\n状态: done\n级别: ${lvl}\n日期: ${date}\n确认指纹: ${fp.slice(0, 16)}\n---\n${full}`);
|
|
3577
|
+
writeLedger(T, [{ ts: `${date}T01:00:00.000Z`, doc: 'workflow/specs/2026-10-13-sp.md', stage: 'done', fingerprint: fp, prev: 'approved', source: 'chat-delegated', batch: 't-23', seq: 1, of: 1 }]);
|
|
3578
|
+
gitCommitAll(T, 'spec done');
|
|
3579
|
+
return T;
|
|
3580
|
+
};
|
|
3581
|
+
// ① 判据全覆盖(全量门命令 + 存在文件,hard 档)→ 无收敛告警
|
|
3582
|
+
let T = mkSpec(TABLE([
|
|
3583
|
+
'| 全量套件绿 | npm test | 覆盖 |',
|
|
3584
|
+
'| 守卫拒绝超量 | src/guard.test.mjs | 覆盖 |',
|
|
3585
|
+
]));
|
|
3586
|
+
fs.mkdirSync(path.join(T, 'src'), { recursive: true });
|
|
3587
|
+
w(T, 'src/guard.test.mjs', '// fixture');
|
|
3588
|
+
w(T, 'workflow/verify-evidence.md', GREEN_ROW('2026-10-14T08:00:00.000Z'));
|
|
3589
|
+
let r = run(T, { git: true, args: [] });
|
|
3590
|
+
check('检查23 硬档判据全覆盖(命令+存在文件+当日全绿行)→ exit 0 无收敛告警',
|
|
3591
|
+
r.status === 0 && !outOf(r).includes('收敛'), `exit=${r.status}\n${outOf(r)}`);
|
|
3592
|
+
rmfix(T);
|
|
3593
|
+
// ② advisory 档:状态自报「覆盖」但文件不存在 → WARN 收敛未覆盖(完成声明不是证据;exit 0 即未升 hard)
|
|
3594
|
+
T = mkSpec(TABLE([
|
|
3595
|
+
'| 守卫拒绝超量 | src/nope.test.mjs | 覆盖 |',
|
|
3596
|
+
]), '2026-10-13');
|
|
3597
|
+
r = run(T, { git: true });
|
|
3598
|
+
check('检查23 advisory 档自报覆盖但证据不存在 → WARN「收敛未覆盖」',
|
|
3599
|
+
r.status === 0 && outOf(r).includes('[WARN 收敛未覆盖]'), `exit=${r.status}\n${outOf(r)}`);
|
|
3600
|
+
rmfix(T);
|
|
3601
|
+
// ③ 无验收判据节 → 收敛无判据(WARN)
|
|
3602
|
+
T = mkSpec('## 风险评估\n\n- 暂无\n', '2026-10-13');
|
|
3603
|
+
r = run(T, { git: true });
|
|
3604
|
+
check('检查23 无验收判据节 → WARN「收敛无判据」',
|
|
3605
|
+
r.status === 0 && outOf(r).includes('收敛无判据'), `exit=${r.status}\n${outOf(r)}`);
|
|
3606
|
+
rmfix(T);
|
|
3607
|
+
// ④ 表只有占位行 → 收敛无判据
|
|
3608
|
+
T = mkSpec(TABLE(['| <行为 1> | <路径或命令> | <覆盖 / 未覆盖 / 人工> |']), '2026-10-13');
|
|
3609
|
+
r = run(T, { git: true });
|
|
3610
|
+
check('检查23 表仅占位行 → WARN「收敛无判据」',
|
|
3611
|
+
r.status === 0 && outOf(r).includes('收敛无判据'), `exit=${r.status}\n${outOf(r)}`);
|
|
3612
|
+
rmfix(T);
|
|
3613
|
+
// ⑤ 人工判据空证据合法 + 命令声明(advisory 档认声明)→ 无告警
|
|
3614
|
+
T = mkSpec(TABLE([
|
|
3615
|
+
'| 视觉布局走查 | | 人工 |',
|
|
3616
|
+
'| 全量套件绿 | npm test | 覆盖 |',
|
|
3617
|
+
]), '2026-10-13');
|
|
3618
|
+
r = run(T, { git: true });
|
|
3619
|
+
check('检查23 人工判据(空证据)+ advisory 命令声明 → 无告警',
|
|
3620
|
+
r.status === 0 && !outOf(r).includes('收敛'), `exit=${r.status}\n${outOf(r)}`);
|
|
3621
|
+
rmfix(T);
|
|
3622
|
+
// ⑥ 锚前存量(日期 < convergenceSince)→ 豁免
|
|
3623
|
+
T = mkSpec('## 风险评估\n\n- 暂无\n', '2026-10-12');
|
|
3624
|
+
r = run(T, { git: true });
|
|
3625
|
+
check('检查23 锚前存量件 → 豁免',
|
|
3626
|
+
r.status === 0 && !outOf(r).includes('收敛'), `exit=${r.status}\n${outOf(r)}`);
|
|
3627
|
+
rmfix(T);
|
|
3628
|
+
// ⑦ v9(有 specVerif/attribution、无 convergenceSince)→ 检查整体跳过
|
|
3629
|
+
T = mkSpec('## 风险评估\n\n- 暂无\n', '2026-10-13', 'L2', 9);
|
|
3630
|
+
r = run(T, { git: true });
|
|
3631
|
+
check('检查23 v9 缺键 → 检查整体跳过',
|
|
3632
|
+
r.status === 0 && !outOf(r).includes('收敛'), `exit=${r.status}\n${outOf(r)}`);
|
|
3633
|
+
rmfix(T);
|
|
3634
|
+
// ⑧ v12 硬锚前一日(advisory 面)无效证据 → 仍 WARN 不升 hard
|
|
3635
|
+
T = mkSpec(TABLE([
|
|
3636
|
+
'| 守卫拒绝超量 | src/nope.test.mjs | 覆盖 |',
|
|
3637
|
+
]), '2026-10-13');
|
|
3638
|
+
r = run(T, { git: true, args: [] });
|
|
3639
|
+
check('检查23 硬锚前一日无效证据 → 默认硬门仍 WARN 不阻断',
|
|
3640
|
+
r.status === 0 && outOf(r).includes('[WARN 收敛未覆盖]'), `exit=${r.status}\n${outOf(r)}`);
|
|
3641
|
+
rmfix(T);
|
|
3642
|
+
// ⑨ 硬锚起:路径不存在 → 默认硬门 HARD
|
|
3643
|
+
T = mkSpec(TABLE([
|
|
3644
|
+
'| 守卫拒绝超量 | src/nope.test.mjs | 覆盖 |',
|
|
3645
|
+
]));
|
|
3646
|
+
r = run(T, { git: true, args: [] });
|
|
3647
|
+
check('检查23 硬锚起路径不存在 → 默认硬门 HARD「收敛未覆盖」',
|
|
3648
|
+
r.status !== 0 && outOf(r).includes('[收敛未覆盖]') && !outOf(r).includes('[WARN 收敛未覆盖]'), `exit=${r.status}\n${outOf(r)}`);
|
|
3649
|
+
rmfix(T);
|
|
3650
|
+
// ⑩ 硬锚起:全量门命令但无全绿行 → HARD(声明不是凭证)
|
|
3651
|
+
T = mkSpec(TABLE([
|
|
3652
|
+
'| 全量套件绿 | npm test | 覆盖 |',
|
|
3653
|
+
]));
|
|
3654
|
+
r = run(T, { git: true });
|
|
3655
|
+
check('检查23 硬锚起命令无全绿行 → HARD',
|
|
3656
|
+
r.status !== 0 && outOf(r).includes('[收敛未覆盖]'), `exit=${r.status}\n${outOf(r)}`);
|
|
3657
|
+
rmfix(T);
|
|
3658
|
+
// ⑪ 全绿行早于 spec 日期 → 仍 HARD
|
|
3659
|
+
T = mkSpec(TABLE([
|
|
3660
|
+
'| 全量套件绿 | npm test | 覆盖 |',
|
|
3661
|
+
]));
|
|
3662
|
+
w(T, 'workflow/verify-evidence.md', GREEN_ROW('2026-10-13T08:00:00.000Z'));
|
|
3663
|
+
r = run(T, { git: true });
|
|
3664
|
+
check('检查23 全绿行早于 spec 日期 → HARD',
|
|
3665
|
+
r.status !== 0 && outOf(r).includes('[收敛未覆盖]'), `exit=${r.status}\n${outOf(r)}`);
|
|
3666
|
+
rmfix(T);
|
|
3667
|
+
// ⑫ 硬锚起非全量门命令(无对应绿记录通道)→ HARD
|
|
3668
|
+
T = mkSpec(TABLE([
|
|
3669
|
+
'| 页面走查 | run-tests | 覆盖 |',
|
|
3670
|
+
]));
|
|
3671
|
+
w(T, 'workflow/verify-evidence.md', GREEN_ROW('2026-10-14T08:00:00.000Z'));
|
|
3672
|
+
r = run(T, { git: true });
|
|
3673
|
+
check('检查23 硬锚起非全量门命令有全绿行 → HARD',
|
|
3674
|
+
r.status !== 0 && outOf(r).includes('[收敛未覆盖]'), `exit=${r.status}\n${outOf(r)}`);
|
|
3675
|
+
rmfix(T);
|
|
3676
|
+
// ⑬ 硬锚起路径存在即覆盖(不读正文是否与判据相关)
|
|
3677
|
+
T = mkSpec(TABLE([
|
|
3678
|
+
'| 守卫拒绝超量 | src/guard.test.mjs | 覆盖 |',
|
|
3679
|
+
]));
|
|
3680
|
+
fs.mkdirSync(path.join(T, 'src'), { recursive: true });
|
|
3681
|
+
w(T, 'src/guard.test.mjs', '// fixture');
|
|
3682
|
+
r = run(T, { git: true });
|
|
3683
|
+
check('检查23 硬锚起路径存在 → 无收敛告警',
|
|
3684
|
+
r.status === 0 && !outOf(r).includes('收敛'), `exit=${r.status}\n${outOf(r)}`);
|
|
3685
|
+
rmfix(T);
|
|
3686
|
+
// ⑭ 硬锚起人工空证据仍合法 + 有当日全绿行的全量门命令 → 无告警;无判据表在硬锚日仍 WARN 不阻断
|
|
3687
|
+
T = mkSpec(TABLE([
|
|
3688
|
+
'| 视觉布局走查 | | 人工 |',
|
|
3689
|
+
'| 全量套件绿 | npm test | 覆盖 |',
|
|
3690
|
+
]));
|
|
3691
|
+
w(T, 'workflow/verify-evidence.md', GREEN_ROW('2026-10-14T08:00:00.000Z'));
|
|
3692
|
+
r = run(T, { git: true });
|
|
3693
|
+
check('检查23 硬锚起人工空证据 + 有凭证的命令 → 无告警',
|
|
3694
|
+
r.status === 0 && !outOf(r).includes('收敛'), `exit=${r.status}\n${outOf(r)}`);
|
|
3695
|
+
rmfix(T);
|
|
3696
|
+
T = mkSpec('## 风险评估\n\n- 暂无\n');
|
|
3697
|
+
r = run(T, { git: true });
|
|
3698
|
+
check('检查23 硬锚起无验收判据表 → 仍 WARN 不阻断',
|
|
3699
|
+
r.status === 0 && outOf(r).includes('[WARN 收敛无判据]'), `exit=${r.status}\n${outOf(r)}`);
|
|
3700
|
+
rmfix(T);
|
|
3701
|
+
}
|
|
3702
|
+
|
|
3703
|
+
// ---- 检查 24:incident 归因字段核查(锚 attributionSince=2026-10-13;发现日锚)----
|
|
3704
|
+
{
|
|
3705
|
+
const mkInc = (name, fm, withPlanDate = '2026-09-01') => {
|
|
3706
|
+
const T = mkfix();
|
|
3707
|
+
w(T, '.agents/kit.json', '{"policyVersion":12}\n'); // 锚经 kit.json policyVersion 供给(v1 缺 attributionSince → 整段跳过)
|
|
3708
|
+
// L1 配对:检查 3 要求 L1 incident 有同名 plan(否则 HARD 掩盖本段 WARN)
|
|
3709
|
+
w(T, `workflow/plans/${name}`, PLAN(name.replace(/\.md$/, ''), `状态: draft\n级别: L1\n日期: ${withPlanDate}`));
|
|
3710
|
+
w(T, `workflow/incidents/${name}`, `---\n${fm}\n---\n# INCIDENT\n\n## 复盘三件套(缺一不可)\n\n1. 结构性修复\n2. 防复发验证\n3. 规范条目\n- 是否需要新 intent:否\n`);
|
|
3711
|
+
return T;
|
|
3712
|
+
};
|
|
3713
|
+
let T = mkInc('2026-10-13-a.md', '状态: open\n级别: L1\n发现: 2026-10-13\n来源: user\n影响环境: dev');
|
|
3714
|
+
let r = run(T);
|
|
3715
|
+
check('检查24 锚后两键合法 → 无归因告警',
|
|
3716
|
+
r.status === 0 && !outOf(r).includes('归因'), `exit=${r.status}\n${outOf(r)}`);
|
|
3717
|
+
rmfix(T);
|
|
3718
|
+
T = mkInc('2026-10-13-b.md', '状态: open\n级别: L1\n发现: 2026-10-13');
|
|
3719
|
+
r = run(T);
|
|
3720
|
+
check('检查24 锚后缺来源/影响环境 → 双 WARN「归因缺字段」',
|
|
3721
|
+
r.status === 0 && (outOf(r).match(/归因缺字段/g) || []).length >= 2, `exit=${r.status}\n${outOf(r)}`);
|
|
3722
|
+
rmfix(T);
|
|
3723
|
+
T = mkInc('2026-10-13-c.md', '状态: open\n级别: L1\n发现: 2026-10-13\n来源: robot\n影响环境: qa');
|
|
3724
|
+
r = run(T);
|
|
3725
|
+
check('检查24 锚后值不在枚举 → WARN「归因非法值」',
|
|
3726
|
+
r.status === 0 && outOf(r).includes('归因非法值'), `exit=${r.status}\n${outOf(r)}`);
|
|
3727
|
+
rmfix(T);
|
|
3728
|
+
T = mkInc('2026-10-12-d.md', '状态: open\n级别: L1\n发现: 2026-10-12');
|
|
3729
|
+
r = run(T);
|
|
3730
|
+
check('检查24 锚前存量件缺键 → 豁免不回填',
|
|
3731
|
+
r.status === 0 && !outOf(r).includes('归因'), `exit=${r.status}\n${outOf(r)}`);
|
|
3732
|
+
rmfix(T);
|
|
3733
|
+
T = mkInc('2026-10-13-e.md', '状态: open\n级别: L1\n发现: 2026-10-13\n流程: legacy');
|
|
3734
|
+
r = run(T);
|
|
3735
|
+
check('检查24 legacy 回填件 → 豁免',
|
|
3736
|
+
r.status === 0 && !outOf(r).includes('归因'), `exit=${r.status}\n${outOf(r)}`);
|
|
3737
|
+
rmfix(T);
|
|
3738
|
+
}
|
|
3739
|
+
|
|
3740
|
+
// ---- 检查 25:路由预判一致性(advisory 纯 WARN;预判字段在场才检)----
|
|
3741
|
+
{
|
|
3742
|
+
// ① 一致 → 零告警
|
|
3743
|
+
const T = mkfix();
|
|
3744
|
+
w(T, 'workflow/intents/2026-09-12-pj1.md', INTENT('pj1', '状态: done\n级别: L1\n日期: 2026-09-12\n预判: kind=require level=L1', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
3745
|
+
w(T, 'workflow/plans/2026-09-12-pj1.md', PLAN('pj1', '状态: done\n级别: L1'));
|
|
3746
|
+
let r = run(T);
|
|
3747
|
+
check('检查25 预判与落位一致 → 零告警 exit 0',
|
|
3748
|
+
r.status === 0 && !outOf(r).includes('预判'), `exit=${r.status}\n${outOf(r)}`);
|
|
3749
|
+
rmfix(T);
|
|
3750
|
+
// ② 类型不符:intent(≡require)预判 fix;incident(≡fix)预判 require → 双 WARN 不阻断
|
|
3751
|
+
const T2 = mkfix();
|
|
3752
|
+
w(T2, 'workflow/intents/2026-09-12-pj2.md', INTENT('pj2', '状态: done\n级别: L1\n日期: 2026-09-12\n预判: kind=fix level=L1', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
3753
|
+
w(T2, 'workflow/plans/2026-09-12-pj2.md', PLAN('pj2', '状态: done\n级别: L1'));
|
|
3754
|
+
w(T2, 'workflow/incidents/2026-09-12-pj3.md', `---\n状态: open\n级别: L1\n发现: 2026-09-12\n预判: kind=require level=L1\n---\n# INCIDENT — pj3\n\n${三件套(false)}`);
|
|
3755
|
+
w(T2, 'workflow/plans/2026-09-12-pj3.md', PLAN('pj3', '状态: draft\n级别: L1'));
|
|
3756
|
+
r = run(T2);
|
|
3757
|
+
check('检查25 类型不符(intent 预判 fix / incident 预判 require)→ 双 WARN「预判类型不符」且 exit 0',
|
|
3758
|
+
r.status === 0 && (outOf(r).match(/预判类型不符/g) || []).length === 2, `exit=${r.status}\n${outOf(r)}`);
|
|
3759
|
+
rmfix(T2);
|
|
3760
|
+
// ③ 级别不符:预判 L2 落位 L1 → WARN「预判级别不符」含判低信号
|
|
3761
|
+
const T3 = mkfix();
|
|
3762
|
+
w(T3, 'workflow/intents/2026-09-12-pj4.md', INTENT('pj4', '状态: done\n级别: L1\n日期: 2026-09-12\n预判: kind=require level=L2', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
3763
|
+
w(T3, 'workflow/plans/2026-09-12-pj4.md', PLAN('pj4', '状态: done\n级别: L1'));
|
|
3764
|
+
r = run(T3);
|
|
3765
|
+
check('检查25 预判 L2 落位 L1 → WARN「预判级别不符」含判低信号',
|
|
3766
|
+
r.status === 0 && outOf(r).includes('预判级别不符') && outOf(r).includes('判低信号'), `exit=${r.status}\n${outOf(r)}`);
|
|
3767
|
+
rmfix(T3);
|
|
3768
|
+
// ④ 词表非法 kind / 整字段不可解析 → WARN「预判字段不可解析」
|
|
3769
|
+
const T4 = mkfix();
|
|
3770
|
+
w(T4, 'workflow/intents/2026-09-12-pj5.md', INTENT('pj5', '状态: done\n级别: L1\n日期: 2026-09-12\n预判: kind=bug level=L1', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
3771
|
+
w(T4, 'workflow/plans/2026-09-12-pj5.md', PLAN('pj5', '状态: done\n级别: L1'));
|
|
3772
|
+
r = run(T4);
|
|
3773
|
+
check('检查25 kind=bug 不在词表 → WARN「预判字段不可解析」',
|
|
3774
|
+
r.status === 0 && outOf(r).includes('预判字段不可解析'), `exit=${r.status}\n${outOf(r)}`);
|
|
3775
|
+
rmfix(T4);
|
|
3776
|
+
const T5 = mkfix();
|
|
3777
|
+
w(T5, 'workflow/intents/2026-09-12-pj6.md', INTENT('pj6', '状态: done\n级别: L1\n日期: 2026-09-12\n预判: 这是个修复', '\n## 验收标准(可测试)\n- [x] 用例通过(证据:fixture)\n'));
|
|
3778
|
+
w(T5, 'workflow/plans/2026-09-12-pj6.md', PLAN('pj6', '状态: done\n级别: L1'));
|
|
3779
|
+
r = run(T5);
|
|
3780
|
+
check('检查25 预判字段无 kv 形态 → WARN「预判字段不可解析」',
|
|
3781
|
+
r.status === 0 && outOf(r).includes('预判字段不可解析'), `exit=${r.status}\n${outOf(r)}`);
|
|
3782
|
+
rmfix(T5);
|
|
3783
|
+
}
|
|
3784
|
+
|
|
3785
|
+
// ---- 检查 26:闭环漏斗「协议前」占比动作阈值(相邻两月均 >30% → WARN;消费 gen-workflow-metrics 产物)----
|
|
3786
|
+
{
|
|
3787
|
+
// 漏斗表构造器(kit gen-workflow-metrics.mjs 产出的表头/节名;单元格带占比与裸数字两种形态都测)
|
|
3788
|
+
const mkMetrics = (rows) => `<!-- GENERATED:BEGIN -->
|
|
3789
|
+
|
|
3790
|
+
## 闭环漏斗(机器口径,源:.agents/confirmations.jsonl)
|
|
3791
|
+
|
|
3792
|
+
| 月份 | 收口 | 完整链 | 协议前 | 一次通过 | 返工件 | 中位周期(天) | 台账行数 |
|
|
3793
|
+
|---|---|---|---|---|---|---|---|
|
|
3794
|
+
${rows.map((r) => `| ${r} |`).join('\n')}
|
|
3795
|
+
|
|
3796
|
+
## 来源与影响环境维度(源:incident frontmatter 枚举)
|
|
3797
|
+
|
|
3798
|
+
| 月份 | incident | 来源(谁发现) | 影响环境 |
|
|
3799
|
+
|---|---|---|---|
|
|
3800
|
+
| 2026-10 | 0 | self 0 / user 0 / test 0 / monitor 0 | dev 0 / staging 0 / prod 0 / none 0 |
|
|
3801
|
+
|
|
3802
|
+
<!-- GENERATED:END -->
|
|
3803
|
+
`;
|
|
3804
|
+
// ① 无 metrics.md(装户/新仓未启用观测)→ 静默跳过零告警
|
|
3805
|
+
const T = mkfix();
|
|
3806
|
+
let r = run(T);
|
|
3807
|
+
check('检查26 无 metrics.md → 跳过零告警 exit 0',
|
|
3808
|
+
r.status === 0 && !outOf(r).includes('漏斗协议前超标'), `exit=${r.status}\n${outOf(r)}`);
|
|
3809
|
+
rmfix(T);
|
|
3810
|
+
// ② 相邻两月均 >30% → WARN「漏斗协议前超标」且不阻断(exit 0)
|
|
3811
|
+
const T2 = mkfix();
|
|
3812
|
+
w(T2, 'workflow/metrics.md', mkMetrics([
|
|
3813
|
+
'2026-09 | 10 | 4 | 6(60%) ⚠️ | 3 | 1 | 1.0 | 20',
|
|
3814
|
+
'2026-10 | 10 | 4 | 5(50%) ⚠️ | 4 | 1 | 1.0 | 20',
|
|
3815
|
+
]));
|
|
3816
|
+
r = run(T2);
|
|
3817
|
+
check('检查26 相邻两月协议前占比均 >30% → WARN 且 exit 0',
|
|
3818
|
+
r.status === 0 && outOf(r).includes('漏斗协议前超标') && outOf(r).includes('2026-09 60% → 2026-10 50%'), `exit=${r.status}\n${outOf(r)}`);
|
|
3819
|
+
rmfix(T2);
|
|
3820
|
+
// ③ 仅单月 >30%(相邻月正常)→ 零告警
|
|
3821
|
+
const T3 = mkfix();
|
|
3822
|
+
w(T3, 'workflow/metrics.md', mkMetrics([
|
|
3823
|
+
'2026-09 | 10 | 9 | 1(10%) | 8 | 1 | 1.0 | 20',
|
|
3824
|
+
'2026-10 | 10 | 4 | 5(50%) ⚠️ | 4 | 1 | 1.0 | 20',
|
|
3825
|
+
]));
|
|
3826
|
+
r = run(T3);
|
|
3827
|
+
check('检查26 仅单月超标(相邻月正常)→ 零告警 exit 0',
|
|
3828
|
+
r.status === 0 && !outOf(r).includes('漏斗协议前超标'), `exit=${r.status}\n${outOf(r)}`);
|
|
3829
|
+
rmfix(T3);
|
|
3830
|
+
// ④ 两月都超标但不相邻(跳月 09→11)→ 零告警(判据是日历相邻)
|
|
3831
|
+
const T4 = mkfix();
|
|
3832
|
+
w(T4, 'workflow/metrics.md', mkMetrics([
|
|
3833
|
+
'2026-09 | 10 | 4 | 6(60%) ⚠️ | 3 | 1 | 1.0 | 20',
|
|
3834
|
+
'2026-11 | 10 | 4 | 5(50%) ⚠️ | 4 | 1 | 1.0 | 20',
|
|
3835
|
+
]));
|
|
3836
|
+
r = run(T4);
|
|
3837
|
+
check('检查26 超标两月不相邻(跳月)→ 零告警 exit 0',
|
|
3838
|
+
r.status === 0 && !outOf(r).includes('漏斗协议前超标'), `exit=${r.status}\n${outOf(r)}`);
|
|
3839
|
+
rmfix(T4);
|
|
3840
|
+
// ⑤ 收口=0 的月无占比(0/0 不判),且存量裸数字单元格同样可解析
|
|
3841
|
+
const T5 = mkfix();
|
|
3842
|
+
w(T5, 'workflow/metrics.md', mkMetrics([
|
|
3843
|
+
'2026-09 | 0 | 0 | 0 | 0 | 0 | — | 1',
|
|
3844
|
+
'2026-10 | 10 | 4 | 5 | 4 | 1 | 1.0 | 20',
|
|
3845
|
+
]));
|
|
3846
|
+
r = run(T5);
|
|
3847
|
+
check('检查26 收口=0 月不判 0/0;裸数字单元格可解析(10 月 50% 单月不触发)',
|
|
3848
|
+
r.status === 0 && !outOf(r).includes('漏斗协议前超标'), `exit=${r.status}\n${outOf(r)}`);
|
|
3849
|
+
rmfix(T5);
|
|
3850
|
+
// ⑥ 存量裸数字 + 相邻两月超标 → 照判(解析只取首个整数,混合形态可读)
|
|
3851
|
+
const T6 = mkfix();
|
|
3852
|
+
w(T6, 'workflow/metrics.md', mkMetrics([
|
|
3853
|
+
'2026-09 | 10 | 4 | 6 | 3 | 1 | 1.0 | 20',
|
|
3854
|
+
'2026-10 | 10 | 4 | 5 | 4 | 1 | 1.0 | 20',
|
|
3855
|
+
]));
|
|
3856
|
+
r = run(T6);
|
|
3857
|
+
check('检查26 存量裸数字单元格相邻两月超标 → 照判 WARN',
|
|
3858
|
+
r.status === 0 && outOf(r).includes('漏斗协议前超标') && outOf(r).includes('2026-09 60% → 2026-10 50%'), `exit=${r.status}\n${outOf(r)}`);
|
|
3859
|
+
rmfix(T6);
|
|
3860
|
+
}
|
|
3861
|
+
|
|
3336
3862
|
console.log(`\n合计: PASS ${pass} / FAIL ${fail}`);
|
|
3337
3863
|
process.exit(fail ? 1 : 0);
|