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.
Files changed (206) hide show
  1. package/README.md +50 -8
  2. package/modules/hosts/claude/commands/wf-build.md +1 -1
  3. package/modules/hosts/claude/commands/wf-design.md +5 -4
  4. package/modules/hosts/claude/commands/wf-maintain.md +1 -1
  5. package/modules/hosts/claude/commands/wf-new-task.md +47 -25
  6. package/modules/hosts/claude/commands/wf-plan.md +1 -1
  7. package/modules/hosts/claude/commands/wf-review.md +2 -2
  8. package/modules/hosts/claude/commands/wf-sync-hosts.md +7 -13
  9. package/modules/hosts/claude/commands/wf-test.md +2 -2
  10. package/modules/hosts/codex/commands/wf-build.md +1 -1
  11. package/modules/hosts/codex/commands/wf-design.md +5 -4
  12. package/modules/hosts/codex/commands/wf-maintain.md +1 -1
  13. package/modules/hosts/codex/commands/wf-new-task.md +47 -25
  14. package/modules/hosts/codex/commands/wf-plan.md +1 -1
  15. package/modules/hosts/codex/commands/wf-review.md +2 -2
  16. package/modules/hosts/codex/commands/wf-sync-hosts.md +7 -13
  17. package/modules/hosts/codex/commands/wf-test.md +2 -2
  18. package/modules/hosts/cursor/commands/wf-build.md +1 -1
  19. package/modules/hosts/cursor/commands/wf-design.md +5 -4
  20. package/modules/hosts/cursor/commands/wf-maintain.md +1 -1
  21. package/modules/hosts/cursor/commands/wf-new-task.md +47 -25
  22. package/modules/hosts/cursor/commands/wf-plan.md +1 -1
  23. package/modules/hosts/cursor/commands/wf-review.md +2 -2
  24. package/modules/hosts/cursor/commands/wf-sync-hosts.md +7 -13
  25. package/modules/hosts/cursor/commands/wf-test.md +2 -2
  26. package/modules/hosts/opencode/commands/wf-build.md +1 -1
  27. package/modules/hosts/opencode/commands/wf-design.md +5 -4
  28. package/modules/hosts/opencode/commands/wf-maintain.md +1 -1
  29. package/modules/hosts/opencode/commands/wf-new-task.md +47 -25
  30. package/modules/hosts/opencode/commands/wf-plan.md +1 -1
  31. package/modules/hosts/opencode/commands/wf-review.md +2 -2
  32. package/modules/hosts/opencode/commands/wf-sync-hosts.md +7 -13
  33. package/modules/hosts/opencode/commands/wf-test.md +2 -2
  34. package/modules/hosts/trae/commands/wf-build.md +1 -1
  35. package/modules/hosts/trae/commands/wf-design.md +5 -4
  36. package/modules/hosts/trae/commands/wf-maintain.md +1 -1
  37. package/modules/hosts/trae/commands/wf-new-task.md +47 -25
  38. package/modules/hosts/trae/commands/wf-plan.md +1 -1
  39. package/modules/hosts/trae/commands/wf-review.md +2 -2
  40. package/modules/hosts/trae/commands/wf-sync-hosts.md +7 -13
  41. package/modules/hosts/trae/commands/wf-test.md +2 -2
  42. package/package.json +1 -1
  43. package/src/cli.mjs +47 -4
  44. package/src/doctor.mjs +48 -3
  45. package/src/executor.mjs +262 -0
  46. package/src/executor.test.mjs +295 -0
  47. package/src/fresh-init.test.mjs +1 -1
  48. package/src/init.mjs +27 -3
  49. package/src/profiles.mjs +10 -3
  50. package/src/render.mjs +3 -3
  51. package/src/rule-budget.test.mjs +2 -2
  52. package/src/stack-profile.test.mjs +6 -0
  53. package/src/sync-hosts.mjs +37 -43
  54. package/src/sync-hosts.test.mjs +67 -56
  55. package/src/validate.mjs +6 -1
  56. package/src/validate.test.mjs +1 -1
  57. package/templates/.gitattributes +12 -1
  58. package/templates/AGENTS.md +3 -3
  59. package/templates/_agents/commands/build.md +1 -1
  60. package/templates/_agents/commands/design.md +5 -4
  61. package/templates/_agents/commands/maintain.md +1 -1
  62. package/templates/_agents/commands/new-task.md +47 -25
  63. package/templates/_agents/commands/plan.md +1 -1
  64. package/templates/_agents/commands/review.md +2 -2
  65. package/templates/_agents/commands/sync-hosts.md +7 -13
  66. package/templates/_agents/commands/test.md +2 -2
  67. package/templates/_agents/control-surfaces.txt +1 -1
  68. package/templates/_agents/engine-lock.json +334 -0
  69. package/templates/_agents/hooks/check-index-incremental.mjs +48 -0
  70. package/templates/_agents/hooks/check-pairing-incremental.mjs +66 -0
  71. package/templates/_agents/hooks/check-wiki-ledger.mjs +30 -0
  72. package/templates/_agents/lane-surfaces.txt +5 -2
  73. package/templates/_agents/level-hints.txt +30 -0
  74. package/templates/_agents/loop/papercut-debt/DESIGN.md +112 -0
  75. package/templates/_agents/mechanism-registry.md +27 -0
  76. package/templates/_agents/rule-budgets.txt +2 -2
  77. package/templates/_agents/scripts/agg-delegations.cjs +21 -3
  78. package/templates/_agents/scripts/agg-delegations.test.mjs +5 -3
  79. package/templates/_agents/scripts/agg-gate-stats.mjs +3 -3
  80. package/templates/_agents/scripts/agg-judgments.mjs +80 -0
  81. package/templates/_agents/scripts/agg-judgments.test.mjs +45 -0
  82. package/templates/_agents/scripts/archive-sweep.mjs +233 -0
  83. package/templates/_agents/scripts/archive-sweep.test.mjs +129 -0
  84. package/templates/_agents/scripts/check-attribution.mjs +37 -0
  85. package/templates/_agents/scripts/check-convergence.mjs +119 -0
  86. package/templates/_agents/scripts/check-engine-integrity.mjs +59 -127
  87. package/templates/_agents/scripts/check-engine-integrity.test.mjs +33 -45
  88. package/templates/_agents/scripts/check-funnel.mjs +56 -0
  89. package/templates/_agents/scripts/check-hygiene.mjs +5 -5
  90. package/templates/_agents/scripts/check-index-incremental.test.mjs +73 -0
  91. package/templates/_agents/scripts/check-lane-surface.mjs +74 -25
  92. package/templates/_agents/scripts/check-lane-surface.test.mjs +71 -5
  93. package/templates/_agents/scripts/check-ledger-invariant.mjs +427 -78
  94. package/templates/_agents/scripts/check-ledger-invariant.test.mjs +438 -4
  95. package/templates/_agents/scripts/check-loop-rev.test.mjs +13 -8
  96. package/templates/_agents/scripts/check-loop-unit.test.mjs +8 -0
  97. package/templates/_agents/scripts/check-loop.mjs +264 -75
  98. package/templates/_agents/scripts/check-loop.test.mjs +587 -61
  99. package/templates/_agents/scripts/check-metric-claims.mjs +4 -2
  100. package/templates/_agents/scripts/check-prejudgment.mjs +59 -0
  101. package/templates/_agents/scripts/check-registry.mjs +45 -0
  102. package/templates/_agents/scripts/check-scope.mjs +96 -0
  103. package/templates/_agents/scripts/check-scope.test.mjs +96 -0
  104. package/templates/_agents/scripts/check-scripts-syntax.mjs +61 -0
  105. package/templates/_agents/scripts/check-scripts-syntax.test.mjs +72 -0
  106. package/templates/_agents/scripts/check-spec-verif.mjs +47 -0
  107. package/templates/_agents/scripts/compact-ledgers.mjs +97 -0
  108. package/templates/_agents/scripts/compact-ledgers.test.mjs +162 -0
  109. package/templates/_agents/scripts/confirm-doc.mjs +136 -31
  110. package/templates/_agents/scripts/confirm-doc.test.mjs +180 -4
  111. package/templates/_agents/scripts/doctor.test.mjs +44 -1
  112. package/templates/_agents/scripts/edit-face-check.mjs +5 -3
  113. package/templates/_agents/scripts/edit-face-check.test.mjs +5 -4
  114. package/templates/_agents/scripts/fill-intent.mjs +7 -5
  115. package/templates/_agents/scripts/fill-intent.test.mjs +19 -1
  116. package/templates/_agents/scripts/fill-plan.mjs +10 -6
  117. package/templates/_agents/scripts/fill-plan.test.mjs +19 -3
  118. package/templates/_agents/scripts/fill-spec.mjs +6 -3
  119. package/templates/_agents/scripts/fill-spec.test.mjs +12 -4
  120. package/templates/_agents/scripts/fixture-scripts.mjs +153 -0
  121. package/templates/_agents/scripts/fixture-scripts.test.mjs +112 -0
  122. package/templates/_agents/scripts/gate-checklist.mjs +10 -9
  123. package/templates/_agents/scripts/gate-checklist.test.mjs +2 -11
  124. package/templates/_agents/scripts/gate-seg.mjs +1 -1
  125. package/templates/_agents/scripts/gen-effective-policy.mjs +107 -0
  126. package/templates/_agents/scripts/gen-effective-policy.test.mjs +100 -0
  127. package/templates/_agents/scripts/gen-verify-evidence.mjs +107 -0
  128. package/templates/_agents/scripts/gen-verify-evidence.test.mjs +84 -0
  129. package/templates/_agents/scripts/gen-workflow-dashboard.mjs +16 -14
  130. package/templates/_agents/scripts/gen-workflow-dashboard.test.mjs +2 -1
  131. package/templates/_agents/scripts/gen-workflow-metrics.mjs +12 -36
  132. package/templates/_agents/scripts/git-core.mjs +45 -0
  133. package/templates/_agents/scripts/git-merge-jsonl.mjs +63 -0
  134. package/templates/_agents/scripts/git-merge-jsonl.test.mjs +125 -0
  135. package/templates/_agents/scripts/judgment-ledger.mjs +65 -0
  136. package/templates/_agents/scripts/judgment-ledger.test.mjs +129 -0
  137. package/templates/_agents/scripts/kb-search.mjs +19 -4
  138. package/templates/_agents/scripts/kb-search.test.mjs +33 -0
  139. package/templates/_agents/scripts/lane-surface-audit.mjs +143 -0
  140. package/templates/_agents/scripts/lane-surface-audit.test.mjs +93 -0
  141. package/templates/_agents/scripts/ledger-doc-chain.test.mjs +166 -0
  142. package/templates/_agents/scripts/ledger-loader.mjs +108 -0
  143. package/templates/_agents/scripts/ledger-loader.test.mjs +131 -0
  144. package/templates/_agents/scripts/ledger-prune.mjs +96 -0
  145. package/templates/_agents/scripts/ledger-prune.test.mjs +120 -0
  146. package/templates/_agents/scripts/ledger-shards.mjs +307 -0
  147. package/templates/_agents/scripts/ledger-shards.test.mjs +339 -0
  148. package/templates/_agents/scripts/ledger-ts-monotonic.test.mjs +135 -0
  149. package/templates/_agents/scripts/level-criteria.mjs +231 -0
  150. package/templates/_agents/scripts/level-criteria.test.mjs +281 -0
  151. package/templates/_agents/scripts/papercut-debt-controller.mjs +141 -0
  152. package/templates/_agents/scripts/papercut-debt-controller.test.mjs +127 -0
  153. package/templates/_agents/scripts/papercut-debt-sensor.mjs +170 -0
  154. package/templates/_agents/scripts/papercut-debt-sensor.test.mjs +146 -0
  155. package/templates/_agents/scripts/pipeline-run.mjs +10 -24
  156. package/templates/_agents/scripts/pipeline-run.test.mjs +22 -42
  157. package/templates/_agents/scripts/policy.mjs +75 -0
  158. package/templates/_agents/scripts/resolve-merge-conflicts.mjs +110 -0
  159. package/templates/_agents/scripts/resolve-merge-conflicts.test.mjs +87 -0
  160. package/templates/_agents/scripts/rewrite-file.mjs +133 -0
  161. package/templates/_agents/scripts/rewrite-file.test.mjs +148 -0
  162. package/templates/_agents/scripts/rule-budget.mjs +83 -0
  163. package/templates/_agents/scripts/run-tests.mjs +99 -0
  164. package/templates/_agents/scripts/run-tests.test.mjs +105 -0
  165. package/templates/_agents/scripts/solidify-task.test.mjs +1 -1
  166. package/templates/_agents/scripts/stage-gates.mjs +38 -21
  167. package/templates/_agents/scripts/trust-mode.mjs +4 -4
  168. package/templates/_agents/scripts/trust-mode.test.mjs +1 -1
  169. package/templates/_agents/scripts/verify.mjs +135 -58
  170. package/templates/_agents/scripts/verify.test.mjs +156 -47
  171. package/templates/_agents/scripts/wf-journal.mjs +8 -4
  172. package/templates/_agents/scripts/wf-journal.test.mjs +1 -1
  173. package/templates/_agents/scripts/wiki-search.test.mjs +5 -6
  174. package/templates/_agents/scripts/workflow-board-server.test.mjs +2 -2
  175. package/templates/_agents/scripts/workflow-enums.test.mjs +14 -5
  176. package/templates/_agents/scripts/workflows-check.mjs +1 -1
  177. package/templates/_agents/scripts-test-exempt.txt +1 -0
  178. package/templates/_agents/skills/papercut-fix/SKILL.md +60 -0
  179. package/templates/_agents/skills/papercut-fix/references/response-template.md +81 -0
  180. package/templates/_agents/workflow-enums.txt +8 -1
  181. package/templates/_agents/workflows/pipeline-closing.md +4 -5
  182. package/templates/_githooks/pre-commit +109 -120
  183. package/templates/_githooks/pre-merge-commit +27 -23
  184. package/templates/_githooks/pre-push +7 -6
  185. package/templates/_github/workflows/kit-ci.yml +4 -3
  186. package/templates/workflow/README.md +23 -5
  187. package/templates/workflow/incidents/_TEMPLATE.md +4 -1
  188. package/templates/workflow/intents/_TEMPLATE.md +4 -2
  189. package/templates/workflow/papercuts.md +2 -0
  190. package/templates/workflow/plans/_TEMPLATE.md +10 -5
  191. package/templates/workflow/regression-checklist.md +1 -0
  192. package/templates/workflow/specs/_TEMPLATE.md +17 -1
  193. package/modules/hosts/claude/commands/wf-source-sync-check.md +0 -89
  194. package/modules/hosts/codex/commands/wf-source-sync-check.md +0 -89
  195. package/modules/hosts/cursor/commands/wf-source-sync-check.md +0 -89
  196. package/modules/hosts/opencode/commands/wf-source-sync-check.md +0 -89
  197. package/modules/hosts/trae/commands/wf-source-sync-check.md +0 -90
  198. package/templates/_agents/commands/source-sync-check.md +0 -103
  199. package/templates/_agents/hooks/check-pairing-incremental.sh +0 -43
  200. package/templates/_agents/hooks/check-wiki-ledger.sh +0 -18
  201. package/templates/_agents/scripts/check-ledger.mjs +0 -109
  202. package/templates/_agents/scripts/check-ledger.test.mjs +0 -199
  203. package/templates/_agents/scripts/rule-budget.sh +0 -74
  204. package/templates/_agents/scripts/source-sync-check.mjs +0 -156
  205. package/templates/_agents/scripts/source-sync-check.test.mjs +0 -286
  206. package/templates/_agents/workflows/source-sync-repair.md +0 -30
