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
package/src/profiles.mjs CHANGED
@@ -113,9 +113,16 @@ export function isOwned(rel) {
113
113
  return rel === 'AGENTS.md' || rel === '.gitattributes' || rel.startsWith('workflow/') || rel.startsWith('wiki/')
114
114
  || rel.startsWith('.agents/notes/')
115
115
  || rel === '.agents/workflow-modules.txt' || rel === '.agents/rule-budgets.txt'
116
- || rel === '.agents/metric-claims.txt' || rel === '.agents/metric-derivers.cjs'
117
- || rel === '.agents/metric-derivers.mjs'
118
- || rel === '.agents/hooks/local-pre-commit'
116
+ // level-hints.txt(2026-10-13 flow-kit-route):风险级别**路径下限**清单,内容项目私有(照抄别人的
117
+ // 清单等于没登记),故归装户自持、sync 永不覆盖——与 lane-surfaces.txt 同款性质。
118
+ || rel === '.agents/level-hints.txt'
119
+ || rel === '.agents/metric-claims.txt' || rel === '.agents/metric-derivers.cjs'
120
+ || rel === '.agents/metric-derivers.mjs'
121
+ // mechanism-registry.md(2026-10-13 judgment-ledger 回灌 B4):机制使用量登记表——装户按月对账
122
+ // 自持运营(增删机制行 / 刷新计数 / 退役处置),若落 managed 则每次对账编辑都报「本地已改」漂移,
123
+ // 与 metric-claims.txt 同款性质,归 owned(随包附带示例行起步内容,此后归项目)。
124
+ || rel === '.agents/mechanism-registry.md'
125
+ || rel === '.agents/hooks/local-pre-commit'
119
126
  || rel.startsWith('.github/');
120
127
  }
121
128
 