@@ -1,16 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  // verify.mjs 的 fixture 测试(--test-cmd 注入假命令 + CHECK_LOOP_ROOT 注入夹具根,不触真实 npm test 与 workflow)
3
- // 断言:①绿路径(假过 + 空夹具闭环)exit 0;②步骤 1 假败 exit 1 且不出全绿;
4
- // ③步骤 2 配对断裂夹具 exit 1(verify 自身标记断言——孙进程 stdio inherit 输出不进本测试管道);
5
- // ④⑤⑥凭证落账三态(绿落 / check-loop 红不落 / 无汇总行省略计数键);
6
- // ⑦装户仓(cwd 无 package.json)未给 --test-cmd → 跳过步骤 1、不落凭证、仍跑步骤 2、exit 0;
7
- // ⑧--doc 绑定五态;⑨⑩嵌套套件同形汇总行不得被误取(计数口径=编排器权威套件数,2026-10-09 审查 P0-2 回归锁)。
3
+ // 断言:①绿路径(假过 + 空夹具闭环)exit 0;②步骤假败 exit 1 且失败行留痕(suite=test);
4
+ // ③步骤 2 配对断裂夹具 exit 1(verify 自身标记断言);④凭证落账(绿行 + 计数捕获);
5
+ // ④b fixture 绿不产 committed 摘要;⑤check-loop 红失败行留痕、无绿行;
6
+ // ⑥无汇总行省略计数键;⑦装户仓(cwd 无 package.json)跳过测试门、不落凭证、其余门全过 → exit 0;
7
+ // ⑧--doc 绑定五态;⑨⑩嵌套套件同形汇总行不得被误取;⑪--list 七门序;⑫--list 与 --test-cmd 互斥;
8
+ // ⑬生产模式中段红 → fail-fast + 未执行门清单 + 失败行留痕;
9
+ // ⑭生产模式全绿端到端(七门 + 凭证含门数/headShort/套件计数 + 绿凭证摘要生成)。
10
+ // 生产模式夹具经 fixture-scripts.mjs#copyScriptClosure 把门脚本依赖闭包拷进临时根(并种子
11
+ // workflow/effective-policy.md——生效规则视图门对缺文件判漂移,与 init 生成器首跑同款)。
8
12
  // 用法:node templates/_agents/scripts/verify.test.mjs
9
13
  import fs from 'node:fs';
10
14
  import os from 'node:os';
11
15
  import path from 'node:path';
12
16
  import { spawnSync } from 'node:child_process';
13
17
  import { fileURLToPath } from 'node:url';
18
+ import { copyScriptClosure } from './fixture-scripts.mjs';
14
19
 
15
20
  const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
16
21
  const VERIFY = path.join(SCRIPT_DIR, 'verify.mjs');
@@ -35,7 +40,7 @@ const mkfix = () => {
35
40
  'doc.status.abandoned=superseded cancelled',
36
41
  'incident.status.all=open fixed closed',
37
42
  'incident.status.active=open',
38
- 'level.all=L0 L1 L2 L3',
43
+ 'level.all=L1 L2 L3',
39
44
  '',
40
45
  ].join('\n'), 'utf8');
41
46
  return d;
@@ -57,6 +62,33 @@ const mkCmd = (code, out = '') => {
57
62
  };
58
63
  const run = (argv, opts = {}) => spawnSync(process.execPath, [VERIFY, ...argv], { encoding: 'utf8', ...opts });
59
64
 
65
+ // 生产模式夹具:门脚本依赖闭包拷贝 + .agents/hooks 目录(语法门 fail-closed 要求)+
66
+ // workflow/effective-policy.md 种子(生效规则视图门对缺文件判漂移——与 init §6 生成器首跑同款)
67
+ const provision = (root) => {
68
+ fs.mkdirSync(path.join(root, '.agents', 'hooks'), { recursive: true });
69
+ copyScriptClosure(path.join(root, '.agents', 'scripts'), [
70
+ 'check-scripts-syntax.mjs', 'workflows-check.mjs', 'rule-budget.mjs',
71
+ 'gen-effective-policy.mjs', 'check-ledger-invariant.mjs', 'check-loop.mjs',
72
+ ]);
73
+ const gen = spawnSync(process.execPath, [path.join(root, '.agents', 'scripts', 'gen-effective-policy.mjs'), '--root', root], { encoding: 'utf8' });
74
+ if (gen.status !== 0) throw new Error(`夹具种子生成失败:${gen.stdout}${gen.stderr}`);
75
+ return root;
76
+ };
77
+ // 生产夹具变 git 仓(台账不变量门在非 git 根会 fail-closed;commit 供凭证 headShort 钉 SHA)
78
+ const gitify = (root) => {
79
+ const g = (a) => spawnSync('git', ['-C', root, ...a], { encoding: 'utf8' });
80
+ g(['init']);
81
+ g(['-c', 'user.email=fixture@test', '-c', 'user.name=fixture', 'add', '-A']);
82
+ const c = g(['-c', 'user.email=fixture@test', '-c', 'user.name=fixture', 'commit', '-m', 'fixture init', '--no-gpg-sign', '-q']);
83
+ if (c.status !== 0) throw new Error(`夹具 git commit 失败:${c.stderr}`);
84
+ return g(['rev-parse', '--short', 'HEAD']).stdout.trim();
85
+ };
86
+ const readLedger = (root) => {
87
+ const p = path.join(root, '.agents', 'verifications.jsonl');
88
+ if (!fs.existsSync(p)) return [];
89
+ return fs.readFileSync(p, 'utf8').split(/\r?\n/).filter(Boolean).map((l) => JSON.parse(l));
90
+ };
91
+
60
92
  // ---- 场景 1:绿路径——假过 + 空夹具闭环 → exit 0 ----