package/src/render.mjs CHANGED
@@ -43,8 +43,8 @@ export function renderTree(srcRoot, targetRoot, vars, { force = false, protectSe
43
43
  continue;
44
44
  }
45
45
  const rel = relOf(abs, srcRoot);
46
- // 引擎基线不是模板:包源径基线的键命名空间与装户盘面不一致(见 check-engine-integrity.mjs
47
- // resolveLock 注释)——下发会致装户全员误报。包源跑 --update 落盘时排除,杜绝入库即污染
46
+ // 引擎基线不是模板:键在 templates/_agents/…,下发成 .agents/engine-lock.json 会让装户
47
+ // 拿包源路径对账。排除该落点,包源基线留在模板目录。
48
48
  if (rel === '.agents/engine-lock.json') continue;
49
49
  const target = path.join(targetRoot, rel);
50
50
  fs.mkdirSync(path.dirname(target), { recursive: true });
@@ -61,7 +61,7 @@ export function renderTree(srcRoot, targetRoot, vars, { force = false, protectSe
61
61
  if (raw.length < 512 * 1024 && !raw.includes(0)) {
62
62
  // 文本面 LF 归一(2026-09-27 closing-coverage):模板盘面 CRLF(autocrlf 检出 / 编辑器写入)与
63
63
  // LF 克隆渲染出同一字节、同一 sha——fresh sha、装户落盘、kit.json 台账三方跨环境稳定;
64
- // sync/doctor/source-sync-check 的读取侧归一(shaText)与本处写盘归一同口径闭合
64
+ // sync/doctor 的读取侧归一(shaText)与本处写盘归一同口径闭合
65
65
  const replaced = renderContent(raw.toString('utf8').replace(/\r\n/g, '\n'), vars);
66
66
  if (replaced.includes('{{')) {
67
67
  for (const m of replaced.matchAll(/\{\{([A-Z][A-Z0-9_]*)\}\}/g)) {
@@ -4,7 +4,7 @@
4
4
  // 两处都不跑——bb69e19/418960a 重构净增 78B 把 .agents/commands/ 推到 65581B > 65536B,包源侧零暴露,
5
5
  // 装户首提被硬拦才发现(2026-10-11 S1 冒烟)。本套件把同一张预算表映射到包源路径核对,npm test / CI
6
6
  // 直接拦超限——模板增长不再静默入包。run-tests 盘面 glob 自动挂载本套件。
7
- // 口径(对照 templates/_agents/scripts/rule-budget.sh 单源,勿两处漂移):
7
+ // 口径(对照 templates/_agents/scripts/rule-budget.mjs 单源,勿两处漂移):
8
8
  // 字节 = LF 归一(工作树读入去 \r\n——等价 fsize 的 git cat-file 口径,core.autocrlf 检出形态无关);
9
9
  // 目录合计 = 扁平 glob(直接子文件,不入子目录);pat=首空格前 / lim=末空格后;lim 非纯数字行跳过;
10
10
  // # 注释与空行跳过。
@@ -36,7 +36,7 @@ if (!fs.existsSync(BUDGETS)) {
36
36
  }
37
37
  const rows = [];
38
38
  for (const line of fs.readFileSync(BUDGETS, 'utf8').split('\n')) {
39
- const t = line.replace(/\r/g, ''); // 表文件检出即 CRLF——显式去 \r(rule-budget.sh 同款)
39
+ const t = line.replace(/\r/g, ''); // 表文件检出即 CRLF——显式去 \r(rule-budget.mjs 读取同款容 \r)
40
40
  if (!t || t.startsWith('#')) continue;
41
41
  const i = t.indexOf(' ');
42
42
  if (i < 0) continue;
@@ -8,6 +8,7 @@ import os from 'node:os';
8
8
  import path from 'node:path';
9
9
  import { fileURLToPath } from 'node:url';
10
10
  import { HELP } from './cli.mjs';
11
+ import { routeKindList } from './executor.mjs';
11
12
  import { commitCheckConfig, settingsJson } from './profiles.mjs';
12
13
 
13
14
  const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
@@ -30,6 +31,11 @@ check('help 含四套已发布门禁',
30
31
  check('help 不再把当前门禁写成只有 dotnet-ca',
31
32
  !HELP.includes('当前:dotnet-ca;') && !HELP.includes('当前:dotnet-ca)'));
32
33
 
34
+ // route 的 kind 值域与 GATE_LIST 同纪律:从权威面(new-task.md 的 route-kinds 机器单源)派生,
35
+ // 不手写第二份——否则装户改 kind 时 help 与 route 判定面双源漂移。
36
+ const kindList = routeKindList(ROOT).join('|');
37
+ check('help route kind 列表等于 new-task.md route-kinds 单源', HELP.includes(`--kind ${kindList}`), kindList);
38
+
33
39
  const nodeCfg = JSON.parse(commitCheckConfig('node'));
34
40
  check('node 构建命令带 pkg:scripts.build',
35
41
  nodeCfg.builds.some((b) => b.command === 'npm run build' && Array.isArray(b.when) && b.when.includes('pkg:scripts.build')));
@@ -47,10 +47,8 @@ function listMd(dir) {
47
47
  // pairsFor:权威源文件 → 薄适配 rel 列表(映射单源见 profiles.mjs#HOSTS.commandPrefix)
48
48
  // commands 权威源 → 带 commandPrefix 的宿主各一份(<dir>/commands/<prefix><name>.md)
49
49
  // roles 权威源 → 全部宿主 agents 各一份
50
- // hostDirOf:宿主 → 适配目录名。包源布局 = 宿主键(modules/hosts/opencode/…);装户布局 = HOSTS[h].dir
51
- // (项目根下 .opencode/…,带点)——2026-10-07 sync-hosts-target-fix 加装户支持时引入。
52
- // hostFilter(2026-10-09 sync-hosts-root-coverage):宿主级过滤(多根模式下「项目根宿主点」根只扫
53
- // 实际存在 dir 的宿主——不存在=没装该宿主点,不算缺失不算漂移)。
50
+ // hostDirOf:宿主 → 适配目录名。包源调用传入 (h) => h,配对落在 modules/hosts/<宿主键>/。
51
+ // hostFilter:宿主级过滤。调用方只扫实际存在的宿主目录时传入。
54
52
  function pairsFor(authRel, hostDirOf = (h) => h, hostFilter = null) {
55
53
  const out = [];
56
54
  const norm = authRel.split(path.sep).join('/');
@@ -74,12 +72,9 @@ function pairsFor(authRel, hostDirOf = (h) => h, hostFilter = null) {
74
72
 
75
73
  // diffHosts:权威源正文段 vs 薄适配正文段的漂移分析(B-b 语义;pure function)。
76
74
  // authorityRoot:权威源根(含 commands/ + roles/)
77
- // 多根对账(2026-10-09 sync-hosts-root-coverage):adapterRoots = [{root, hostDirOf, hostFilter?, label?}]——
78
- // 包源布局传两根(modules/hosts 宿主键形态 + 项目根 HOSTS.dir 形态宿主点);装户布局单根。
79
- // 向后兼容:旧单根入参 adaptersRoot/hostDirOf 归一为单元素数组。
80
- // 根因教训(engine-quality-round2 B1):包源布局此前只扫 modules/hosts,项目根宿主点(本仓 .zcode/)
81
- // 完全不在扫描面——bb69e19「81 对全对齐」声明漏掉 .zcode 三份(独立复核 P2-3 实证)。
82
- // hostDirOf:宿主 → 适配目录名(默认宿主键 = 包源布局;装户布局传 (h) => HOSTS[h].dir)
75
+ // adapterRoots = [{root, hostDirOf, hostFilter?, label?}]。只扫 modules/hosts。
76
+ // 向后兼容:旧单根入参 adaptersRoot/hostDirOf 归一为单元素数组。
77
+ // hostDirOf:宿主 → 适配目录名(默认宿主键)
83
78
  // 返回:{ drift, inSync, authorityMissing, adapterOrphans, faces }
84
79
  // drift 条目自带 root(applyForward 按它定位适配文件);faces = 对账面自描述行(防不可解释的「N 对」)
85
80
  export function diffHosts(opts) {
@@ -196,55 +191,35 @@ function applyForward({ authorityRoot }, drift) {
196
191
  export function syncHosts(args, pkgRoot) {
197
192
  let apply = false;
198
193
  let json = false;
194
+ let strict = false;
199
195
  let dir = null;
200
196
  for (let i = 0; i < args.length; i++) {
201
197
  const a = args[i];
202
198
  if (a === '--diff') apply = false;
203
199
  else if (a === '--apply') apply = true;
204
200
  else if (a === '--json') json = true;
201
+ else if (a === '--strict') strict = true;
205
202
  else if (a === '--dir') { dir = args[++i]; if (!dir) fail('--dir 缺少参数(目标项目根)'); }
206
203
  else fail(`未知选项:${a}(flow-kit sync-hosts --help)`);
207
204
  }
208
205
  if (!pkgRoot) fail('sync-hosts 缺少 pkgRoot(开发模式入口)');
209
206
 
210
- // 目标根:--dir > cwd > pkgRoot(兜底)。
211
- // 2026-10-07 sync-hosts-target-fix:此前恒用 pkgRoot——装户里跑 `flow-kit sync-hosts` 时 pkgRoot
212
- // 解析为**包安装目录**,于是 doctor §7.x 报漂移、照提示跑 `--apply` 会去改包源仓(装户自己的适配层纹丝不动)。
207
+ // 目标根:--dir > cwd > pkgRoot。只认包源仓(templates/_agents + modules/hosts)。
208
+ // 装户仓的宿主文件由 init / add-host 写入,不在这里把 .agents 再当成第二份权威源。
213
209
  const target = path.resolve(dir || process.cwd() || pkgRoot);
214
- // 布局判定(与 doctor.checkAdapterDrift 同口径):
215
- // 包源(templates/_agents 与 modules/hosts 同时存在)→ 权威源 = <target>/templates/_agents
216
- // 薄适配根(2026-10-09 sync-hosts-root-coverage 多根)= ① modules/hosts(宿主键形态,全宿主)
217
- // + ② 项目根 HOSTS.dir 实际存在的宿主点(如本仓 .zcode/——此前不在扫描面,bb69e19「81 对全对齐」
218
- // 漏掉其三份,独立复核 P2-3 实证);不存在 = 没装该宿主点,不算缺失。
219
- // 装户(其余)→ 权威源 = <target>/.agents,薄适配根 = <target>(宿主目录 .opencode/.trae/… 直接位于项目根)
220
- const isSourceRepo = fs.existsSync(path.join(target, 'templates', '_agents'))
221
- && fs.existsSync(path.join(target, 'modules', 'hosts'));
222
- const authorityRoot = isSourceRepo ? path.join(target, 'templates', '_agents') : path.join(target, '.agents');
223
- const adapterRoots = isSourceRepo
224
- ? [
225
- { root: path.join(target, 'modules', 'hosts'), hostDirOf: (h) => h, label: 'modules/hosts' },
226
- {
227
- root: target,
228
- hostDirOf: (h) => (HOSTS[h] && HOSTS[h].dir) || h,
229
- hostFilter: (h) => HOSTS[h] && fs.existsSync(path.join(target, HOSTS[h].dir)),
230
- label: '项目根宿主点',
231
- },
232
- ]
233
- : [{
234
- root: target,
235
- hostDirOf: (h) => (HOSTS[h] && HOSTS[h].dir) || h,
236
- hostFilter: (h) => HOSTS[h] && fs.existsSync(path.join(target, HOSTS[h].dir)),
237
- label: '项目根宿主点',
238
- }];
239
- if (!fs.existsSync(authorityRoot)) {
240
- fail(`未找到权威源目录:${authorityRoot}——目标根 ${target} 既非包源(无 templates/_agents)也非装户(无 .agents);用 --dir <项目根> 指定`);
210
+ const authorityRoot = path.join(target, 'templates', '_agents');
211
+ const hostsRoot = path.join(target, 'modules', 'hosts');
212
+ if (!fs.existsSync(authorityRoot) || !fs.existsSync(hostsRoot)) {
213
+ fail(`sync-hosts 只在包源仓运行(需要 templates/_agents 与 modules/hosts)。目标根 ${target} 不是包源仓。装户的宿主文件由 init / add-host 写入。`);
241
214
  }
242
- const layout = isSourceRepo ? '包源' : '装户';
215
+ const adapterRoots = [
216
+ { root: hostsRoot, hostDirOf: (h) => h, label: 'modules/hosts' },
217
+ ];
243
218
  const result = diffHosts({ authorityRoot, adapterRoots });
244
219
 
245
220
  if (apply && result.drift.length > 0) {
246
221
  const { synced } = applyForward({ authorityRoot }, result.drift);
247
- console.log(`▶ flow-kit sync-hosts --apply → ${target}(${layout}布局)`);
222
+ console.log(`▶ flow-kit sync-hosts --apply → ${target}(包源)`);
248
223
  console.log(` 对账面:${result.faces.join(' + ')}`);
249
224
  console.log(` 按权威源正文覆盖薄适配正文段 ${synced.length} 份(薄适配 frontmatter 不动):`);
250
225
  for (const rel of synced) console.log(` - ${rel}`);
@@ -256,8 +231,27 @@ export function syncHosts(args, pkgRoot) {
256
231
  if (json) {
257
232
  process.stdout.write(JSON.stringify(result, null, 2) + '\n');
258
233
  } else {
259
- console.log(`▶ flow-kit sync-hosts --diff → ${target}(${layout}布局)`);
234
+ console.log(`▶ flow-kit sync-hosts --diff → ${target}(包源)`);
260
235
  printDiff(result);
261
236
  }
237
+ // --strict(B4,2026-10-13):漂移**报告器**变机器门。
238
+ // 背景:source-sync-check 删除后,跨宿主正文漂移只剩人工跑一次 --diff,CI 里无任何调用点——
239
+ // 而改权威源要同步 N 份薄适配,人工 grep 易漏(本次评审前 10 份薄适配就带着漂移)。
240
+ // 默认仍返回 0(保持「报告器」语义,人工跑不因漂移而失败);CI 用 --strict 把漂移变成红。
241
+ // 口径:**只判「跑一次 --apply 就能归零」的两项**(正文漂移 + 权威源缺失)。
242
+ // 孤儿薄适配不判红——--apply 按设计不删它(可能是宿主特化/历史残留),
243
+ // 把它算红就是一条机器无法自愈的假红,按本仓「假阳性即噪声」纪律不能设成门(仍照常报告)。
244
+ if (strict) {
245
+ const fixable = result.drift.length + result.authorityMissing.length;
246
+ if (fixable > 0) {
247
+ console.error(`[跨宿主漂移 strict] 正文漂移 ${result.drift.length} · 权威源缺失 ${result.authorityMissing.length}`
248
+ + `${result.adapterOrphans.length ? `(另有孤儿薄适配 ${result.adapterOrphans.length} 份,按设计不判红)` : ''}`
249
+ + '——须先 flow-kit sync-hosts --apply 再提交');
250
+ return 1;
251
+ }
252
+ if (result.adapterOrphans.length) {
253
+ console.log(` ℹ️ 孤儿薄适配 ${result.adapterOrphans.length} 份(--apply 按设计不删,故 strict 不判红;确为残留请手工删)`);
254
+ }
255
+ }
262
256
  return 0;
263
257
  }
@@ -4,7 +4,7 @@
4
4
  // 判据:①全对齐 → inSync=28, drift 空 ②权威源改 → drift 列 ③apply → 正文一致,frontmatter 各自保留
5
5
  // ④薄适配 frontmatter 手改 → 不进 drift ⑤薄适配正文手改 → drift 列,apply 覆盖
6
6
  // ⑥薄适配缺失 → authorityMissing 列;apply 不自动创建
7
- // ⑦--json ⑧孤儿薄适配 ⑨装户布局(--dir / cwd 指向装户根 → 按 .agents ↔ 宿主目录比对)
7
+ // ⑦--json ⑧孤儿薄适配 ⑨非包源根拒绝(不把 .agents 当权威源)
8
8
  // 用法:node src/sync-hosts.test.mjs
9
9
  import fs from 'node:fs';
10
10
  import path from 'node:path';
@@ -78,7 +78,18 @@ syncHosts(process.argv.slice(2), root);
78
78
  return spawnSync(process.execPath, [driver, '--dir', fixtureRoot, ...extra], { encoding: 'utf8', ...opts });
79
79
  }
80
80
 
81
- // 不传 --dir 形态:把 cwd 设为 fixture(验「默认当前目录」解析;装户布局靠 cwd 判定)
81
+ // runSyncHostsExit:与 CLI 同口径**传播退出码**(cli.mjs 会 process.exit(syncHosts(...)))。
82
+ // 上面的 runSyncHosts driver 丢弃返回值,故退出码恒 0——不能用来断言 --strict 判红(B4)。
83
+ function runSyncHostsExit(fixtureRoot, extra = []) {
84
+ const driver = path.join(fixtureRoot, 'driver-exit.mjs');
85
+ W(driver, `import { syncHosts } from ${JSON.stringify(pathToFileURL(path.join(SRC_ROOT, 'sync-hosts.mjs')).href)};
86
+ const code = syncHosts(process.argv.slice(2), ${JSON.stringify(fixtureRoot)});
87
+ process.exit(typeof code === 'number' ? code : 0);
88
+ `);
89
+ return spawnSync(process.execPath, [driver, '--dir', fixtureRoot, ...extra], { encoding: 'utf8' });
90
+ }
91
+
92
+ // 不传 --dir 形态:把 cwd 设为 fixture(验「默认当前目录」解析)
82
93
  function runSyncHostsByCwd(fixtureRoot, extra = []) {
83
94
  const driver = path.join(fixtureRoot, 'driver.mjs');
84
95
  W(driver, `import { syncHosts } from ${JSON.stringify(pathToFileURL(path.join(SRC_ROOT, 'sync-hosts.mjs')).href)};
@@ -192,39 +203,23 @@ syncHosts(process.argv.slice(2), root);
192
203
  check('S8 apply 不删孤儿薄适配', fs.existsSync(path.join(fx, 'modules/hosts/opencode/commands/orphan.md')) && fs.existsSync(path.join(fx, 'modules/hosts/opencode/commands/wf-legacy.md')));
193
204
  }
194
205
 
195
- // ============ 场景 9:装户布局(.agents 为权威源 + 宿主目录为薄适配,无 templates/modules)============
196
- // 2026-10-07 sync-hosts-target-fix:此前目标根恒为 pkgRoot(= 包安装目录),装户跑 sync-hosts
197
- // 会去改包源仓。本场景钉住「--dir / cwd 指向装户根 → 按装户布局比对」。
206
+ // ============ 场景 9:非包源根拒绝(.agents 不再当权威源,宿主文件保持原样)============
198
207
  {
199
208
  const root = fs.mkdtempSync(path.join(os.tmpdir(), 'fk-synchosts-inst-'));
200
209
  W(path.join(root, '.agents/commands/test.md'),
201
210
  `---\ndescription: Test 阶段\n---\n\n# Test · v2\n\n权威源正文 test v2\n`);
202
- W(path.join(root, '.opencode/commands/wf-test.md'),
203
- `---\ndescription: Test · opencode\n---\n\n# Test · v1\n\n旧正文 test v1\n`);
204
- W(path.join(root, '.trae/commands/wf-test.md'),
205
- `---\nname: wf-test\ndescription: Test · trae\n---\n\n# Test · v1\n\n旧正文 test v1\n`);
211
+ const hostBody = `---\ndescription: Test · opencode\n---\n\n# Test · v1\n\n旧正文 test v1\n`;
212
+ W(path.join(root, '.opencode/commands/wf-test.md'), hostBody);
206
213
 
207
- // 9a:--dir 指向装户根 → 布局判定「装户」,列出 2 处漂移
208
214
  const r1 = runSyncHosts(root, ['--diff']);
209
215
  const out1 = r1.stdout + r1.stderr;
210
- check('S9 装户布局:--dir 指向装户根 → 头部标「装户布局」', /装户布局/.test(out1), out1);
211
- check('S9 装户布局:列出 2 处正文漂移', /正文段漂移(2/.test(out1), out1);
212
- // 未装宿主(claude/cursor/codex)目录不存在 → 不算「缺失」(否则装户刷屏 + apply 恒 exit 1)
213
- check('S9 装户布局:未装宿主不进「权威源声明但薄适配缺失」', !/权威源声明但薄适配缺失/.test(out1), out1);
216
+ check('S9 非包源根 --diff:拒绝且点明只在包源仓运行', r1.status !== 0 && /只在包源仓运行/.test(out1), out1);
217
+ check('S9 非包源根 --diff:不改宿主文件', R(path.join(root, '.opencode/commands/wf-test.md')) === hostBody, out1);
214
218
 
215
- // 9b:不传 --dir(cwd = 装户根)→ 同样命中装户布局(旧行为会误指包源)
216
- const r2 = runSyncHostsByCwd(root, ['--diff']);
219
+ const r2 = runSyncHostsByCwd(root, ['--apply']);
217
220
  const out2 = r2.stdout + r2.stderr;
218
- check('S9 cwd 默认:装户布局解析正确(不再误指包源)', /装户布局/.test(out2) && !/包源布局/.test(out2), out2);
219
-
220
- // 9c:apply → 正文段对齐、薄适配 frontmatter 保留
221
- const r3 = runSyncHosts(root, ['--apply']);
222
- const out3 = r3.stdout + r3.stderr;
223
- const bodyOf = (p) => { const t = R(p); const m = t.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n/); return t.slice(m ? m[0].length : 0); };
224
- check('S9 装户布局 apply:opencode 正文段对齐', bodyOf(path.join(root, '.opencode/commands/wf-test.md')) === bodyOf(path.join(root, '.agents/commands/test.md')), out3);
225
- check('S9 装户布局 apply:trae frontmatter 保留(name: wf-test)', /^---\nname: wf-test/m.test(R(path.join(root, '.trae/commands/wf-test.md'))), out3);
226
- // 未装宿主不计缺失 → apply 摘要「权威源缺失:0」且 exit 0
227
- check('S9 装户布局 apply:未装宿主不计缺失 → 权威源缺失 0 且 exit 0', /权威源缺失:0/.test(out3) && r3.status === 0, `status=${r3.status}\n${out3}`);
221
+ check('S9 cwd 为装户根 --apply:同样拒绝', r2.status !== 0 && /只在包源仓运行/.test(out2), out2);
222
+ check('S9 cwd 为装户根 --apply:宿主文件未被改写', R(path.join(root, '.opencode/commands/wf-test.md')) === hostBody, out2);
228
223
  }
229
224
 
230
225
  {
@@ -237,43 +232,59 @@ syncHosts(process.argv.slice(2), root);
237
232
  check('实仓薄适配无缺失无正文漂移', r.authorityMissing.length === 0 && r.drift.length === 0, `missing=${r.authorityMissing.length} drift=${r.drift.length} ${sample}`);
238
233
  }
239
234
 
240
- // ============ 场景 10-12:包源布局多根对账(2026-10-09 sync-hosts-root-coverage,B1)============
241
- // 根因教训(bb69e19「81 对全对齐」漏 .zcode 三份,独立复核 P2-3):包源布局此前只扫 modules/hosts,
242
- // 项目根 HOSTS.dir 形态宿主点(本仓 .zcode/)不在扫描面。三场景钉住:漂移可检出 / apply 可修复且
243
- // frontmatter 保留 / 对账面自描述行在案。
235
+ // ============ 场景 10:包源仓不扫描项目根宿主点(.zcode 等不是第二对账面)============
244
236
  {
245
- // S10:包源 fixture + 项目根 .zcode/agents 漂移(正文旧 + frontmatter zcode 风格)→ --diff 报漂移
246
237
  const fx = mkFixture();
247
- W(path.join(fx, '.zcode/agents/implementer.md'),
248
- `---\nname: implementer\ndescription: zcode 本仓风格\n---\n\n# Implementer · v0\n\n旧版正文 implementer v0\n`);
238
+ const zcodeBody = `---\nname: implementer\ndescription: zcode 本仓风格\n---\n\n# Implementer · v0\n\n旧版正文 implementer v0\n`;
239
+ W(path.join(fx, '.zcode/agents/implementer.md'), zcodeBody);
249
240
  const r = runSyncHosts(fx, ['--diff']);
250
241
  const out = r.stdout + r.stderr;
251
- check('S10 项目根宿主点漂移:--diff 报 .zcode/agents/implementer.md',
252
- out.includes('.zcode/agents/implementer.md') && /正文段漂移/.test(out), out.slice(0, 600));
253
- check('S10 对账面自描述:含「项目根宿主点(1 宿主)」', /对账面:.*项目根宿主点(1 宿主)/.test(out), out.split('\n').slice(0, 4).join('\n'));
254
-
255
- // S11:--apply 修复项目根漂移——正文覆盖为权威源、frontmatter 保留、漂移归零
256
- const r2 = runSyncHosts(fx, ['--apply']);
257
- const out2 = r2.stdout + r2.stderr;
258
- const synced = /- \.zcode\/agents\/implementer\.md/.test(out2);
259
- const fixed = R(path.join(fx, '.zcode/agents/implementer.md'));
260
- check('S11 --apply 同步清单含 .zcode 条目', synced, out2.slice(0, 500));
261
- check('S11 --apply 正文覆盖 + frontmatter 保留',
262
- fixed.includes('权威源正文 implementer v1') && fixed.includes('description: zcode 本仓风格'),
263
- fixed.slice(0, 200));
264
- check('S11 --apply 后漂移归零', /正文漂移:\s*0/.test(out2), out2.slice(-300));
242
+ check('S10 对账面只有 modules/hosts', /对账面:modules\/hosts/.test(out) && !/项目根宿主点/.test(out), out.split('\n').slice(0, 6).join('\n'));
243
+ check('S10 项目根 .zcode 漂移不入报告', !out.includes('.zcode/agents/implementer.md'), out.slice(0, 500));
244
+ runSyncHosts(fx, ['--apply']);
245
+ check('S10 --apply 不改写项目根 .zcode', R(path.join(fx, '.zcode/agents/implementer.md')) === zcodeBody);
265
246
  }
266
247
 
248
+ // ============ 场景 11:--strict 把漂移报告器变成机器门(B4)============
249
+ // 背景:source-sync-check 删除后,跨宿主正文漂移只剩人工跑一次 --diff,CI 里无任何调用点——
250
+ // 而改权威源要同步 N 份薄适配,人工 grep 易漏(本仓评审前就带着 10 份漂移)。
251
+ // 默认 --diff 仍恒 exit 0(人工跑不因漂移失败),--strict 才判红,供 CI 当门。
267
252
  {
268
- // S12:装户布局 faces 自描述(回归钉住——装户单根 label=项目根宿主点,行为不变)
269
- const fx = fs.mkdtempSync(path.join(os.tmpdir(), 'fk-synchosts-adopter-'));
270
- W(path.join(fx, '.agents/roles/implementer.md'),
271
- `---\ndescription: Implementer 角色契约\n---\n\n# Implementer · v1\n\n权威源正文 implementer v1\n`);
272
- W(path.join(fx, '.zcode/agents/implementer.md'),
273
- `---\nname: implementer\ndescription: zcode\n---\n\n# Implementer · v1\n\n权威源正文 implementer v1\n`);
274
- const r = runSyncHostsByCwd(fx, ['--diff']);
275
- const out = r.stdout + r.stderr;
276
- check('S12 装户布局:faces 单根自描述 + 对齐 1 对', /对账面:项目根宿主点(1 宿主)/.test(out) && /正文对齐:\s*1\s*对/.test(out), out.slice(0, 400));
253
+ const fx = mkFixture();
254
+ // 先 --apply 归零:夹具出厂状态未必干净(默认就可能带孤儿/缺失),
255
+ // 「--strict 在无漂移时 exit 0」这句话得先有个干净盘面才成立。
256
+ runSyncHosts(fx, ['--apply']);
257
+ const clean = runSyncHostsExit(fx, ['--diff', '--strict']);
258
+ check('S11 无漂移 + --strict → exit 0', clean.status === 0, `status=${clean.status} ${(clean.stdout + clean.stderr).slice(0, 300)}`);
259
+
260
+ // 制造一处薄适配正文漂移
261
+ const anyAdapter = fs.readdirSync(path.join(fx, 'modules/hosts'), { withFileTypes: true })
262
+ .filter((d) => d.isDirectory()).map((d) => d.name);
263
+ let broke = null;
264
+ for (const host of anyAdapter) {
265
+ const cmdDir = path.join(fx, 'modules/hosts', host, 'commands');
266
+ if (!fs.existsSync(cmdDir)) continue;
267
+ const f = fs.readdirSync(cmdDir).find((x) => x.endsWith('.md'));
268
+ if (!f) continue;
269
+ const abs = path.join(cmdDir, f);
270
+ fs.appendFileSync(abs, '\n手改一行,权威源没有这段。\n');
271
+ broke = `${host}/commands/${f}`;
272
+ break;
273
+ }
274
+ check('S11 夹具确实制造了漂移', !!broke, '未找到可改的薄适配');
275
+
276
+ const loose = runSyncHostsExit(fx, ['--diff']);
277
+ check('S11 有漂移但默认 --diff 仍 exit 0(报告器语义不变)', loose.status === 0, `status=${loose.status}`);
278
+ check('S11 默认 --diff 仍报告漂移', (loose.stdout + loose.stderr).includes('正文段漂移'), loose.stdout.slice(0, 300));
279
+
280
+ const strict = runSyncHostsExit(fx, ['--diff', '--strict']);
281
+ const sout = strict.stdout + strict.stderr;
282
+ check('S11 有漂移 + --strict → exit 1(CI 机器门)', strict.status === 1, `status=${strict.status}`);
283
+ check('S11 strict 报错点名修法(sync-hosts --apply)', sout.includes('--apply') && sout.includes('strict'), sout.slice(-300));
284
+
285
+ runSyncHosts(fx, ['--apply']);
286
+ const after = runSyncHostsExit(fx, ['--diff', '--strict']);
287
+ check('S11 --apply 归零后 --strict 回到 exit 0', after.status === 0, `status=${after.status}`);
277
288
  }
278
289
 
279
290
  console.log(`\n合计: PASS ${pass} / FAIL ${failCount}`);
package/src/validate.mjs CHANGED
@@ -7,6 +7,8 @@
7
7
  // 落在目标仓——tracked 过滤按目标仓 HEAD 算,与 git 钩子看到的提交面一致。
8
8
  // 只读范围:check-loop 自身仅有的两处落盘都在 .agents/cache/ 运行时缓存(gate-stats 需 --gate-stats
9
9
  // 才触发、added-dates 缓存 best-effort),且该目录已 gitignore——本命令不改任何文档与台账。
10
+ // 判定流水(2026-10-13 judgment-ledger 回灌 B4):check-loop 真实运行会追加 .agents/judgments.jsonl,
11
+ // validate 是探针而非门裁决点(init 装后自检同路径)——挂 CHECK_LOOP_NO_LEDGER 守卫保持只读契约。
10
12
  // 不暴露 --rev:runCheckLoop 中 rev 与 root 互斥(提交时点判定属钩子链职责,validate 只看盘面现状)。
11
13
  // 输出契约:check-loop 的 HARD-BLOCK/WARN 两段式原样走 stderr(稳定输出契约,消费者 .githooks/pre-push);
12
14
  // stdout 只承载本命令的汇总行 / --json——机器消费者按 stdout 解析,人按 stderr 读明细。
@@ -49,7 +51,10 @@ export async function validate(args, pkgRoot) {
49
51
  // 动态 import 包内置引擎:pkgRoot = npx 缓存 / 全局安装 / 仓内直跑三种形态下都指向包源模板
50
52
  const engineUrl = pathToFileURL(path.join(pkgRoot, 'templates', '_agents', 'scripts', 'check-loop.mjs')).href;
51
53
  const { runCheckLoop } = await import(engineUrl);
52
- const result = runCheckLoop({ root: target });
54
+ process.env.CHECK_LOOP_NO_LEDGER = '1'; // 探针不落判定流水(守卫开关见 judgment-ledger.mjs recordRun)
55
+ // mode:'all' 显式全量(2026-10-13 分道后裸跑=默认硬门)——validate 自述「全部 20 项检查」且 --strict
56
+ // 判红的是全量 warning 面,随分道保留原语义须显式声明,勿依赖缺省。
57
+ const result = runCheckLoop({ root: target, mode: 'all' });
53
58
  const code = validateExit(result, strict);
54
59
 
55
60
  if (json) {
@@ -35,7 +35,7 @@ const ENUMS_FIXTURE = [
35
35
  'doc.status.abandoned=superseded cancelled',
36
36
  'incident.status.all=open fixed closed',
37
37
  'incident.status.active=open',
38
- 'level.all=L0 L1 L2 L3',
38
+ 'level.all=L1 L2 L3',
39
39
  ].join('\n') + '\n';
40
40
 
41
41
  const mkfix = () => {
@@ -1,6 +1,17 @@
1
1
  # agentic-flow-kit:钩子与 shell 脚本必须 LF(CRLF 会破坏 Git Bash/MSYS 执行);
2
- # 预算表按 LF 计量(rule-budget.sh 已显式去 \r,此条为双保险)。已有自有 .gitattributes 的项目合并这几条即可。
2
+ # 预算表按 LF 计量(读取方 rule-budget.mjs 容 CRLF 行尾,此条为双保险)。已有自有 .gitattributes 的项目合并这几条即可。
3
3
  *.sh text eol=lf
4
4
  .githooks/* text eol=lf
5
5
  .agents/hooks/local-pre-commit text eol=lf
6
6
  *.ps1 text eol=crlf
7
+
8
+ # 台账并集合并(2026-10-12 ledger-merge-driver):确认台账是「尾部追加 + 多人/多 worktree 并行」形态,
9
+ # 默认三方合并会把它当文本冲突,人工解的常见错法是「整取一侧」——静默丢掉另一侧的确认行,
10
+ # 门禁事后无从发现。本 driver 把合并降级为确定性并集去重:两侧行全保留,无冲突可解。
11
+ # 需配 driver(flow-kit init 自动配):git config merge.jsonl-union.driver "node .agents/scripts/git-merge-jsonl.mjs %O %A %B"
12
+ # **故意不加 text eol=lf**:给既有 .jsonl 加 text 规则会让下次提交时 git 重新规范化换行 → 台账 blob 字节变化
13
+ # → check-ledger-invariant 的 append-only 前缀不变量判为「台账重写」而硬拦。台账换行归一由 driver 自身负责。
14
+ .agents/*.jsonl merge=jsonl-union
15
+ # 分片目录(2026-10-13 ledger-shard-default):上面的 glob 不跨目录层级,confirmations.d/ 下的分片
16
+ # 须单列——分片已是默认写入口径,漏配则并行分支合并分片时仍走文本冲突(整取一侧 = 静默丢确认行)。
17
+ .agents/confirmations.d/*.jsonl merge=jsonl-union
@@ -11,7 +11,7 @@
11
11
 
12
12
  1. **新需求 / 功能**:先立 `workflow/intents/` intent(复制 `_TEMPLATE.md`),对话内与用户确认后开工。
13
13
  2. **修复类任务**:先查 `workflow/incidents/` 同类历史;L1 以上立 incident(修复中即 intent 等价入口,L2/L3 与 spec/plan 同名配对)。
14
- 3. **级别判断与风险泳道**(就高不就低):L0 例行;L1 实现级;L2 规则/契约;L3 数据与运行时结构。L0/L1 协作道(轻确认+异步审计);L2/L3 防御道(同步确认门:spec/plan 确认后方可动手,L3 加独立复核)。见 `.agents/commands/new-task.md`。
14
+ 3. **级别判断与风险泳道**(就高不就低):L1 实现级(一份 plan 即入口);L2 规则/契约;L3 数据与运行时结构。L1 协作道(轻确认+异步审计);L2/L3 防御道(同步确认门:spec/plan 确认后方可动手,L3 加独立复核)。见 `.agents/commands/new-task.md`。
15
15
 
16
16
  ### 阶段路由
17
17
 
@@ -23,12 +23,12 @@
23
23
 
24
24
  ### 门禁与提交
25
25
 
26
- - `.githooks/`(经 `core.hooksPath` 挂载,本地 clone 后执行 `git config core.hooksPath .githooks`):pre-commit(闭环配对 / 泳道完整性 / wiki 台账 / 规则面预算 / 双源一致性 / managed 台账快检 / 敏感信息扫描与条件构建)、pre-push(闭环断档扫描)、commit-msg、post-commit、pre-merge-commit。
26
+ - `.githooks/`(经 `core.hooksPath` 挂载,本地 clone 后执行 `git config core.hooksPath .githooks`):pre-commit(闭环配对 / 泳道完整性 / wiki 台账 / 规则面预算 / 确认台账不变量 / 敏感信息扫描与条件构建)、pre-push(闭环断档扫描)、commit-msg、post-commit、pre-merge-commit。
27
27
  - **CI 远端门(2026-10-07 起)**:`.github/workflows/kit-ci.yml`(owned)在 GitHub runner 复跑 `node .agents/scripts/verify.mjs`(云端兜底本地钩子);非 GitHub 装户可删,等价门 = 同入口命令 + 服务端分支策略(人工一次性配置,机器不校验)。
28
28
  - **`git commit` / `git merge` / `git push` 三处一律禁 `--no-verify`**——被拦说明产出不合规,按提示修完原路重试。
29
29
  - 项目专属门禁挂 `.agents/hooks/local-pre-commit`。装户五条硬规则(配对、验收、确认留痕、敏感信息、双源台账)与 `audit` 档见 `workflow/README.md`「硬规则」。
30
30
  - 提交遵循 Conventional Commits 中文(feat / fix / docs / style / refactor / perf)。L1+ 三段式闭环链:`docs(*)` approved 留痕 → 代码 → 关单 `docs(*)`,相邻提交即同一闭环(2026-10-04 修订)。
31
- - **确认门(2026-09-27 起)**:intent/spec/plan 的 approved/done 与 incident 的 fixed/closed(两跳、无单跳)唯一入口 = `node .agents/scripts/confirm-doc.mjs <path>`,放弃态走显式 `--to superseded|cancelled`(合法前态见脚本提示;四终态同样内容绑定),两形态:① 用户终端亲手运行键入「可以」(TTY,AI 会话内被拒);② **对话委托代录**——用户在对话内明确确认后,AI 跑 `confirm-doc.mjs <path> --delegated "<授权摘要>"` 逐件代录(默认一次一份,多份并录被拒),台账如实记 `source: chat-delegated`,永不伪装 TTY 行(check-loop 15 指纹+台账对账拦截,与 source 无关);L0/L1 协作道可 `--batch` 多份一次代录(台账 `brief:true`,15 并录告警豁免;L2/L3 仍逐份)。
31
+ - **确认门(2026-09-27 起)**:intent/spec/plan 的 approved/done 与 incident 的 fixed/closed(两跳、无单跳)唯一入口 = `node .agents/scripts/confirm-doc.mjs <path>`,放弃态走显式 `--to superseded|cancelled`(合法前态见脚本提示;四终态同样内容绑定),两形态:① 用户终端亲手运行键入「可以」(TTY,AI 会话内被拒);② **对话委托代录**——用户在对话内明确确认后,AI 跑 `confirm-doc.mjs <path> --delegated "<授权摘要>"` 逐件代录(默认一次一份,多份并录被拒),台账如实记 `source: chat-delegated`,永不伪装 TTY 行(check-loop 15 指纹+台账对账拦截,与 source 无关);L1 协作道可 `--batch` 多份一次代录(台账 `brief:true`,15 并录告警豁免;L2/L3 仍逐份)。
32
32
  - **改动面 scope = 授权的机器事实(2026-10-09 起)**:confirm-doc 落账自动记 `scope{files,sha256}`(被确认文档 + `git status` 实测未提交改动集,机器派生、**AI 无输入面**)。分辨力由 scope + 指纹承担,`--delegated` 的摘要降级为人类可读留痕——**「每次换一句原话」纪律退役**,复用同一句摘要不再削弱任何判据。check-loop 15 校验受管行带 scope 且摘要自洽(`policyVersion ≥ 7` 起,旧版本与存量行零新增告警)。
33
33
 
34
34
  ### 检索、看板与量化
@@ -55,7 +55,7 @@ next: .agents/commands/test.md
55
55
  ### 1.3 实现
56
56
 
57
57
  7. 确认后按 plan 顺序推进;每个任务对照判据自验。若任务满足 `.agents/roles/implementer.md` 的边界,主智能体可将单个工作包连同必读材料、授权文件和验收条件委派给 `implementer`;否则由主智能体执行。
58
- 8. 自验:按改动域跑 `项目构建命令` / `项目类型检查命令` / `项目测试命令`(对应编译 / 前端类型检查 / 自动化测试;静态门全集见 `test.md` §1)。
58
+ 8. 自验:按改动域跑 `项目构建命令` / `项目类型检查命令` / `项目测试命令`(对应编译 / 前端类型检查 / 自动化测试;静态门全集见 `test.md` §1);范围对账 `node .agents/scripts/check-scope.mjs workflow/plans/YYYY-MM-DD-<主题>.md`——越界件(改了未声明)即收口(回填 plan 或回滚),plan 不可读 exit 2;默认 advisory exit 0,`--strict` 供门禁化调用。
59
59
  9. **项目架构红线**:数据访问模式、公共字段写入等以根与目录级 `AGENTS.md`(如有)红线为准,不自创旁路。
60
60
  10. **L3 且含 schema 变更时**:schema 变更走项目约定的维护方式(人工 SQL / 迁移工具,以目录级 `AGENTS.md` 或 spec 约束为准),不得擅自引入项目未采用的迁移机制(仅运行时 / 管线类 L3 无此约束)。
61
61
  11. 实现中若需偏离已确认的 plan(改公共接口 / 动 plan 未列文件 / 引新依赖 / 影响任务判据)→ 停下,向用户说明原因与影响,确认后再继续。
@@ -29,10 +29,11 @@ next: .agents/commands/build.md
29
29
  ## 1. 执行
30
30
 
31
31
  1. 确认同名入口文档已批准:新需求为 approved intent,修复为已确认的 incident 草稿;否则拒绝,分别回 `plan.md` / `maintain.md`(2026-09-30 起机器强制:fill-spec / confirm-doc 对前置未过直接拒绝 exit 2、不落盘不记账)。
32
- 2. 级别判断(对照 AGENTS.md 第 3 条):
33
- - **L1**(实现级改动,未命中 L2/L3)→ 走快车道,省略 spec,直接跳 `next: build.md` 起草 Quick-Plan
34
- - **L2**(改规则 / 契约:编码权威 / 共享契约 / 既有接口语义 / 全局横切口径,闭集见 `new-task.md` §级别判断)→ 必须有 spec
35
- - **L3**(数据与运行时结构:schema / 迁移 SQL / DI 链 / 认证与中间件管线)→ 必须有 spec + **新会话独立复核**
32
+ 2. 级别判断(对照 AGENTS.md 第 3 条;判据单源 = `.agents/scripts/level-criteria.mjs`,**本文件只承载流程映射,不复述判据闭集**):
33
+ - **L1**(未命中 L2/L3)→ 快车道:省略 spec,直接跳 `next: build.md` 起草 Quick-Plan
34
+ - **L2**(触达面闭集见单源 `L2_SURFACES`)→ 必须有 spec
35
+ - **L3**(结构面闭集见单源 `L3_SURFACES`)→ 必须有 spec + **新会话独立复核**
36
+ - 判哪一档:逐条对照单源的 `L2_SURFACES` / `L3_SURFACES` / `L1_NEGATIVES`,不要凭记忆——判据只读单源一处
36
37
  3. 起草 spec:
37
38
  - **填空工具先跑**:`node .agents/scripts/fill-spec.mjs --level <L1|L2|L3> --topic <主题> --output workflow/specs/<date>-<主题>.md`——输出含 frontmatter 5 字段 + 5 节正文(功能行为/数据流/系统改动/约束遵守映射/风险评估)的草稿;AI 据此填实
38
39
  - 复制 `workflow/specs/_TEMPLATE.md` → `workflow/specs/YYYY-MM-DD-<主题>.md`(与入口文档同名)
@@ -59,7 +59,7 @@ next: .agents/commands/plan.md(若复盘需要新 intent)
59
59
  **3. 规范条目**(必须有可追溯落点)
60
60
  - 落点优先级(取最前者可行即止):`workflow/regression-checklist.md` 回归条目 / `.agents/hooks/` 或 `tests/` 断言(机器门)→ `.agents/skills/`、`.agents/notes/` → `workflow/` 文档 → `.agents/commands/`
61
61
  - **根 `AGENTS.md` 不再是落点**;**入册门槛**:已由机器门强制的内容只留一行指针,禁止在常驻面复述;**一进一出**:落点为常驻面时须在本 commit 内删除或下沉一条被取代条目
62
- - 常驻面体积预算由 pre-commit 硬拦(`sh .agents/scripts/rule-budget.sh --staged`,表 `.agents/rule-budgets.txt`)——超限先删再增
62
+ - 常驻面体积预算由 pre-commit 硬拦(`node .agents/scripts/rule-budget.mjs --staged`,表 `.agents/rule-budgets.txt`)——超限先删再增
63
63
  - 引用:commit <SHA> / 文件:<路径>#L<行>;若无新增条目,理由:<已有条目覆盖,说明条目位置>
64
64
 
65
65
  ### 1.4 起草后停下