61
93
  {
62
94
  const root = mkfix();
@@ -66,12 +98,16 @@ const run = (argv, opts = {}) => spawnSync(process.execPath, [VERIFY, ...argv],
66
98
  fs.rmSync(root, { recursive: true, force: true });
67
99
  }
68
100
 
69
- // ---- 场景 2:红路径步骤 1——假败 → exit 1、fail-fast 不出全绿 ----
101
+ // ---- 场景 2:红路径测试门——假败 → exit 1、fail-fast 不出全绿;失败行留痕 ----
70
102
  {
71
103
  const root = mkfix();
72
104
  const r = run(['--test-cmd', `node ${mkCmd(3)}`], { env: { ...process.env, CHECK_LOOP_ROOT: root } });
73
105
  check('场景 2:exit 1', r.status === 1, `exit=${r.status}\n${r.stdout}\n${r.stderr}`);
74
- check('场景 2:步骤 1 失败标记在 stderr,且不输出全绿', r.stderr.includes('❌ 1/2') && !r.stdout.includes('全绿'), `${r.stdout}\n${r.stderr}`);
106
+ check('场景 2:步骤失败标记在 stderr,且不输出全绿', r.stderr.includes('❌ 1/2') && !r.stdout.includes('全绿'), `${r.stdout}\n${r.stderr}`);
107
+ const lines = readLedger(root);
108
+ check('场景 2:测试门红 → 失败行留痕(exitCode 3 + suite=test)',
109
+ lines.length === 1 && lines[0].exitCode === 3 && lines[0].suite === 'test' && typeof lines[0].ts === 'string',
110
+ `lines=${JSON.stringify(lines)}`);
75
111
  fs.rmSync(root, { recursive: true, force: true });
76
112
  }
77
113
 
@@ -84,65 +120,75 @@ const run = (argv, opts = {}) => spawnSync(process.execPath, [VERIFY, ...argv],
84
120
  fs.rmSync(root, { recursive: true, force: true });
85
121
  }
86
122
 
87
- // ---- 场景 4:绿路径凭证落账(2026-10-07 verify-evidence)——全绿后 fixture 台账出合法绿行 ----
123
+ // ---- 场景 4:绿路径凭证落账——全绿后 fixture 台账出合法绿行(计数捕获 + testCmd 注入输出口径) ----
88
124
  {
89
125
  const root = mkfix();
90
126
  const r = run(['--test-cmd', `node ${mkCmd(0, '[run-tests] 合计: SUITES 3 / FAILED 0\n')}`], { env: { ...process.env, CHECK_LOOP_ROOT: root } });
91
- const vp = path.join(root, '.agents', 'verifications.jsonl');
92
- const line = fs.existsSync(vp) ? JSON.parse(fs.readFileSync(vp, 'utf8').trim()) : null;
93
- check('场景 4:全绿 → verifications.jsonl 落绿行(exitCode 0 + 计数捕获)',
127
+ const line = readLedger(root)[0] || null;
128
+ check('场景 4:全绿 → verifications.jsonl 落绿行(exitCode 0 + 计数捕获 + 门数)',
94
129
  r.status === 0 && line !== null && line.exitCode === 0 && line.passed === 3 && line.failed === 0
95
- && typeof line.ts === 'string' && r.stdout.includes('凭证已落账'),
130
+ && line.gates === 2 && typeof line.ts === 'string' && line.suite === 'npm test' && r.stdout.includes('凭证已落账'),
96
131
  `exit=${r.status} line=${JSON.stringify(line)}`);
97
132
  fs.rmSync(root, { recursive: true, force: true });
98
133
  }
99
134
 
100
- // ---- 场景 5:红路径不落账(check-loop 红 → fail-fast 在落账前退出)----
135
+ // ---- 场景 4b:fixture 全绿不得触发摘要生成(假门凭证只进本地台账,不产 committed 摘要) ----
136
+ {
137
+ const root = mkfix();
138
+ const r = run(['--test-cmd', `node ${mkCmd(0, '[run-tests] 合计: SUITES 3 / FAILED 0\n')}`], { env: { ...process.env, CHECK_LOOP_ROOT: root } });
139
+ const ev = path.join(root, 'workflow', 'verify-evidence.md');
140
+ check('场景 4b:fixture 绿 → 不生成 workflow/verify-evidence.md、输出无摘要生成标记',
141
+ r.status === 0 && !fs.existsSync(ev) && !r.stdout.includes('已生成'),
142
+ `exit=${r.status} exists=${fs.existsSync(ev)}\n${r.stdout}`);
143
+ fs.rmSync(root, { recursive: true, force: true });
144
+ }
145
+
146
+ // ---- 场景 5:check-loop 红 → 失败行留痕、无绿行(失败也留痕;绿行语义 = verify 全绿不变) ----
101
147
  {
102
148
  const root = mkBroken();
103
149
  const r = run(['--test-cmd', `node ${mkCmd(0)}`], { env: { ...process.env, CHECK_LOOP_ROOT: root } });
104
- check('场景 5:npm test 绿但 check-loop 红 → 无凭证行(凭证语义 = verify 全绿)',
105
- r.status === 1 && !fs.existsSync(path.join(root, '.agents', 'verifications.jsonl')),
106
- `exit=${r.status}`);
150
+ const lines = readLedger(root);
151
+ check('场景 5:测试门绿但 check-loop 红 → 仅失败行(suite=check-loop),无绿行',
152
+ r.status === 1 && lines.length === 1 && lines[0].exitCode !== 0 && lines[0].suite === 'check-loop'
153
+ && !lines.some((l) => l.exitCode === 0),
154
+ `exit=${r.status} lines=${JSON.stringify(lines)}`);
107
155
  fs.rmSync(root, { recursive: true, force: true });
108
156
  }
109
157
 
110
- // ---- 场景 6:输出无汇总行 → 计数键省略(exitCode 才是硬事实)----
158
+ // ---- 场景 6:输出无汇总行 → 计数键省略(exitCode 才是硬事实) ----
111
159
  {
112
160
  const root = mkfix();
113
161
  const r = run(['--test-cmd', `node ${mkCmd(0, 'ok\n')}`], { env: { ...process.env, CHECK_LOOP_ROOT: root } });
114
- const vp = path.join(root, '.agents', 'verifications.jsonl');
115
- const line = fs.existsSync(vp) ? JSON.parse(fs.readFileSync(vp, 'utf8').trim()) : null;
162
+ const line = readLedger(root)[0] || null;
116
163
  check('场景 6:无汇总输出 → 绿行落盘但计数键省略',
117
164
  r.status === 0 && line !== null && line.exitCode === 0 && !('passed' in line) && !('failed' in line),
118
165
  `line=${JSON.stringify(line)}`);
119
166
  fs.rmSync(root, { recursive: true, force: true });
120
167
  }
121
168
 
122
- // ---- 场景 7:装户仓(cwd 无 package.json)且未给 --test-cmd → 跳过步骤 1、不落凭证、仍跑步骤 2 → exit 0 ----
169
+ // ---- 场景 7:装户仓(cwd 无 package.json)且未给 --test-cmd → 跳过测试门、不落凭证、其余门全过 → exit 0 ----
123
170
  {
124
- const root = mkfix();
125
- const bare = fs.mkdtempSync(path.join(os.tmpdir(), 'verify-bare-'));
126
- const r = run([], { cwd: bare, env: { ...process.env, CHECK_LOOP_ROOT: root } });
127
- check('场景 7:exit 0', r.status === 0, `exit=${r.status}\n${r.stdout}\n${r.stderr}`);
128
- check('场景 7:步骤 1 显式跳过(⏭ 1/2)、步骤 2 通过且全绿', r.stdout.includes('⏭ 1/2') && r.stdout.includes('✅ 2/2') && r.stdout.includes('全绿'), r.stdout);
129
- check('场景 7:跳过时不落测试绿凭证(防假绿)',
130
- !fs.existsSync(path.join(root, '.agents', 'verifications.jsonl')) && r.stdout.includes('不落测试绿凭证'),
131
- r.stdout);
132
- fs.rmSync(root, { recursive: true, force: true });
133
- fs.rmSync(bare, { recursive: true, force: true });
171
+ const root = provision(mkfix());
172
+ gitify(root);
173
+ const bare = fs.mkdtempSync(path.join(os.tmpdir(), 'verify-bare-'));
174
+ const r = run([], { cwd: bare, env: { ...process.env, CHECK_LOOP_ROOT: root } });
175
+ check('场景 7:exit 0', r.status === 0, `exit=${r.status}\n${r.stdout}\n${r.stderr}`);
176
+ check('场景 7:测试门显式跳过(⏭ 2/7)、末门通过且全绿', r.stdout.includes('⏭ 2/7') && r.stdout.includes('✅ 7/7') && r.stdout.includes('全绿'), r.stdout);
177
+ check('场景 7:跳过时不落测试绿凭证(防假绿)且不生成摘要',
178
+ readLedger(root).length === 0 && !fs.existsSync(path.join(root, 'workflow', 'verify-evidence.md')) && r.stdout.includes('不落测试绿凭证'),
179
+ r.stdout);
180
+ fs.rmSync(root, { recursive: true, force: true });
181
+ fs.rmSync(bare, { recursive: true, force: true });
134
182
  }
135
183
 
136
- // ---- 场景 8:--doc 凭证绑定(2026-10-08 verify-doc-binding)----
184
+ // ---- 场景 8:--doc 凭证绑定 ----
137
185
  // ① 带 --doc → 末行含 doc 且为规范化 posix 相对路径;② 不带 → 末行**无 doc 键**(不是 null);
138
186
  // ③ win32 反斜杠 / posix / 绝对路径三种写法落同一值(归一是本单的地基,错一处=凭证永远匹配不上);
139
- // ④ 缺值 → exit 1 用法错误;⑤ 跳过步骤 1 时即便带 --doc 仍不落账(防假绿语义不得被 --doc 破坏)。
187
+ // ④ 缺值 → exit 1 用法错误;⑤ 跳过测试门时即便带 --doc 仍不落账(防假绿语义不得被 --doc 破坏)。
140
188
  {
141
189
  const readLast = (root) => {
142
- const p = path.join(root, '.agents', 'verifications.jsonl');
143
- if (!fs.existsSync(p)) return null;
144
- const lines = fs.readFileSync(p, 'utf8').trim().split('\n').filter(Boolean);
145
- return lines.length ? JSON.parse(lines[lines.length - 1]) : null;
190
+ const lines = readLedger(root);
191
+ return lines.length ? lines[lines.length - 1] : null;
146
192
  };
147
193
 
148
194
  const rootA = mkfix();
@@ -175,27 +221,27 @@ const run = (argv, opts = {}) => spawnSync(process.execPath, [VERIFY, ...argv],
175
221
  check('场景 8④:--doc 缺值 → exit 1 + 用法提示',
176
222
  rD.status === 1 && /--doc 缺值/.test(rD.stderr), `exit=${rD.status} ${rD.stderr}`);
177
223
 
178
- const rootE = mkfix();
224
+ const rootE = provision(mkfix());
225
+ gitify(rootE);
179
226
  const bare = fs.mkdtempSync(path.join(os.tmpdir(), 'verify-bare-doc-'));
180
227
  const rE = run(['--doc', rel], { cwd: bare, env: { ...process.env, CHECK_LOOP_ROOT: rootE } });
181
- check('场景 8⑤:步骤 1 跳过时即便带 --doc 仍不落账(防假绿语义不被 --doc 破坏)',
182
- rE.status === 0 && !fs.existsSync(path.join(rootE, '.agents', 'verifications.jsonl')) && rE.stdout.includes('不落测试绿凭证'),
228
+ check('场景 8⑤:测试门跳过时即便带 --doc 仍不落账(防假绿语义不被 --doc 破坏)',
229
+ rE.status === 0 && readLedger(rootE).length === 0 && rE.stdout.includes('不落测试绿凭证'),
183
230
  `exit=${rE.status} ${rE.stdout}`);
184
231
 
185
232
  for (const r of [rootA, rootB, rootC, rootD, rootE]) fs.rmSync(r, { recursive: true, force: true });
186
233
  fs.rmSync(bare, { recursive: true, force: true });
187
234
  }
188
235
 
189
- // ---- 场景 9 / 10:嵌套套件同形汇总行不得被误取(2026-10-09 审查 P0-2 回归锁)----
190
- // 缺陷复现:旧判据 pop 通配「合计: PASS n / FAIL n」,而全仓 45+ 套件每个都打印自己那行同形文本,
191
- // 取到的是字母序最后一个套件的场景数(本仓实况 workflows-check.test.mjs = 15),落进凭证 passed。
236
+ // ---- 场景 9 / 10:嵌套套件同形汇总行不得被误取(计数口径=编排器权威套件数,回归锁) ----
237
+ // 缺陷复现:旧判据 pop 通配「合计: PASS n / FAIL n」,而全套件每个都打印自己那行同形文本,
238
+ // 取到的是字母序最后一个套件的场景数,落进凭证 passed。
192
239
  // ⑨ 权威行与同形行并存 → 取权威套件数;⑩ 仅有同形行 → 省略计数键(宁缺勿错,不回退到错的事实)。
193
240
  {
194
241
  const root9 = mkfix();
195
242
  const r9 = run(['--test-cmd', `node ${mkCmd(0, '合计: PASS 15 / FAIL 0\n[run-tests] 合计: SUITES 45 / FAILED 0\n')}`],
196
243
  { 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;
244
+ const l9 = readLedger(root9)[0] || null;
199
245
  check('场景 9:嵌套套件同形行不被误取,计数取编排器权威套件数',
200
246
  r9.status === 0 && l9 !== null && l9.passed === 45 && l9.failed === 0,
201
247
  `line=${JSON.stringify(l9)}`);
@@ -204,14 +250,77 @@ const run = (argv, opts = {}) => spawnSync(process.execPath, [VERIFY, ...argv],
204
250
  const root10 = mkfix();
205
251
  const r10 = run(['--test-cmd', `node ${mkCmd(0, '合计: PASS 15 / FAIL 0\n')}`],
206
252
  { 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;
253
+ const l10 = readLedger(root10)[0] || null;
209
254
  check('场景 10:仅有嵌套同形行 → 绿行落盘但省略计数键(不回退到错的事实)',
210
255
  r10.status === 0 && l10 !== null && l10.exitCode === 0 && !('passed' in l10) && !('failed' in l10),
211
256
  `line=${JSON.stringify(l10)}`);
212
257
  fs.rmSync(root10, { recursive: true, force: true });
213
258
  }
214
259
 
260
+ // ---- 场景 11:--list 锁定 engine cohort 七门序,不执行门 ----
261
+ {
262
+ const expect = ['scripts-syntax', 'engine-tests', 'workflows-check', 'rule-budget', 'effective-policy', 'check-loop', 'ledger-invariant'];
263
+ const r = run(['--list'], {});
264
+ const ids = (r.stdout || '').trim().split(/\r?\n/).filter(Boolean);
265
+ check('场景 11:--list exit 0 且恰七门、序不变、不执行',
266
+ r.status === 0 && ids.length === expect.length && expect.every((id, i) => ids[i] === id)
267
+ && !String(r.stdout).includes('开始静态门'),
268
+ `exit=${r.status}\n${r.stdout}`);
269
+ }
270
+
271
+ // ---- 场景 12:--list 与 --test-cmd 互斥 ----
272
+ {
273
+ const r = run(['--list', '--test-cmd', 'node nothing.mjs'], {});
274
+ check('场景 12:--list + --test-cmd → exit 1', r.status === 1 && /互斥/.test(r.stderr), `exit=${r.status} ${r.stderr}`);
275
+ }
276
+
277
+ // ---- 场景 13:生产模式中段红 → fail-fast + 未执行门清单 + 失败行留痕 ----
278
+ // 编排脚本检查门注入一份缺 frontmatter 的坏编排(E1 error)→ 第 3 门红,后四门状态未知须点名
279
+ {
280
+ const root = provision(mkfix());
281
+ fs.mkdirSync(path.join(root, '.agents', 'workflows'), { recursive: true });
282
+ fs.writeFileSync(path.join(root, '.agents', 'workflows', 'bad.md'), '没有 frontmatter 的坏编排\n', 'utf8');
283
+ const bare = fs.mkdtempSync(path.join(os.tmpdir(), 'verify-bare-ff-'));
284
+ const r = run([], { cwd: bare, env: { ...process.env, CHECK_LOOP_ROOT: root } });
285
+ check('场景 13:exit 1 且不出全绿', r.status === 1 && !r.stdout.includes('全绿'), `exit=${r.status}\n${r.stdout}\n${r.stderr}`);
286
+ check('场景 13:fail-fast 点名未执行门(rule-budget、effective-policy、check-loop、ledger-invariant)',
287
+ r.stderr.includes('未执行门') && ['rule-budget', 'effective-policy', 'check-loop', 'ledger-invariant'].every((id) => r.stderr.includes(id)),
288
+ r.stderr);
289
+ const lines = readLedger(root);
290
+ check('场景 13:失败行留痕(suite=workflows-check)且无绿行',
291
+ lines.length === 1 && lines[0].suite === 'workflows-check' && lines[0].exitCode !== 0,
292
+ `lines=${JSON.stringify(lines)}`);
293
+ fs.rmSync(root, { recursive: true, force: true });
294
+ fs.rmSync(bare, { recursive: true, force: true });
295
+ }
296
+
297
+ // ---- 场景 14:生产模式全绿端到端——七门按序全过、凭证钉门数/headShort/套件计数、摘要生成 ----
298
+ {
299
+ const root = provision(mkfix());
300
+ // 根 package.json(装户带根 package.json 的形态):测试命令打印编排器权威汇总行
301
+ fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify({
302
+ name: 'verify-e2e-fixture',
303
+ private: true,
304
+ scripts: { test: 'node .agents/probe-test.mjs' },
305
+ }, null, 2));
306
+ fs.writeFileSync(path.join(root, '.agents', 'probe-test.mjs'), "console.log('[run-tests] 合计: SUITES 3 / FAILED 0');\n", 'utf8');
307
+ const headShort = gitify(root);
308
+ const r = run([], { cwd: root, env: { ...process.env, CHECK_LOOP_ROOT: root } });
309
+ check('场景 14:exit 0 且七门逐门输出 + 全绿',
310
+ r.status === 0 && ['✅ 1/7', '✅ 2/7', '✅ 3/7', '✅ 4/7', '✅ 5/7', '✅ 6/7', '✅ 7/7'].every((s) => r.stdout.includes(s)) && r.stdout.includes('全绿'),
311
+ `exit=${r.status}\n${r.stdout}\n${r.stderr}`);
312
+ const line = readLedger(root)[0] || null;
313
+ check('场景 14:绿凭证钉门数 7 + headShort + 套件计数',
314
+ line !== null && line.exitCode === 0 && line.gates === 7 && line.headShort === headShort && line.passed === 3 && line.failed === 0,
315
+ `headShort=${line && line.headShort} expect=${headShort} line=${JSON.stringify(line)}`);
316
+ const ev = path.join(root, 'workflow', 'verify-evidence.md');
317
+ const evText = fs.existsSync(ev) ? fs.readFileSync(ev, 'utf8') : '';
318
+ check('场景 14:绿凭证摘要已生成且含本 SHA 一行(自动重生成接线)',
319
+ fs.existsSync(ev) && evText.includes(`\`${headShort}\``) && evText.includes('| 7 | PASS 3 / FAIL 0 |'),
320
+ evText);
321
+ fs.rmSync(root, { recursive: true, force: true });
322
+ }
323
+
215
324
  console.log(`\n合计: PASS ${pass} / FAIL ${fail}`);
216
325
  if (fail) {
217
326
  console.log('\n用法:node templates/_agents/scripts/verify.test.mjs');
@@ -15,6 +15,8 @@
15
15
  import fs from 'node:fs';
16
16
  import path from 'node:path';
17
17
  import { parseStages } from './workflows-check.mjs';
18
+ // 委派台账分片感知读(2026-10-13 ledger-shard-ledgers 回灌):单源 = ledger-shards.readDelegationsText。
19
+ import { readDelegationsText as readDelegationsShardText } from './ledger-shards.mjs';
18
20
 
19
21
  function fail(msg) { console.error('❌ ' + msg); process.exit(1); }
20
22
 
@@ -162,8 +164,9 @@ if (isMain) {
162
164
  fail(`未知命令:${a.cmd ?? ''}(add | status,用法见文件头注释)`);
163
165
  }
164
166
  function readDelegationsText(file) {
165
- const p = file || path.join(process.cwd(), 'workflow', 'delegations.md');
166
- try { return fs.readFileSync(p, 'utf8'); } catch { return ''; }
167
+ // 分片感知读(2026-10-13 ledger-shard-ledgers 回灌):单源 = ledger-shards.readDelegationsText。
168
+ //(file 为显式注入路径时只读该文件——测试语义不变;null/默认路径 → 主台账 ∪ delegations.d/*.md)
169
+ return readDelegationsShardText(process.cwd(), file || null);
167
170
  }
168
171
 
169
172
  function delegationSectionRows(text) {
@@ -172,8 +175,9 @@ function delegationSectionRows(text) {
172
175
  const rows = [];
173
176
  for (const line of lines) {
174
177
  if (/^##\s+/.test(line)) {
175
- if (inSec) break;
176
- if (/^##\s+委派结果\s*$/.test(line)) inSec = true;
178
+ // 不 break:合并文本(主台账 ∪ 分片)里 `## 委派结果` 会出现多次,break 会漏掉后续分片的行
179
+ // (2026-10-13 源仓实测踩中:分片行读不到 → 静默漏报「委派台账未记录」)。
180
+ inSec = /^##\s+委派结果\s*$/.test(line);
177
181
  continue;
178
182
  }
179
183
  if (!inSec || !/^\s*\|/.test(line)) continue;
@@ -139,7 +139,7 @@ const STAGES = parseStages(`| id | after | role | step | task | files | accept |
139
139
  const j = path.join(os.tmpdir(), `wfj-real-${Date.now()}.jsonl`);
140
140
  const r = spawnSync(process.execPath, [JOURNAL_CLI, 'status', '--wf', 'pipeline-closing', '--journal', j], { encoding: 'utf8', cwd: ROOT });
141
141
  check('S9 真实 pipeline-closing:confirm 为 human 形态且无 journal 时 author 首层就绪',
142
- !!confirm && confirm.human && !confirm.role && stages.length >= 10
142
+ !!confirm && confirm.human && !confirm.role && stages.length >= 9
143
143
  && r.status === 0 && /就绪(第 0 层.*author/.test(r.stdout),
144
144
  JSON.stringify({ confirm, out: r.stdout }));
145
145
  fs.rmSync(j, { force: true });
@@ -6,22 +6,21 @@ import fs from 'node:fs';
6
6
  import path from 'node:path';
7
7
  import os from 'node:os';
8
8
  import { spawnSync } from 'node:child_process';
9
- import { fileURLToPath } from 'node:url';
10
9
 
11
- const SRC = path.dirname(fileURLToPath(import.meta.url));
12
10
  let pass = 0, fail = 0;
13
11
  const check = (name, cond, detail = '') => {
14
12
  if (cond) { pass++; console.log(`PASS ${name}`); }
15
13
  else { fail++; console.log(`FAIL ${name}${detail ? `——${detail}` : ''}`); }
16
14
  };
17
15
 
18
- const COPIED = ['wiki-search.mjs', 'kb-search.mjs', 'workflow-enums.mjs'];
16
+ // 夹具闭包(2026-10-12 fixture-closure-rootfix):入口只写 wiki-search.mjs,kb-search 及其传递依赖
17
+ // (workflow-enums.mjs 等)由闭包自动带入——此前手写枚举,漏拷即 import 失败 → 假绿。
18
+ const { copyScriptClosure, copyAgentsFiles } = await import('./fixture-scripts.mjs');
19
19
  function mkFixture() {
20
20
  const fx = fs.mkdtempSync(path.join(os.tmpdir(), 'ws-'));
21
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));
22
+ copyScriptClosure(path.join(fx, '.agents', 'scripts'), ['wiki-search.mjs']);
23
+ copyAgentsFiles(path.join(fx, '.agents'), ['workflow-enums.txt', 'workflow-modules.txt']);
25
24
  fs.mkdirSync(path.join(fx, 'wiki', '测试主题'), { recursive: true });
26
25
  fs.writeFileSync(path.join(fx, 'wiki', '测试主题', '说明.md'), '# 说明\n\n编码模板的领域知识条目。\n');
27
26
  fs.mkdirSync(path.join(fx, 'workflow', 'intents'), { recursive: true });
@@ -64,7 +64,7 @@ const CHECK_LOOP = path.join(SCRIPT_DIR, 'check-loop.mjs');
64
64
  'doc.status.abandoned=superseded cancelled',
65
65
  'incident.status.all=open fixed closed',
66
66
  'incident.status.active=open',
67
- 'level.all=L0 L1 L2 L3',
67
+ 'level.all=L1 L2 L3',
68
68
  ].join('\n') + '\n');
69
69
  const w = (rel, c) => fs.writeFileSync(path.join(T, rel), c);
70
70
  // 回路断档:incident 三件套选「是」但引用 intent 不存在
@@ -169,7 +169,7 @@ const CHECK_LOOP = path.join(SCRIPT_DIR, 'check-loop.mjs');
169
169
  requirement: '在 runtime-env.md 补记 pipeline-run 运行态',
170
170
  stage: 'done',
171
171
  stopType: 'done',
172
- triage: { kind: 'require', level: 'L0', module: 'pipeline', topic: 'runtime-note', decidedBy: 'ai' },
172
+ triage: { kind: 'require', level: 'L1', module: 'pipeline', topic: 'runtime-note', decidedBy: 'ai' },
173
173
  updatedAt: '2026-10-02T10:19:22.566Z',
174
174
  events: [
175
175
  { t: '2026-10-02T10:19:02.680Z', type: 'run-created', requirement: 'x' },
@@ -1,6 +1,6 @@
1
1
  // workflow-enums.test.mjs — 枚举单源读取器测试(2026-09-25 enum-single-source)
2
2
  // 判据:① 真实单源文件解析 + 不变量通过 ② 坏数据(坏行/重复键/重复值/缺键/子集破/重叠)fail-loud
3
- // ③ 缺文件 fail-loud ④ sh 消费方同口径(sed 可取到全部 8 键)
3
+ // ③ 缺文件 fail-loud ④ sh 消费方同口径(sed 可取到全部键)
4
4
  // 用法:node templates/_agents/scripts/workflow-enums.test.mjs(npm test 随跑)
5
5
  import path from 'node:path';
6
6
  import { execSync } from 'node:child_process';
@@ -20,14 +20,22 @@ const throwsWith = (fn, kw) => {
20
20
  const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
21
21
  const ENUMS_TXT = path.join(SCRIPT_DIR, '..', 'workflow-enums.txt');
22
22
 
23
- // ---- S1 模块级 ENUMS 即加载即校验(真实文件),8 键齐 ----
23
+ // ---- S1 模块级 ENUMS 即加载即校验(真实文件),10 键齐(2026-10-13 自 Shipyard 回灌归因两键)----
24
24
  {
25
25
  const keys = Object.keys(ENUMS).sort();
26
- check('S1 真实单源文件加载成功且 8 键齐',
27
- keys.length === 8 && keys.join(',') === 'doc.status.abandoned,doc.status.active,doc.status.all,doc.status.confirmed,doc.status.terminal,incident.status.active,incident.status.all,level.all',
26
+ check('S1 真实单源文件加载成功且 10 键齐',
27
+ keys.length === 10 && keys.join(',') === 'doc.status.abandoned,doc.status.active,doc.status.all,doc.status.confirmed,doc.status.terminal,incident.env.all,incident.source.all,incident.status.active,incident.status.all,level.all',
28
28
  JSON.stringify(keys));
29
29
  }
30
30
 
31
+ // ---- S1b 归因两键值域逐字对齐 Shipyard(incident.source.all / incident.env.all)----
32
+ {
33
+ check('S1b 归因两键值域:source=self user test monitor、env=dev staging prod none',
34
+ ENUMS['incident.source.all'].join('+') === 'self+user+test+monitor'
35
+ && ENUMS['incident.env.all'].join('+') === 'dev+staging+prod+none',
36
+ JSON.stringify(ENUMS));
37
+ }
38
+
31
39
  // ---- S2 真实文件关键值口径(check-loop/看板/检索的行为锚点,值变更须连本测试一起改)----
32
40
  {
33
41
  check('S2 关键集值口径:confirmed 不含 draft、active=draft+approved、terminal 含 done、abandoned ⊆ terminal',
@@ -35,7 +43,8 @@ const ENUMS_TXT = path.join(SCRIPT_DIR, '..', 'workflow-enums.txt');
35
43
  && ENUMS['doc.status.active'].join('+') === 'draft+approved'
36
44
  && ENUMS['doc.status.terminal'].includes('done')
37
45
  && ENUMS['doc.status.abandoned'].every((v) => ENUMS['doc.status.terminal'].includes(v))
38
- && ENUMS['level.all'].length === 4,
46
+ // level.all=L1 L2 L3——L0 已退役(2026-10-13 自源仓收敛系列 1 回灌)
47
+ && ENUMS['level.all'].join('+') === 'L1+L2+L3',
39
48
  JSON.stringify(ENUMS));
40
49
  }
41
50
 
@@ -27,7 +27,7 @@
27
27
  // 到首个非 `|` 行截止;仅此一张表——编排脚本按机制文档只允许一张 stages 表。
28
28
  // 用法:node .agents/scripts/workflows-check.mjs [--root <目标根>] [--json]
29
29
  // 默认根 = process.cwd()(doctor 以 cwd=target 调用;仓库根直跑即自扫)
30
- // 零依赖;只报告不修复(B-b 决策口径,同 source-sync-check)
30
+ // 零依赖;只报告不修复(B-b 决策口径)
31
31
  // 测试:node templates/_agents/scripts/workflows-check.test.mjs(fixture + 真实仓库 baseline)
32
32
  import fs from 'node:fs';
33
33
  import path from 'node:path';
@@ -4,6 +4,7 @@
4
4
  # 新增引擎脚本默认必须带同名 .test.mjs(README 规范条目);确属库件/工具才登记本文件。
5
5
  # 2026-10-09 A2 评估:verify-wiki-consistency 已移出(补测 3 场景);wiki-search 已移出(转发壳,判定单源 kb-search.test 覆盖)。
6
6
  check-metric-claims.mjs | 经 check-loop.mjs 消费 | 由 check-loop.test.mjs 间接覆盖其调用路径
7
+ rule-budget.mjs | 判定器(2026-10-13 自 .sh 移植 node;经 check-hygiene 检查 13 与 pre-commit 消费,Shipyard 侧同无直测) | check-loop.test.mjs 场景 28-30(含 --staged 硬拦两态)+ check-loop-rev.test.mjs 间接覆盖;src/rule-budget.test.mjs 平行实现作口径对照
7
8
  ensure-board.mjs | 看板启动器 | 永久工具豁免(需真实端口/进程环境,自动化覆盖无性价比——2026-10-09 engine-quality-round2 A2 评估定性)
8
9
  policy.mjs | 库件 | 被 check-loop.test.mjs 直接 import 覆盖
9
10
  # 2026-10-12 needs-clarification:stage-gates.mjs 已建同名直测(stage-gates.test.mjs 15 断言),
@@ -0,0 +1,60 @@
1
+ ---
2
+ name: papercut-fix
3
+ description: 修 workflow/papercuts.md 里指定的未升级 papercut 条目(把存量债降到 0 的回路执行器)。按控制器给的行号定位症状、照「拟修」列改权威文件、回写台账并跑验证门。
4
+ ---
5
+
6
+ # papercut-fix — 单条 papercut 升级执行器
7
+
8
+ ## 目标
9
+ 将 `workflow/papercuts.md` 中指定的一条 papercut 条目状态更新为「已消债」。
10
+
11
+ ## 触发条件
12
+ - 用户指令:`跑一轮 papercut 回路` / `清 papercut 存量` / `修第 N 条 papercut`。
13
+
14
+ ## 约束与红线
15
+ - **单一目标**:每轮执行仅处理一条目标,禁止顺手修复相邻条目或夹带其他改动。
16
+ - **范围限制**:改动仅限文档面(`.agents/commands/`, `.agents/roles/`, `.agents/skills/`, `.githooks/`, `.agents/scripts/`, `workflow/`)。
17
+ - **绝对禁区**:禁止修改 `backend/`, `frontend/`, `tests/`, `wiki/`,禁止连接数据库——与控制器 scope gate(`OUT_OF_SCOPE` 正则)同一清单,命中即停。超出范围时必须停止并报告用户。
18
+ - **验证优先**:禁止凭台账描述臆测现状。必须先读权威文件验证症状是否成立。
19
+ - **编辑规范**:修改 `workflow/` 文档必须使用 Edit 工具。禁止使用 `node -e` 或脚本批量替换(CRLF 环境下会静默失败)。
20
+ - **消债判定**:分支 A(修复)与分支 B(暂缓/失效)**均**满足 `debt -1` 不变量。
21
+
22
+ ## 工作流
23
+
24
+ ### 1. 定位与预检
25
+ - 从控制器输出提取:`行号` (Lxx), `位置` (路径), `症状`, `拟修`。
26
+ - 使用 Read 工具读取该行及其上下文。
27
+
28
+ ### 2. 现状验证
29
+ - 读取「位置」列指定的权威文件。
30
+ - **判定结论**:
31
+ - **缺陷仍在** $\rightarrow$ 进入分支 A。
32
+ - **已失效** (已被其他改动修复/文件已删除) $\rightarrow$ 进入分支 B。
33
+ - **方向相反/描述有误** $\rightarrow$ 重新定性后再执行。
34
+
35
+ ### 3. 执行修复
36
+ - **分支 A (缺陷仍在)**:
37
+ 1. 对权威文件执行最小化修复。
38
+ 2. 在 `workflow/papercuts.md`(或承载该条目的 `workflow/papercuts.d/` 分片)该行末尾追加:`✅ 已升级修复(YYYY-MM-DD): <改动描述> <commit>`。
39
+ 3. 若原「拟修」描述已不适用,在追加记录中写明实际修法。
40
+ - **分支 B (已失效)**:
41
+ 1. 在 `workflow/papercuts.md`(或承载该条目的分片)该行末尾追加:`⏸ 暂缓(YYYY-MM-DD): <失效理由>`。理由必须明确。
42
+ 2. **核实纪律**:
43
+ - 检查用户级技能目录(`~/.claude/skills/`、`~/.trae/skills/` 等宿主适配目录)确认文件是否仅是迁移而非删除。
44
+ - 区分「环境约束」与「代码缺陷」。
45
+ - 针对「跨环境」症状,使用 `git worktree` 构建真实检出态实验证伪。
46
+ - 针对「已落地」症状,使用 `git log -S` 追溯引入提交日期。
47
+ - **分支 C (越界)**:停止执行,报告理由并请求授权。
48
+
49
+ ### 4. 验证与收尾
50
+ - **备份**:修改台账(主台账或分片)前强制 `cp <台账文件> tmp/papercuts.wip-backup.md`。
51
+ - **运行检查**:
52
+ - `node .agents/scripts/papercut-debt-sensor.mjs` $\rightarrow$ `debt` 必须恰好减少 1。
53
+ - `node .agents/scripts/papercut-debt-controller.mjs` $\rightarrow$ 确认目标推进。
54
+ - 检查台账(主台账 ∪ 分片)的总行数(`total` 不得减少——`debt` 符合预期 ≠ 文件没被压行)、列数分布及是否存在超长行(压行信号)。
55
+ - **体积检查**:若修改了 `.agents/commands/`,跑 `node .agents/scripts/rule-budget.mjs --staged` 对比 `.agents/rule-budgets.txt` 上限(一进一出;禁用工作区 `wc -c` 核对——CRLF 膨胀会误报)。
56
+
57
+ ## 回报要求
58
+ - 参照 `references/response-template.md` 输出。
59
+ - 包含:行号、位置、分支结论(A/B/C)、改动文件、验证结果(debt 前后值)、待决策事项。
60
+ - **禁令**:禁止自动 commit 或 push。
@@ -0,0 +1,81 @@
1
+ # papercut-fix 回应模板
2
+
3
+ 单轮回路执行器的最终输出格式。给用户看,决定他是否放行这次改动。
4
+
5
+ ---
6
+
7
+ ## 模板
8
+
9
+ ```markdown
10
+ papercut 回路 · 第 N 轮
11
+
12
+ **目标**:L{line} `{location}`({date},第 {hits} 次命中)
13
+ **结论**:{A 缺陷仍在 / B 已失效 / B2 账未销(拟修已由别的改动落地)/ B3 台账方向记反(检出态实验证伪)/ C 越界待授权}
14
+
15
+ ### 改动
16
+
17
+ | 文件 | 改了什么 |
18
+ |---|---|
19
+ | `{权威文件路径}` | {一句话} |
20
+ | `workflow/papercuts.md` | 回写行标记:{`✅ 已升级修复(日期)` / `⏸ 暂缓(日期):理由`} |
21
+
22
+ ### 验证
23
+
24
+ - `papercut-debt-sensor.mjs`:debt {before} → {after}(预期 -1)
25
+ - {其他检查,如体积预算核对:通过 / 不适用}
26
+
27
+ ### 台账依据
28
+
29
+ {若实际修法与「拟修」列描述不一致,在此说明「拟修」列写的什么、实际怎么做的、为什么。
30
+ B2 结论必须写出落地提交:`由 <sha>(<日期>,<标题>)落地`。}
31
+
32
+ ### 需你决定
33
+
34
+ {列出需要用户拍板的事项;没有则写「无」。典型场景:
35
+ - 结论为 C(越界)——是否单独授权改源码/连库
36
+ - 「拟修」列描述已过期——是否更新该列文本
37
+ - 控制器列出的「待人工定性」条目——是否本轮顺带定性
38
+ - 改动触及常驻面体积预算——是否接受,或改为下沉/删除被取代条目}
39
+ ```
40
+
41
+ ---
42
+
43
+ ## 格式要求
44
+
45
+ 1. **先给结论,再给细节**。用户扫第一行就要知道"修了哪条、结论是什么"。
46
+ 2. **「需你决定」不许空着**。即使本轮无事,也显式写「无」——让"没有待决事项"和"忘了写"可区分。
47
+ 3. **验证值给具体数字**,不要只说"验证通过"。debt 的前后值是设定点误差,必须可核。
48
+ 4. **不粉饰失败**。验证门红了就写红,附命令与原始报错摘要。回路的价值在于给出可信的账,不是好看的账。
49
+ 5. **不写"建议接下来…"式的空话**。要写就写具体动作 + 具体条目编号。
50
+
51
+ ---
52
+
53
+ ## 示例(结论 A,示意——占位符形态,非真实条目)
54
+
55
+ ```markdown
56
+ papercut 回路 · 第 3 轮
57
+
58
+ **目标**:L34 `.agents/commands/test.md`(静态门清单 §2 缺一条豁免说明)(<日期>)
59
+ **结论**:A 缺陷仍在
60
+
61
+ ### 改动
62
+
63
+ | 文件 | 改了什么 |
64
+ |---|---|
65
+ | `.agents/commands/test.md` | §2 补一条:跳过条件的判据以 <权威判据> 为准,不以 <表象> 为准;§常见误判 补对应条目 |
66
+ | `workflow/papercuts.md` | 回写 L34 标记:`✅ 已升级修复(YYYY-MM-DD)` |
67
+
68
+ ### 验证
69
+
70
+ - `papercut-debt-sensor.mjs`:debt 16 → 15(预期 -1)
71
+ - 体积预算:`node .agents/scripts/rule-budget.mjs --staged` 通过(未触 `.agents/commands/` 上限)
72
+
73
+ ### 台账依据
74
+
75
+ 「拟修」列写的是「补一条 <表象> 的判定说明」。实际执行时发现症状描述的是 <相邻机制>,而 §2 真正缺的是**判据本身**(<相邻机制> 只在特定前提后需要,判据该按 <权威判据>)。两者相邻但不同,已按判据写,相邻条目并入同节。
76
+
77
+ ### 需你决定
78
+
79
+ - 「拟修」列文本与实际修法有偏差(见上),是否更新该列描述以免下次复发时对不上?
80
+ - 控制器列出的「待人工定性」3 条(位置列路径已不存在)——其中 1 条指向的脚本性能问题已随某次重构解决,本轮修完后建议一并定性。要不要现在处理?
81
+ ```
@@ -16,4 +16,11 @@ doc.status.terminal=done superseded cancelled
16
16
  doc.status.abandoned=superseded cancelled
17
17
  incident.status.all=open fixed closed
18
18
  incident.status.active=open
19
- level.all=L0 L1 L2 L3
19
+ # 归因枚举(2026-10-13 自 Shipyard 回灌 incident-attribution):归因检查(B6b 落地后编号以 check-loop.mjs 头部清单为准)校验 incident frontmatter 的「来源:」/「影响环境:」。
20
+ # source = 谁发现(self=AI 自查 / user=用户报出 / test=测试或实测暴露 / monitor=监控告警)
21
+ # env = 缺陷触达的环境(none=未触达任何运行环境,如仅静态检查发现)
22
+ # 锚前存量不回填;新 open 件必填(生效锚随 B6b 归因检查落地)。
23
+ incident.source.all=self user test monitor
24
+ incident.env.all=dev staging prod none
25
+ # 级别三档(2026-10-13 L0 退役自源仓收敛系列 1 回灌):L1 = 一份 plan 即入口(协作道);L2/L3 = 防御道。
26
+ level.all=L1 L2 L3