agentic-flow-kit 1.6.0 → 1.7.15

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 (115) hide show
  1. package/README.md +40 -6
  2. package/modules/gates/README.md +1 -0
  3. package/modules/gates/test-protect/README.md +23 -0
  4. package/modules/gates/test-protect/check-test-protect.mjs +64 -0
  5. package/modules/gates/test-protect/local-pre-commit.line +1 -0
  6. package/modules/hosts/README.md +57 -0
  7. package/modules/hosts/claude/commands/wf-build.md +5 -24
  8. package/modules/hosts/claude/commands/wf-deploy.md +1 -0
  9. package/modules/hosts/claude/commands/wf-design.md +2 -2
  10. package/modules/hosts/claude/commands/wf-gate-checklist.md +1 -3
  11. package/modules/hosts/claude/commands/wf-plan.md +1 -1
  12. package/modules/hosts/claude/commands/wf-source-sync-check.md +1 -3
  13. package/modules/hosts/claude/commands/wf-sync-hosts.md +1 -3
  14. package/modules/hosts/claude/commands/wf-test.md +5 -4
  15. package/modules/hosts/codex/commands/wf-build.md +5 -24
  16. package/modules/hosts/codex/commands/wf-deploy.md +1 -0
  17. package/modules/hosts/codex/commands/wf-design.md +2 -2
  18. package/modules/hosts/codex/commands/wf-gate-checklist.md +1 -3
  19. package/modules/hosts/codex/commands/wf-plan.md +1 -1
  20. package/modules/hosts/codex/commands/wf-source-sync-check.md +1 -3
  21. package/modules/hosts/codex/commands/wf-sync-hosts.md +1 -3
  22. package/modules/hosts/codex/commands/wf-test.md +5 -4
  23. package/modules/hosts/cursor/commands/wf-build.md +5 -24
  24. package/modules/hosts/cursor/commands/wf-deploy.md +1 -0
  25. package/modules/hosts/cursor/commands/wf-design.md +2 -2
  26. package/modules/hosts/cursor/commands/wf-gate-checklist.md +1 -3
  27. package/modules/hosts/cursor/commands/wf-plan.md +1 -1
  28. package/modules/hosts/cursor/commands/wf-source-sync-check.md +1 -3
  29. package/modules/hosts/cursor/commands/wf-sync-hosts.md +1 -3
  30. package/modules/hosts/cursor/commands/wf-test.md +5 -4
  31. package/modules/hosts/opencode/commands/wf-build.md +5 -24
  32. package/modules/hosts/opencode/commands/wf-deploy.md +1 -0
  33. package/modules/hosts/opencode/commands/wf-design.md +2 -2
  34. package/modules/hosts/opencode/commands/wf-gate-checklist.md +1 -3
  35. package/modules/hosts/opencode/commands/wf-plan.md +1 -1
  36. package/modules/hosts/opencode/commands/wf-source-sync-check.md +1 -3
  37. package/modules/hosts/opencode/commands/wf-sync-hosts.md +1 -3
  38. package/modules/hosts/opencode/commands/wf-test.md +5 -4
  39. package/modules/hosts/trae/commands/wf-build.md +5 -24
  40. package/modules/hosts/trae/commands/wf-deploy.md +1 -0
  41. package/modules/hosts/trae/commands/wf-design.md +2 -2
  42. package/modules/hosts/trae/commands/wf-gate-checklist.md +1 -3
  43. package/modules/hosts/trae/commands/wf-plan.md +1 -1
  44. package/modules/hosts/trae/commands/wf-source-sync-check.md +1 -3
  45. package/modules/hosts/trae/commands/wf-sync-hosts.md +1 -3
  46. package/modules/hosts/trae/commands/wf-test.md +5 -4
  47. package/package.json +2 -2
  48. package/src/add-gate.mjs +25 -1
  49. package/src/add-gate.test.mjs +103 -0
  50. package/src/cli.mjs +17 -2
  51. package/src/doctor.mjs +99 -11
  52. package/src/gates.test.mjs +17 -0
  53. package/src/init.mjs +7 -1
  54. package/src/init.test.mjs +19 -0
  55. package/src/instance-ref.test.mjs +62 -0
  56. package/src/jscpd-drift.test.mjs +144 -0
  57. package/src/render.mjs +5 -0
  58. package/src/rule-budget.test.mjs +103 -0
  59. package/src/run-tests.mjs +16 -9
  60. package/src/sync-hosts.mjs +4 -2
  61. package/src/trae-hooks.test.mjs +15 -3
  62. package/src/validate.mjs +72 -0
  63. package/src/validate.test.mjs +95 -0
  64. package/templates/AGENTS.md +3 -1
  65. package/templates/_agents/commands/build.md +5 -24
  66. package/templates/_agents/commands/deploy.md +1 -0
  67. package/templates/_agents/commands/design.md +2 -2
  68. package/templates/_agents/commands/gate-checklist.md +1 -3
  69. package/templates/_agents/commands/plan.md +1 -1
  70. package/templates/_agents/commands/source-sync-check.md +1 -3
  71. package/templates/_agents/commands/sync-hosts.md +1 -3
  72. package/templates/_agents/commands/test.md +5 -4
  73. package/templates/_agents/control-surfaces.txt +53 -0
  74. package/templates/_agents/scripts/agg-delegations.cjs +51 -6
  75. package/templates/_agents/scripts/agg-delegations.test.mjs +79 -1
  76. package/templates/_agents/scripts/check-engine-integrity.mjs +288 -0
  77. package/templates/_agents/scripts/check-engine-integrity.test.mjs +227 -0
  78. package/templates/_agents/scripts/check-lane-surface.mjs +7 -2
  79. package/templates/_agents/scripts/check-ledger-invariant.mjs +18 -0
  80. package/templates/_agents/scripts/check-ledger-invariant.test.mjs +16 -0
  81. package/templates/_agents/scripts/check-ledger.test.mjs +2 -0
  82. package/templates/_agents/scripts/check-loop.mjs +247 -5
  83. package/templates/_agents/scripts/check-loop.test.mjs +367 -2
  84. package/templates/_agents/scripts/confirm-doc.mjs +50 -3
  85. package/templates/_agents/scripts/confirm-doc.test.mjs +93 -1
  86. package/templates/_agents/scripts/doctor.test.mjs +251 -1
  87. package/templates/_agents/scripts/draft-sediment.mjs +178 -0
  88. package/templates/_agents/scripts/draft-sediment.test.mjs +272 -0
  89. package/templates/_agents/scripts/gate-checklist.mjs +10 -2
  90. package/templates/_agents/scripts/gate-checklist.test.mjs +7 -1
  91. package/templates/_agents/scripts/gen-wiki-board.mjs +61 -6
  92. package/templates/_agents/scripts/gen-wiki-board.test.mjs +74 -1
  93. package/templates/_agents/scripts/gen-workflow-dashboard.mjs +2 -2
  94. package/templates/_agents/scripts/gen-workflow-metrics.mjs +11 -5
  95. package/templates/_agents/scripts/kb-search.mjs +87 -12
  96. package/templates/_agents/scripts/kb-search.test.mjs +83 -0
  97. package/templates/_agents/scripts/pipeline-run.mjs +1 -1
  98. package/templates/_agents/scripts/policy.mjs +49 -0
  99. package/templates/_agents/scripts/solidify-task.mjs +1 -1
  100. package/templates/_agents/scripts/source-sync-check.test.mjs +9 -4
  101. package/templates/_agents/scripts/stage-gates.mjs +48 -1
  102. package/templates/_agents/scripts/stage-gates.test.mjs +105 -0
  103. package/templates/_agents/scripts/verify-wiki-consistency.mjs +34 -0
  104. package/templates/_agents/scripts/verify-wiki-consistency.test.mjs +44 -2
  105. package/templates/_agents/scripts/verify.mjs +12 -4
  106. package/templates/_agents/scripts/verify.test.mjs +30 -3
  107. package/templates/_agents/scripts-test-exempt.txt +3 -1
  108. package/templates/_agents/skills/wiki/SKILL.md +5 -3
  109. package/templates/wiki/INDEX.md +44 -1
  110. package/templates/wiki//347/237/245/350/257/206/346/262/211/346/267/200/346/200/273/350/247/210.html +22 -1
  111. package/templates/workflow/delegations.md +5 -4
  112. package/templates/workflow/incidents/_TEMPLATE.md +2 -0
  113. package/templates/workflow/intents/_TEMPLATE.md +3 -0
  114. package/templates/workflow/plans/_TEMPLATE.md +4 -0
  115. package/templates/workflow/specs/_TEMPLATE.md +23 -0
@@ -0,0 +1,103 @@
1
+ // rule-budget.test.mjs — 纯包源预算自检门(2026-10-11 budget-self-check)——关闭「纯包源仓不跑装户预算门」盲区。
2
+ // 背景:常驻面预算单源 templates/_agents/rule-budgets.txt(装户命名空间),此前只在装户侧强制
3
+ // (pre-commit --staged 硬拦 / check-loop 13 --all warning);v1.7.1 起本仓纯包源(5ca9e8c 删装副本)、
4
+ // 两处都不跑——bb69e19/418960a 重构净增 78B 把 .agents/commands/ 推到 65581B > 65536B,包源侧零暴露,
5
+ // 装户首提被硬拦才发现(2026-10-11 S1 冒烟)。本套件把同一张预算表映射到包源路径核对,npm test / CI
6
+ // 直接拦超限——模板增长不再静默入包。run-tests 盘面 glob 自动挂载本套件。
7
+ // 口径(对照 templates/_agents/scripts/rule-budget.sh 单源,勿两处漂移):
8
+ // 字节 = LF 归一(工作树读入去 \r\n——等价 fsize 的 git cat-file 口径,core.autocrlf 检出形态无关);
9
+ // 目录合计 = 扁平 glob(直接子文件,不入子目录);pat=首空格前 / lim=末空格后;lim 非纯数字行跳过;
10
+ // # 注释与空行跳过。
11
+ // 与装户门的口径差异(诚实声明):
12
+ // - AGENTS.md 是渲染模板:装户拿到的是占位符展开后的文件,渲染形态权威仍在装户门;本套件按全部
13
+ // STACKS 渲染取 max(BOARD_PORT 按最坏 5 位端口)做包源侧近似——单源复用 profiles/render,不复制命令表。
14
+ // - fail-loud 项(装户侧是 skip,包源侧是打包回归——表不随包或常驻面失踪 = 装户门整体变暗):
15
+ // 预算表缺失 / 解析 0 行 / 映射目标不存在 / 文件 glob 零命中。装户侧专属面(无包源对应)SKIP 并注记。
16
+ import fs from 'node:fs';
17
+ import path from 'node:path';
18
+ import { fileURLToPath } from 'node:url';
19
+ import { renderContent } from './render.mjs';
20
+ import { STACKS, pickStackVars } from './profiles.mjs';
21
+
22
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
23
+ const BUDGETS = path.join(ROOT, 'templates', '_agents', 'rule-budgets.txt');
24
+
25
+ let pass = 0, fail = 0;
26
+ function check(name, cond, detail = '') {
27
+ if (cond) { pass++; console.log(`PASS ${name}`); }
28
+ else { fail++; console.log(`FAIL ${name}${detail ? `——${detail}` : ''}`); }
29
+ }
30
+ const lfBytes = (abs) => Buffer.byteLength(fs.readFileSync(abs, 'utf8').replace(/\r\n/g, '\n'), 'utf8');
31
+
32
+ // ---- 预算表(缺失即 fail-loud:装户门会整体变暗)----
33
+ if (!fs.existsSync(BUDGETS)) {
34
+ console.log('FAIL R0 预算表缺失 templates/_agents/rule-budgets.txt——装户侧 pre-commit/检查13 将整体 skip');
35
+ process.exit(1);
36
+ }
37
+ const rows = [];
38
+ for (const line of fs.readFileSync(BUDGETS, 'utf8').split('\n')) {
39
+ const t = line.replace(/\r/g, ''); // 表文件检出即 CRLF——显式去 \r(rule-budget.sh 同款)
40
+ if (!t || t.startsWith('#')) continue;
41
+ const i = t.indexOf(' ');
42
+ if (i < 0) continue;
43
+ const pat = t.slice(0, i);
44
+ const lim = t.slice(t.lastIndexOf(' ') + 1);
45
+ if (!/^\d+$/.test(lim)) continue; // 非纯数字 lim 跳过(sh case 同款)
46
+ rows.push({ pat, lim: Number(lim) });
47
+ }
48
+ check('R1 预算表解析非空', rows.length > 0, `仅 ${rows.length} 行——表被清空即盲区复活`);
49
+
50
+ // ---- 映射:装户命名空间 → 包源路径(新预算行若只属装户侧,在此登记映射或留 SKIP)----
51
+ function mapToPkg(pat) {
52
+ if (pat === 'AGENTS.md') return 'templates/AGENTS.md';
53
+ if (pat.startsWith('.agents/')) return 'templates/_agents/' + pat.slice('.agents/'.length);
54
+ return null;
55
+ }
56
+
57
+ const REMEDY = '处置沿先例:压缩版本括号/措辞(71e0b0e、v1.7.10 三处),或在 rule-budgets.txt 注明理由调预算(512B 步进)';
58
+
59
+ for (const { pat, lim } of rows) {
60
+ const rel = mapToPkg(pat);
61
+ if (!rel) { console.log(`SKIP ${pat}——装户侧专属面,无包源对应`); continue; }
62
+ const abs = path.join(ROOT, rel);
63
+ if (pat.endsWith('/')) {
64
+ // 目录合计:扁平直接子文件,不入子目录(sh `for f in "$pat"*` + [ -f ] 同款)
65
+ if (!fs.existsSync(abs)) { check(`R-dir ${pat} 目标存在`, false, `${rel} 缺失`); continue; }
66
+ const files = fs.readdirSync(abs, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name);
67
+ const total = files.reduce((s, n) => s + lfBytes(path.join(abs, n)), 0);
68
+ check(`R-dir ${rel} 合计 ${total}B ≤ ${lim}B(${files.length} 文件)`, total <= lim, `超 ${total - lim}B。${REMEDY}`);
69
+ } else if (pat.includes('*')) {
70
+ // 文件 glob:仅支持 dir 内后缀形(*.md)——与 sh glob 展开同效
71
+ const dir = path.dirname(abs);
72
+ const base = path.basename(pat);
73
+ const suffix = base.slice(base.indexOf('*') + 1);
74
+ const files = fs.existsSync(dir) ? fs.readdirSync(dir).filter((n) => n.endsWith(suffix)) : [];
75
+ if (files.length === 0) { check(`R-glob ${pat} 有命中`, false, `${dir} 下无 *${suffix}——常驻面失踪或映射失效`); continue; }
76
+ const sizes = files.map((n) => ({ n, b: lfBytes(path.join(dir, n)) }));
77
+ const over = sizes.filter((s) => s.b > lim);
78
+ const maxS = sizes.slice().sort((a, b) => b.b - a.b)[0];
79
+ check(`R-glob ${path.dirname(rel)}/${base} ${sizes.length} 份,最大 ${maxS.n} ${maxS.b}B ≤ ${lim}B`, over.length === 0,
80
+ `${over.map((s) => `${s.n}=${s.b}B 超 ${s.b - lim}B`).join(';')}。${REMEDY}`);
81
+ } else {
82
+ if (!fs.existsSync(abs)) { check(`R-file ${pat} 目标存在`, false, `${rel} 缺失`); continue; }
83
+ const b = lfBytes(abs);
84
+ check(`R-file ${rel} = ${b}B ≤ ${lim}B`, b <= lim, `超 ${b - lim}B。${REMEDY}`);
85
+ }
86
+ }
87
+
88
+ // ---- AGENTS.md 渲染近似(装户渲染形态 = 占位符展开;包源侧按全部栈取 max)----
89
+ const agentsRow = rows.find((r) => r.pat === 'AGENTS.md');
90
+ if (agentsRow) {
91
+ const tpl = fs.readFileSync(path.join(ROOT, 'templates', 'AGENTS.md'), 'utf8');
92
+ let worst = { stack: '-', b: 0 };
93
+ for (const stack of Object.keys(STACKS)) {
94
+ const text = renderContent(tpl, { BOARD_PORT: '99999', ...pickStackVars(stack) });
95
+ const b = Buffer.byteLength(text.replace(/\r\n/g, '\n'), 'utf8');
96
+ if (b > worst.b) worst = { stack, b };
97
+ }
98
+ check(`R-render templates/AGENTS.md 渲染 max(${worst.stack}) ${worst.b}B ≤ ${agentsRow.lim}B`, worst.b <= agentsRow.lim,
99
+ `超 ${worst.b - agentsRow.lim}B——渲染形态超限装户门同样会拦,先压模板正文。${REMEDY}`);
100
+ }
101
+
102
+ console.log(`\n合计: PASS ${pass} / FAIL ${fail}`);
103
+ process.exit(fail ? 1 : 0);
package/src/run-tests.mjs CHANGED
@@ -11,16 +11,17 @@ import { fileURLToPath } from 'node:url';
11
11
 
12
12
  const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
13
13
 
14
+ // src/ 侧按盘面枚举,不硬编码清单(2026-10-09 审查 P0-1)——此前硬编码 8 项,`src/sync-hosts.test.mjs`
15
+ // 漏挂:npm test 不跑它,CI 也盖不住(ci.yml shipped 步骤只 glob `.agents/scripts/*.test.mjs`),
16
+ // 而 `templates/_agents/commands/sync-hosts.md:62`「npm test 含 sync-hosts.test.mjs 8 场景」随模板
17
+ // 下发 5 个装户宿主。检查 20 只扫 `templates/_agents/scripts/` 面,结构上抓不到 src/ 漏挂。
18
+ // 改 glob 后:新增 src 测试文件不可能再被静默漏跑(枚举漂移由盘面兜住,不靠人记得补)。
19
+ // 含宿主门禁模块回归(2026-09-27 host-gates-p1:dotnet-ca BRE 豁免/fail-closed + trae 强推 token 化)。
14
20
  const nodeSuites = [
15
- 'src/sync.test.mjs',
16
- 'src/init.test.mjs',
17
- 'src/pack.test.mjs',
18
- 'src/gates.test.mjs',
19
- 'src/fresh-init.test.mjs',
20
- 'src/stack-profile.test.mjs',
21
- // 宿主门禁模块回归(2026-09-27 host-gates-p1:dotnet-ca BRE 豁免/fail-closed + trae 强推 token 化)
22
- 'src/gate-dotnet-ca.test.mjs',
23
- 'src/trae-hooks.test.mjs',
21
+ ...fs.readdirSync(path.join(ROOT, 'src'))
22
+ .filter((f) => f.endsWith('.test.mjs'))
23
+ .sort()
24
+ .map((f) => `src/${f}`),
24
25
  ...fs.readdirSync(path.join(ROOT, 'templates/_agents/scripts'))
25
26
  .filter((f) => f.endsWith('.test.mjs'))
26
27
  .sort()
@@ -46,4 +47,10 @@ for (const rel of nodeSuites) {
46
47
  }
47
48
 
48
49
  console.log(`\n${failed ? `❌ ${failed} 个套件失败` : '✅ 全部套件通过'}`);
50
+ // 机器可读汇总行(2026-10-09 审查 P0-2):verify.mjs 的凭证计数**只认这一行**。
51
+ // 此前它 pop 通配「合计: PASS n / FAIL n」,而 45+ 套件每个都打印自己那行同形文本——
52
+ // 取到的是字母序最后一个套件的场景数(本仓实况:workflows-check.test.mjs 的 15),
53
+ // 于是「一行机器事实」的 passed 字段落成了错的事实。独立前缀 + 套件口径,嵌套同形行不再被误取。
54
+ // 口径:SUITES = 本编排器实际执行的套件数(lint 不计入;lint 红照样 exit 1,凭证不落)。
55
+ console.log(`[run-tests] 合计: SUITES ${nodeSuites.length} / FAILED ${failed}`);
49
56
  process.exit(failed ? 1 : 0);
@@ -26,8 +26,10 @@ function splitFm(text) {
26
26
  }
27
27
 
28
28
  // bodySha:剥离 frontmatter 后的正文段 sha256(缺失返 null);正文 LF 归一(2026-09-27 closing-coverage:
29
- // CRLF/LF 盘面同值——权威源与薄适配行尾不一致不再误报漂移,与 doctor §6.7 同口径)
30
- function bodySha(p) {
29
+ // CRLF/LF 盘面同值——权威源与薄适配行尾不一致不再误报漂移)。
30
+ // 单源 export(2026-10-11 bodysha-single-source):doctor §6.7 checkAdapterDrift 复用——此前 doctor
31
+ // 内联第二份实现靠注释约束同口径,收敛为单源 import 消灭漂移面。
32
+ export function bodySha(p) {
31
33
  try {
32
34
  const { body } = splitFm(fs.readFileSync(p, 'utf8'));
33
35
  return sha256(Buffer.from(body.replace(/\r\n/g, '\n'), 'utf8'));
@@ -39,6 +39,17 @@ const runHook = (command, cwd) => {
39
39
  return { decision, reason, raw: r.stdout || '', status: r.status };
40
40
  };
41
41
 
42
+ // 门禁在位桩(2026-10-09 装户端内容清理后):纯包源仓无 .githooks/——hook 第二道关卡
43
+ // (commit/push 前置 .githooks/*)在仓内必判「门禁未执行」。①/②/②附 的 token 边界断言
44
+ // 需要「门禁通过」语境:造临时目录放通过型桩门禁当 cwd;③ 另用空目录保留「缺失 → 门禁未执行」deny。
45
+ const GATE_FIXTURE = fs.mkdtempSync(path.join(os.tmpdir(), 'trae-hook-gates-'));
46
+ fs.mkdirSync(path.join(GATE_FIXTURE, '.githooks'));
47
+ for (const name of ['pre-commit', 'pre-push']) {
48
+ const stub = path.join(GATE_FIXTURE, '.githooks', name);
49
+ fs.writeFileSync(stub, '#!/usr/bin/env bash\nexit 0\n');
50
+ fs.chmodSync(stub, 0o755);
51
+ }
52
+
42
53
  // ---- ① 强推 token 化(P1-B2)----
43
54
  {
44
55
  const cases = [
@@ -54,7 +65,7 @@ const runHook = (command, cwd) => {
54
65
  ['dotnet build --force', 'none', '非 push 命令带 --force 不误拦'],
55
66
  ];
56
67
  for (const [cmd, expect, label] of cases) {
57
- const r = runHook(cmd);
68
+ const r = runHook(cmd, GATE_FIXTURE);
58
69
  check(`①${label}(${cmd})→ ${expect === 'none' ? '放行' : expect}`,
59
70
  r.decision === expect,
60
71
  `decision=${r.decision} reason=${r.reason}`);
@@ -79,7 +90,7 @@ const runHook = (command, cwd) => {
79
90
  ['git commit -m "fix: 修复"', 'none'], // commit 走 .githooks 链(本仓有钩子且全绿);deny 门本身不应拦 commit
80
91
  ];
81
92
  for (const [cmd, expect] of cases) {
82
- const r = runHook(cmd, PKG_ROOT); // cwd=本仓:git commit 场景能找到 .githooks
93
+ const r = runHook(cmd, GATE_FIXTURE); // cwd=桩门禁目录:git commit 能通过第二道关卡,只测 deny/ask 门
83
94
  check(`②${cmd} → ${expect === 'none' ? '不因 deny/ask 门拦' : expect}`,
84
95
  r.decision === expect,
85
96
  `decision=${r.decision} reason=${r.reason}`);
@@ -94,7 +105,7 @@ const runHook = (command, cwd) => {
94
105
  ['git commit -m "真正要拦的消息"', 'none', '普通消息照常'],
95
106
  ];
96
107
  for (const [cmd, expect, label] of cases) {
97
- const r = runHook(cmd, PKG_ROOT);
108
+ const r = runHook(cmd, GATE_FIXTURE);
98
109
  check(`②附 ${label} → ${expect === 'none' ? '不误拦' : expect}`,
99
110
  r.decision === expect,
100
111
  `decision=${r.decision} reason=${r.reason}`);
@@ -112,4 +123,5 @@ const runHook = (command, cwd) => {
112
123
  }
113
124
 
114
125
  console.log(`\n合计: PASS ${pass} / FAIL ${fail}`);
126
+ fs.rmSync(GATE_FIXTURE, { recursive: true, force: true });
115
127
  process.exit(fail ? 1 : 0);
@@ -0,0 +1,72 @@
1
+ // flow-kit validate:只读校验装户盘面(2026-10-10 validate-entry;对照 OpenSpec validate 形态:
2
+ // 只读 + 0/1 退出码 + --strict/--json)。
3
+ // 语义:用**包内置** check-loop 引擎(templates/_agents/scripts/check-loop.mjs 的 runCheckLoop,
4
+ // isMain 主守卫保证 import 零副作用)对目标仓 workflow/ 闭环跑全部 20 项检查——不执行装户侧
5
+ // .agents/scripts/*(供应链面:判定引擎随包版本走、目标侧零代码执行;doctor 走另一极:scriptGuard
6
+ // 先验后跑装户侧脚本,两命令互补不重复)。root 经 runCheckLoop({root}) 传入,spawnGit 以 cwd:ROOT
7
+ // 落在目标仓——tracked 过滤按目标仓 HEAD 算,与 git 钩子看到的提交面一致。
8
+ // 只读范围:check-loop 自身仅有的两处落盘都在 .agents/cache/ 运行时缓存(gate-stats 需 --gate-stats
9
+ // 才触发、added-dates 缓存 best-effort),且该目录已 gitignore——本命令不改任何文档与台账。
10
+ // 不暴露 --rev:runCheckLoop 中 rev 与 root 互斥(提交时点判定属钩子链职责,validate 只看盘面现状)。
11
+ // 输出契约:check-loop 的 HARD-BLOCK/WARN 两段式原样走 stderr(稳定输出契约,消费者 .githooks/pre-push);
12
+ // stdout 只承载本命令的汇总行 / --json——机器消费者按 stdout 解析,人按 stderr 读明细。
13
+ // 退出码:blocker → 1;--strict 下 advisory warning 同样 → 1(判据单源 validateExit,纯函数可单测)。
14
+ import fs from 'node:fs';
15
+ import path from 'node:path';
16
+ import { pathToFileURL } from 'node:url';
17
+
18
+ // 退出码判定单源(独立 export 供单元测试):引擎 exitCode 非 0 → 1;--strict 时 warning 也红;
19
+ // 结果缺失(引擎调用异常路径)fail-closed → 1。
20
+ export function validateExit(result, strict) {
21
+ if (!result || result.exitCode !== 0) return 1;
22
+ if (strict && (result.warnings || []).length > 0) return 1;
23
+ return 0;
24
+ }
25
+
26
+ export async function validate(args, pkgRoot) {
27
+ let target = process.cwd();
28
+ let strict = false;
29
+ let json = false;
30
+ for (let i = 0; i < args.length; i++) {
31
+ if (args[i] === '--dir') target = path.resolve(args[++i]);
32
+ else if (args[i] === '--strict') strict = true;
33
+ else if (args[i] === '--json') json = true;
34
+ else {
35
+ console.error(`validate: 未知参数 ${args[i]}——用法 flow-kit validate [--strict] [--json] [--dir <目录>]`);
36
+ process.exit(1);
37
+ }
38
+ }
39
+
40
+ // 装户盘面前置:枚举单源(workflow-enums.txt 等)都在 .agents/ 下,无装副本则引擎无从判定——
41
+ // 先给指引再红,不产出「20 项全断档」的噪音墙
42
+ if (!fs.existsSync(path.join(target, '.agents'))) {
43
+ const msg = `缺 .agents/——validate 校验 flow-kit 装户盘面,先跑 npx agentic-flow-kit init`;
44
+ if (json) console.log(JSON.stringify({ target, strict, checkLoopExitCode: null, exitCode: 1, blockers: [msg], warnings: [] }, null, 2));
45
+ else console.error(`❌ validate: ${target} ${msg}`);
46
+ process.exit(1);
47
+ }
48
+
49
+ // 动态 import 包内置引擎:pkgRoot = npx 缓存 / 全局安装 / 仓内直跑三种形态下都指向包源模板
50
+ const engineUrl = pathToFileURL(path.join(pkgRoot, 'templates', '_agents', 'scripts', 'check-loop.mjs')).href;
51
+ const { runCheckLoop } = await import(engineUrl);
52
+ const result = runCheckLoop({ root: target });
53
+ const code = validateExit(result, strict);
54
+
55
+ if (json) {
56
+ console.log(JSON.stringify({
57
+ target,
58
+ strict,
59
+ checkLoopExitCode: result.exitCode,
60
+ exitCode: code,
61
+ blockers: result.blockers,
62
+ warnings: result.warnings,
63
+ }, null, 2));
64
+ } else {
65
+ const n = (result.blockers || []).length;
66
+ const w = (result.warnings || []).length;
67
+ if (code === 0) console.log(`✅ flow-kit validate 通过——blocker 0 / warning ${w}${w ? '(advisory 不阻断;--strict 可把 warning 判红)' : ''}`);
68
+ else if (result.exitCode !== 0) console.log(`❌ flow-kit validate 未通过——blocker ${n} / warning ${w}(明细见上方 HARD-BLOCK / WARN 输出)`);
69
+ else console.log(`❌ flow-kit validate(--strict)未通过——warning ${w} 条判红(默认退出码 0,advisory 不阻断)`);
70
+ }
71
+ process.exit(code);
72
+ }
@@ -0,0 +1,95 @@
1
+ // validate.test.mjs — flow-kit validate 断言(2026-10-10 validate-entry)。
2
+ // 两层:validateExit 纯函数直测(退出码判据单源)+ 端到端 spawn 真 CLI(bin/flow-kit.mjs validate,
3
+ // --json 断言 stdout 可解析、退出码契约、缺 .agents/ 指引、未知参数用法错)。
4
+ // fixture 沿 check-loop.test.mjs 同款:临时根(workflow 四目录 + .agents/ + 枚举单源内联 8 键),
5
+ // 自包含、不依赖真实 git 与 workflow 文档。
6
+ import fs from 'node:fs';
7
+ import os from 'node:os';
8
+ import path from 'node:path';
9
+ import { spawnSync } from 'node:child_process';
10
+ import { fileURLToPath } from 'node:url';
11
+ import { validateExit } from './validate.mjs';
12
+
13
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
14
+ const CLI = path.join(ROOT, 'bin', 'flow-kit.mjs');
15
+
16
+ let pass = 0, fail = 0;
17
+ function check(name, cond, detail = '') {
18
+ if (cond) { pass++; console.log(`PASS ${name}`); }
19
+ else { fail++; console.log(`FAIL ${name}${detail ? `——${detail}` : ''}`); }
20
+ }
21
+
22
+ // ---- validateExit 纯函数(判据单源)----
23
+ check('V1 blocker → 1', validateExit({ exitCode: 1, blockers: ['x'], warnings: [] }, false) === 1);
24
+ check('V2 干净 → 0', validateExit({ exitCode: 0, blockers: [], warnings: [] }, false) === 0);
25
+ check('V3 warning 非 strict → 0', validateExit({ exitCode: 0, blockers: [], warnings: ['w'] }, false) === 0);
26
+ check('V4 warning + strict → 1', validateExit({ exitCode: 0, blockers: [], warnings: ['w'] }, true) === 1);
27
+ check('V5 引擎结果缺失 fail-closed → 1', validateExit(null, false) === 1 && validateExit(undefined, true) === 1);
28
+
29
+ // ---- fixture(枚举单源内联,同 check-loop.test.mjs#ENUMS_FIXTURE)----
30
+ const ENUMS_FIXTURE = [
31
+ 'doc.status.all=draft approved done superseded cancelled',
32
+ 'doc.status.confirmed=approved done superseded cancelled',
33
+ 'doc.status.active=draft approved',
34
+ 'doc.status.terminal=done superseded cancelled',
35
+ 'doc.status.abandoned=superseded cancelled',
36
+ 'incident.status.all=open fixed closed',
37
+ 'incident.status.active=open',
38
+ 'level.all=L0 L1 L2 L3',
39
+ ].join('\n') + '\n';
40
+
41
+ const mkfix = () => {
42
+ const d = fs.mkdtempSync(path.join(os.tmpdir(), 'flow-kit-validate-'));
43
+ for (const s of ['intents', 'specs', 'plans', 'incidents']) {
44
+ fs.mkdirSync(path.join(d, 'workflow', s), { recursive: true });
45
+ }
46
+ fs.mkdirSync(path.join(d, '.agents'), { recursive: true });
47
+ fs.writeFileSync(path.join(d, '.agents', 'workflow-enums.txt'), ENUMS_FIXTURE);
48
+ return d;
49
+ };
50
+
51
+ const runValidate = (dir, args = []) => spawnSync(process.execPath, [CLI, 'validate', '--dir', dir, ...args], {
52
+ cwd: ROOT, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024,
53
+ });
54
+ const stdoutOf = (r) => r.stdout || '';
55
+ const stderrOf = (r) => r.stderr || '';
56
+
57
+ // ---- 端到端:绿盘面(空四目录 + 枚举单源)----
58
+ const green = mkfix();
59
+ const rg = runValidate(green, ['--json']);
60
+ let greenJson = null;
61
+ try { greenJson = JSON.parse(stdoutOf(rg)); } catch { /* 解析失败在断言里报 */ }
62
+ check('V6 绿盘面 exit 0', rg.status === 0, `exit=${rg.status}\n${stdoutOf(rg)}\n${stderrOf(rg).slice(0, 300)}`);
63
+ check('V7 --json stdout 可解析且 blockers 空', !!greenJson && Array.isArray(greenJson.blockers) && greenJson.blockers.length === 0, stdoutOf(rg).slice(0, 300));
64
+ check('V8 --json 契约字段在位', !!greenJson && greenJson.exitCode === 0 && greenJson.checkLoopExitCode === 0 && greenJson.strict === false && typeof greenJson.target === 'string', JSON.stringify(greenJson));
65
+ // --strict 契约端到端:同一盘面 fixture 恒产出 advisory warning(装副本骨架件缺失类,绿判不受其扰),
66
+ // 非 strict 绿(V6 已证)、strict 判红且 checkLoopExitCode 保持 0——判红来源是 strict 规则而非引擎
67
+ const rs = runValidate(green, ['--json', '--strict']);
68
+ let strictJson = null;
69
+ try { strictJson = JSON.parse(stdoutOf(rs)); } catch { /* 同上 */ }
70
+ check('V9 --strict 把 advisory warning 判红(checkLoopExitCode 仍 0)',
71
+ rs.status === 1 && !!strictJson && strictJson.exitCode === 1 && strictJson.checkLoopExitCode === 0 && strictJson.strict === true && strictJson.warnings.length > 0,
72
+ `exit=${rs.status}\n${stdoutOf(rs).slice(0, 300)}`);
73
+
74
+ // ---- 端到端:红盘面(同名配对断档:intent 无同名 spec/plan → 检查 1 hard-block)----
75
+ const red = mkfix();
76
+ fs.writeFileSync(path.join(red, 'workflow', 'intents', '2026-01-01-solo.md'),
77
+ '---\nstatus: draft\nlevel: L1\n---\n# INTENT — solo\n');
78
+ const rr = runValidate(red, ['--json']);
79
+ let redJson = null;
80
+ try { redJson = JSON.parse(stdoutOf(rr)); } catch { /* 同上 */ }
81
+ check('V10 配对断档 exit 1', rr.status === 1, `exit=${rr.status}\n${stdoutOf(rr).slice(0, 300)}\n${stderrOf(rr).slice(0, 500)}`);
82
+ check('V11 --json blockers 非空且明细走 stdout JSON', !!redJson && redJson.blockers.length > 0, stdoutOf(rr).slice(0, 300));
83
+
84
+ // ---- 指引与用法错 ----
85
+ const bare = fs.mkdtempSync(path.join(os.tmpdir(), 'flow-kit-validate-bare-'));
86
+ const rb = runValidate(bare);
87
+ check('V12 缺 .agents/ → exit 1 且给 init 指引', rb.status === 1 && /init/.test(stderrOf(rb)), `exit=${rb.status}\n${stderrOf(rb).slice(0, 300)}`);
88
+ const ru = runValidate(green, ['--bogus']);
89
+ check('V13 未知参数 → exit 1 用法行', ru.status === 1 && /用法/.test(stderrOf(ru)), `exit=${ru.status}\n${stderrOf(ru).slice(0, 300)}`);
90
+
91
+ // ---- 清理 ----
92
+ for (const d of [green, red, bare]) fs.rmSync(d, { recursive: true, force: true });
93
+
94
+ console.log(`\n合计: PASS ${pass} / FAIL ${fail}`);
95
+ process.exit(fail ? 1 : 0);
@@ -28,7 +28,8 @@
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 无关);L0/L1 协作道可 `--batch` 多份一次代录(台账 `brief:true`,15 并录告警豁免;L2/L3 仍逐份)。
32
+ - **改动面 scope = 授权的机器事实(2026-10-09 起)**:confirm-doc 落账自动记 `scope{files,sha256}`(被确认文档 + `git status` 实测未提交改动集,机器派生、**AI 无输入面**)。分辨力由 scope + 指纹承担,`--delegated` 的摘要降级为人类可读留痕——**「每次换一句原话」纪律退役**,复用同一句摘要不再削弱任何判据。check-loop 15 校验受管行带 scope 且摘要自洽(`policyVersion ≥ 7` 起,旧版本与存量行零新增告警)。
32
33
 
33
34
  ### 检索、看板与量化
34
35
 
@@ -45,6 +46,7 @@
45
46
  `wiki/` 是项目知识沉淀,按主题目录归位;原始 / 一次性文档归档 `wiki/drafts-archive/<日期-主题>/`(只读不增量)。
46
47
 
47
48
  - 人工只维护两样:**磁盘文件本身 + `wiki/INDEX.md` 速览表「用途」列**;计数 / 映射表 / 看板 DATA 均为生成区,勿手改——`node .agents/scripts/gen-wiki-board.mjs` 重生成,`node .agents/scripts/verify-wiki-consistency.mjs` 三方一致性验证(触及 `wiki/` 的提交会被 pre-commit 台账门禁校验)。
49
+ - **沉淀与血缘(2026-10-10 起)**:关单时 `node .agents/scripts/draft-sediment.mjs workflow/<intents|incidents>/<本单>.md` 自动把高价值节抽成 `drafts-archive/` 草稿(`--dry-run` 预览;全占位拒抽);草稿是中间态,人工归类到 `wiki/<主题>/` 后跑生成器 + 验证。沉淀关系用 frontmatter 双向链接:wiki 文件 `来源: workflow/…`、workflow 文件 `沉淀: wiki/…`(不沉淀写 `沉淀: 无`)——两向字段的存在性由 verify-wiki-consistency 与 check-loop 校验,改名 / 移动时须同步。
48
50
  - wiki 看板:`wiki/知识沉淀总览.html`。
49
51
 
50
52
  ## 项目适配区(项目自填)
@@ -27,7 +27,7 @@ next: .agents/commands/test.md
27
27
 
28
28
  起草 intent / spec / plan 任一件后,**在输出草稿全文前先给三件要点总览**(各一件:目标 / 关键取舍 /
29
29
  验收判据 diff,合计 ≤12 行)——用户一次看整体再逐件拍板,省三次盲确认的上下文切换。
30
- 确认纪律不变:逐件过目全文 + 逐件 `confirm-doc` 代录(禁并录、禁复用同句)。本阶段总览侧重:plan 任务拆解与两道确认门位置。
30
+ 确认纪律不变:逐件过目全文 + 逐件 `confirm-doc` 代录(禁并录;摘要可复用——分辨力由 scope+指纹承担,2026-10-09 起)。本阶段总览侧重:plan 任务拆解与两道确认门位置。
31
31
 
32
32
  ## 1. 执行
33
33
 
@@ -38,7 +38,7 @@ next: .agents/commands/test.md
38
38
  - 复制 `workflow/plans/_TEMPLATE.md` → `workflow/plans/YYYY-MM-DD-<主题>.md`(与入口文档同名)
39
39
  - L2/L3 按 4 节填写:改动方案 / 任务拆解(每个任务有判据) / 执行顺序 / 验证计划(与 fill-plan.mjs 单源一致)
40
40
  - L1 使用 Quick-Plan 极简三节:改动方案 / 约束与风险(原 Spec 核心) / 验证计划;多文件多步骤时可补任务拆解/执行顺序
41
- 3. 停下,输出 plan 草稿全文给用户过目,一句"可以"即确认。**逐件确认**:入口文档 / spec / plan 各自的确认点不得并作一次,plan 草稿须全文过目(不接受摘要代替)。**逐件配逐件原话**:`--delegated` 每次传当次实际放行的措辞,不复用同句——复用同句使台账 quote 失去分辨力(事后对质看不出放行的是哪一件);原因与根治见 workflow/papercuts.md 2026-09-28。
41
+ 3. 停下,输出 plan 草稿全文给用户过目,一句"可以"即确认。**逐件确认**:入口文档 / spec / plan 各自的确认点不得并作一次,plan 草稿须全文过目(不接受摘要代替)。**逐件代录**:`--delegated "<授权摘要>"` 每件单独一次——摘要为人类可读留痕,复用同句不削弱判据(分辨力由落账自动记录的改动面 `scope{files,sha256}` 与文档指纹承担,2026-10-09 起)。
42
42
  **确认落态**:唯一入口 `node .agents/scripts/confirm-doc.mjs <path>`(draft→approved / approved→done;TTY 两形态 / 一次一份多份并录被拒 / 台账对质全口径见根 `AGENTS.md` §1.3 确认门)。
43
43
  4. 确认后 → plan 状态 draft → approved,并在 plan 内补「确认与复核」节(确认结果 + 日期)。确认环节须在文档留痕:`approved` 是确认的机器可见态,`done` 只在关单出现,禁从 `draft` 直跳 `done`;确认后立即 `docs(workflow)` 单独提交留痕(根 `AGENTS.md` 提交约定;check-loop 检查 14 口径)。
44
44
 
@@ -48,7 +48,7 @@ next: .agents/commands/test.md
48
48
  - **先拉同模块历史坑**:按入口文档 `模块:` 跑 `node .agents/scripts/kb-search.mjs --scope workflow --module <token> "<关键词>"`——命中 incident 的根因 / 防复发条目列入改动自查
49
49
  - 列改动文件清单(读后改 vs 新建)
50
50
  - 列步骤(改 X 文件做 Y → 改 Z 文件做 W)
51
- - **owned 文件同步**:kit.owned 列表文件(AGENTS.md / workflow 模板等)sync 不动,须列「手动同步装副本」步骤(见 incidents/2026-09-25-wf-runtime 复盘)
51
+ - **owned 文件同步**:kit.owned 列表文件(AGENTS.md / workflow 模板等)sync 不动,须列「手动同步装副本」步骤(仅包源/dogfood 双形态仓适用;纯装户仓 owned 即项目自有文件,无此步)
52
52
  - 标注触达红线(对照根 `AGENTS.md` 红线/约束相关节(如「项目适配区」「门禁与提交」)+ 改动域目录级 `AGENTS.md`(如有))
53
53
  6. 停下,等用户确认改动清单。
54
54
 
@@ -71,32 +71,13 @@ next: .agents/commands/test.md
71
71
 
72
72
  - 红线(架构 / 数据 / 安全等)正文与豁免见根与目录级 `AGENTS.md`(如有)——已由 pre-commit 机器门强制的部分以钩子为准,写代码前先过一遍目录级 AGENTS
73
73
 
74
- ## 4. 改权威源后必跑(薄适配同步防漏)
75
-
76
- - 改 `templates/_agents/{commands,roles}/*.md` 正文后跑 `node bin/flow-kit.mjs sync-hosts --diff` → 拍板 → `--apply` 单向同步(只动正文段,frontmatter 不动);口径与边界详见 `.agents/commands/sync-hosts.md`
77
- - 改根 `AGENTS.md`(owned 常驻面)后跑 `node .agents/scripts/edit-face-check.mjs`——工作树面快检(阶段索引/引用/预算 advisory),别等提交门才发觉砍坏(2026-10-09 B2)
78
-
79
- ## 5. 改 doctor / check-loop 后必跑(三处口径对账)
80
-
81
- - 改 `src/doctor.mjs` 加新 § 检查项 → 跑 `node .agents/scripts/gate-checklist.mjs --diff` 看 doctor ↔ check-loop 对照表
82
- - 改 `.agents/scripts/check-loop.sh` 加新 § 检查项 → 同上
83
- - 任一处有而另一边无 → 工具报"缺点",由用户拍板是否补齐(不自动同步,B-b 决策);详见 `.agents/commands/gate-checklist.md`
84
-
85
- ## 6. 改包源后必跑(装户面同步一致性)
86
-
87
- - 改 `templates/_agents/*` 包源后 → 跑 `node .agents/scripts/source-sync-check.mjs --diff` 看装副本 `.agents/` 是否同步(缺失 / 孤儿 / 漂移三类差异)
88
- - 漂移说明包源改了装副本未跟;缺失说明新增文件 init 未执行;孤儿说明装副本独有(可能是装户配置,不删)
89
- - **新增 managed 类文件须经 init/sync 登记,禁手动双写绕过台账**——手工同时写 `templates/_agents/` 与 `.agents/` 会绕过台账登记,使该文件永远不被 sync 升级(`doctor` 台账覆盖率检查会拦;见 incidents/2026-09-26-managed-ledger-adopt 复盘)
90
- - 门禁边界(pre-commit 双源一致性):比较工作树非暂存区——两侧改好、分两笔提交不拦(最终一致即可);包源删除文件退化为「孤儿」只报告不拦,删包源须手动清理装副本残件(复核 P2-2 声明,2026-09-27 gate-hardening-p2-batch)
91
- - B-b 决策「只报告不修复」;详见 `.agents/commands/source-sync-check.md`
92
-
93
- ## 7. 子代理调用约定
74
+ ## 4. 子代理调用约定
94
75
 
95
76
  - plan 起草、风险判断、改动清单与两道确认门由主智能体负责;仅两道确认后范围明确的工作包可委派 `implementer`,公共接口 / Schema / 依赖 / 安全 / 权限 / 破坏性操作不得委派。
96
77
  - 多工作包并行 / 链式推进:编排机制(声明随 plan 草稿一并确认;确认后读 `.agents/workflows/` 按机制文档执行,`steps/` 为项目自定义步骤扩展点);详见 `.agents/workflows/_TEMPLATE.md`。
97
78
  - 子智能体返回后,主智能体须检查 diff 与授权文件范围、确认未覆盖既有修改并重跑相关验证;不支持子智能体时 `fallback: main`,验收标准不变。
98
79
 
99
- ## 8. 确认后
80
+ ## 5. 确认后
100
81
 
101
82
  - plan 状态 → done(实现完成;intent / spec 关单在 test)
102
83
  - commit:L1+ 入口文档 / spec / plan 随代码同一提交(Conventional Commits 中文 feat/fix/refactor/perf);docs 豁免与 bootstrap 可单独 `docs(*)`
@@ -49,6 +49,7 @@ next: .agents/commands/maintain.md(仅事故时)
49
49
  ## 6. 上线后观察(用户观察,Maintain 阶段接手)
50
50
 
51
51
  - 观察期:上线后 24h;由用户观察错误率 / 延迟 / 关键业务流(无监控时人工看)
52
+ - 异常判据(观察期退出规则):项目口径填 `workflow/regression-checklist.md`——无监控时的最低判据:错误日志 / 异常告警 / 关键业务流失败**任一出现即触发 §5 回滚预案,不等 24h**;无异常满 24h 方可闭环
52
53
  - 闭环宣告:用户确认无异常后才算闭环;AI 不得自行宣告
53
54
  - 异常回落:超阈值 → 生成 `workflow/incidents/YYYY-MM-DD-<主题>.md`,进 `maintain.md`(止血方案用户拍板,见 maintain.md §0)
54
55
  - 事故响应:单用途账号 + 最小权限;不得跨智能体协作推送修复(参考 Anthropic 教训)
@@ -24,7 +24,7 @@ next: .agents/commands/build.md
24
24
 
25
25
  起草 intent / spec / plan 任一件后,**在输出草稿全文前先给三件要点总览**(各一件:目标 / 关键取舍 /
26
26
  验收判据 diff,合计 ≤12 行)——用户一次看整体再逐件拍板,省三次盲确认的上下文切换。
27
- 确认纪律不变:逐件过目全文 + 逐件 `confirm-doc` 代录(禁并录、禁复用同句)。本阶段总览侧重:spec 方案取舍与 L3 复核安排。
27
+ 确认纪律不变:逐件过目全文 + 逐件 `confirm-doc` 代录(禁并录;摘要可复用——分辨力由 scope+指纹承担,2026-10-09 起)。本阶段总览侧重:spec 方案取舍与 L3 复核安排。
28
28
 
29
29
  ## 1. 执行
30
30
 
@@ -57,6 +57,6 @@ next: .agents/commands/build.md
57
57
  ## 4. 确认后
58
58
 
59
59
  - spec 状态 draft → approved
60
- - **确认落态**:唯一入口 `node .agents/scripts/confirm-doc.mjs <path>`(draft→approved / approved→done;TTY 两形态 / 逐件调用+逐件原话 / 台账对质全口径见根 `AGENTS.md` §1.3 确认门)。
60
+ - **确认落态**:唯一入口 `node .agents/scripts/confirm-doc.mjs <path>`(draft→approved / approved→done;TTY 两形态 / 逐件代录(摘要可复用——分辨力由 scope+指纹承担)/ 台账对质全口径见根 `AGENTS.md` §1.3 确认门)。
61
61
  - 立即 `docs(workflow)` 单独提交留痕(根 `AGENTS.md` 提交约定;check-loop 检查 14 口径)
62
62
  - 进 `next: .agents/commands/build.md`
@@ -88,6 +88,4 @@ npm test # 含 gate-checklist 套件
88
88
 
89
89
  ## 6. 相关留痕
90
90
 
91
- - intent(登记表重写):`workflow/intents/2026-09-25-gate-checklist-registry.md`
92
- - intent(工具首建):`workflow/intents/2026-09-25-gate-checklist.md`
93
- - 父 incident:`workflow/incidents/2026-09-25-wf-runtime.md`(owned 漂移未对账,催生本工具)
91
+ - 立法出处:2026-09-25(登记表重写 + 工具首建;父事件 = owned 漂移未对账,催生本工具)——留痕在包源仓 git 历史与 CHANGELOG,装户径不引实例路径
@@ -28,7 +28,7 @@ next: L1 → .agents/commands/build.md (起草 Quick-Plan) ;L2/L3 → .agents/
28
28
  - 红线勾选对照根 `AGENTS.md` 红线/约束相关节(如「项目适配区」「门禁与提交」)+ 改动域目录级 `AGENTS.md`(如有);**没触及就全不勾**,不要硬填
29
29
  3. 写不出可测试判据 = 还没想清楚 → 反问澄清,不要硬填。
30
30
  4. 起草后停下,输出草稿全文给用户过目,一句"可以"即确认。
31
- **确认落态**:唯一入口 `node .agents/scripts/confirm-doc.mjs <path>`(draft→approved / approved→done;TTY 两形态 / 逐件调用+逐件原话 / 台账对质全口径见根 `AGENTS.md` §1.3 确认门)。
31
+ **确认落态**:唯一入口 `node .agents/scripts/confirm-doc.mjs <path>`(draft→approved / approved→done;TTY 两形态 / 逐件代录(摘要可复用——分辨力由 scope+指纹承担)/ 台账对质全口径见根 `AGENTS.md` §1.3 确认门)。
32
32
 
33
33
  ## 2. 技能辅助(可选,宿主级 skills)
34
34
 
@@ -100,6 +100,4 @@ flow-kit doctor # 10/0/0
100
100
 
101
101
  ## 6. 相关留痕
102
102
 
103
- - intent:`workflow/intents/2026-09-25-source-sync-check.md`
104
- - plan:`workflow/plans/2026-09-25-source-sync-check.md`
105
- - 父 incident:`workflow/incidents/2026-09-25-wf-runtime.md`(包源改了装副本未同步——本工具预防性体检)
103
+ - 立法出处:2026-09-25 source-sync-check(intent/plan;父事件 = 包源改了装副本未同步,本工具预防性体检)——留痕在包源仓 git 历史与 CHANGELOG,装户径不引实例路径
@@ -80,6 +80,4 @@ sh .agents/scripts/check-loop.sh # Windows 无 sh 时 doctor 已
80
80
 
81
81
  ## 5. 相关留痕
82
82
 
83
- - intent:`workflow/intents/2026-09-25-cross-host-sync.md`
84
- - plan:`workflow/plans/2026-09-25-cross-host-sync.md`(含 B-b 方案拍板)
85
- - incident:`workflow/incidents/2026-09-25-wf-runtime.md`(双源纪律教训,规范 §7.x 包源环境跳过)
83
+ - 立法出处:2026-09-25 cross-host-sync(intent/plan,含 B-b 方案拍板;父事件 = wf-runtime 双源纪律教训,规范 §7.x 包源环境跳过)——留痕在包源仓 git 历史与 CHANGELOG,装户径不引实例路径
@@ -35,7 +35,7 @@ next: L1 关单结束 ;L2/L3 上 prod 时 → .agents/commands/deploy.md
35
35
  项目测试命令 # 自动化测试
36
36
  项目类型检查命令 # 前端类型检查/构建(涉及前端时跑)
37
37
  node .agents/scripts/verify-wiki-consistency.mjs # 触及 wiki/ 时跑(exit 1 即先修台账再继续:跑 gen-wiki-board.mjs 补索引/看板;pre-commit 已按增量自动拦,此行用于改完即验)
38
- node bin/flow-kit.mjs doctor # §6.5 delegations + §6.6 owned 漂移 + §6.7 跨宿主薄适配正文漂移(装户环境;包源环境 §6.7 skipped)
38
+ npx agentic-flow-kit doctor # §6.5 delegations + §6.6 owned 漂移 + §6.7 跨宿主薄适配正文漂移(装户环境;包源环境 §6.7 skipped)
39
39
  ```
40
40
 
41
41
  > **环境注记**:本地服务占用构建产物导致编译失败、多会话并行时「谁跑静态门 / 提交谁先停服务」等约定——如有,见项目注记 `.agents/notes/runtime-env.md` §2,跑静态门前先过一眼。
@@ -74,11 +74,12 @@ node bin/flow-kit.mjs doctor # §6.5 delegations + §6.6 o
74
74
 
75
75
  - 输出测试报告(通过项 / 失败项);失败项已修复并复测通过。
76
76
  - **关单**(日常闭环点,不依赖是否上 prod):
77
- - 关单前一键过门:`node .agents/scripts/verify.mjs --doc workflow/intents/<本单>.md`(npm test + check-loop 固定编排,任一失败非零退出,非绿不关单);**`--doc` 把测试绿凭证绑到本单**——不带它落下的绿行不给任何单背书,confirm-doc 前置与检查 8 对账只认带本单 `--doc` 的绿行(仅 v6+ 生效);**装户仓**(根目录无 `package.json`,工程在子目录)默认 `npm test` 不可跑 → 脚本**显式跳过步骤 1 且不落测试绿凭证**(防假绿)并提示;此时按项目口径手工跑测试,或带 `--test-cmd "<项目测试命令>"` 指定(2026-10-07 papercut)
77
+ - 关单前一键过门:`node .agents/scripts/verify.mjs --doc workflow/intents/<本单>.md`(npm test + check-loop 固定编排,任一失败非零退出,非绿不关单);**`--doc` 把测试绿凭证绑到本单**——不带它落下的绿行不给任何单背书,confirm-doc 前置与检查 8 对账只认带本单 `--doc` 的绿行(仅 v6+ 生效);**装户仓**(根目录无 `package.json`,工程在子目录)默认 `npm test` 不可跑 → 脚本**显式跳过步骤 1 且不落测试绿凭证**(防假绿)并提示;此时按项目口径手工跑测试,或带 `--test-cmd "<项目测试命令>"` 指定
78
78
  - 新需求:逐条勾验入口 intent「验收标准」,每条补「证据:」(例如:粘贴 CI 运行结果、UI 截图路径、或具体的 commit SHA),intent → done;同名 spec/Quick-Plan(若有)仍 approved → done。**注意:勾选但缺证据会被 check-loop 拦截(hard-block)**
79
- - **approved 留痕提交**(2026-09-30 起):done 确认前确认**本次关单文档**的 approved 态已进 git 历史(越早越好——approved 后即应提交;若尚未,先补提交再 done;门为逐份校验,三件套均关单时逐份各自满足)——confirm-doc 的 done 前置门硬校验(历史未出现「状态: approved」→ 拒绝落账);done 后再提交 done 态与其余改动
80
- - **确认落态**:唯一入口 `node .agents/scripts/confirm-doc.mjs <path>`(两形态 / 逐件+逐件原话 / done 内容绑定(关单编辑先于 done 确认、confirm-doc 是最后一次写入)/ 台账对质全口径见根 `AGENTS.md` §1.3 确认门)。
79
+ - **approved 留痕提交**:done 确认前确认**本次关单文档**的 approved 态已进 git 历史(越早越好——approved 后即应提交;若尚未,先补提交再 done;门为逐份校验,三件套均关单时逐份各自满足)——confirm-doc 的 done 前置门硬校验(历史未出现「状态: approved」→ 拒绝落账);done 后再提交 done 态与其余改动
80
+ - **确认落态**:唯一入口 `node .agents/scripts/confirm-doc.mjs <path>`(两形态 / 逐件代录(摘要可复用——分辨力由 scope+指纹承担)/ done 内容绑定(关单编辑先于 done 确认、confirm-doc 是最后一次写入)/ 台账对质全口径见根 `AGENTS.md` §1.3 确认门)。
81
81
  - 修复:防复发验证已落地,incident → closed(经确认门 confirm-doc:open→fixed / fixed→closed 两跳、无单跳;2026-09-28 起 check-loop 15 对账——关单编辑先于 closed 确认,口径同 done 内容绑定)
82
+ - **沉淀草稿**:intent done / incident closed 后跑 `node .agents/scripts/draft-sediment.mjs workflow/<intents|incidents>/<本单>.md`——自动抽高价值节(intent:影响面/触达红线;incident:影响面/根因/为什么之前没拦住/复盘三件套)落 `wiki/drafts-archive/<日期-主题>/` 草稿、回写源文件 `沉淀` 字段、重跑 gen-wiki-board(归档计数随动,否则 verify-wiki-consistency 拦提交)。全占位单拒抽(正常,无沉淀价值)。`--dry-run` 先预览。草稿是**中间态**:人工读一遍归类到 `wiki/<主题>/`(补主题与用途、保留 `来源` 字段)后跑 gen-wiki-board + verify-wiki-consistency。沉淀是人工判断——机器只代抽与登记,值不值得入库由人定;不沉淀写 `沉淀: 无` 显式豁免(check-loop 的 `[WARN 未沉淀]` 随之消除)
82
83
  - **同族收尾**:入口置终态时,同名 plan 一并置 `done`(spec 见上条),不留 `approved` 孤儿(口径同看板「入口已 done,本 plan 未终态」告警)
83
84
  - 主智能体自做的 L1+ 新需求在 `workflow/delegations.md`「自做任务结果表」记一行(修复类不重复记)
84
85
  - **委派快照回写**:关单后若 `workflow/delegations.md` §月度聚合快照缺当月行或 doctor §6.5 报陈旧 → 跑 `node .agents/scripts/agg-delegations.cjs` 并回贴当月快照行
@@ -0,0 +1,53 @@
1
+ # 控制面清单(单源:本文件——check-lane-surface.mjs 检查 C 共读,pre-commit「泳道完整性」门禁)
2
+ # 格式:每行一条 ERE(grep -E 兼容),匹配暂存文件的仓库相对路径(/ 分隔)。
3
+ # # 注释与空行忽略;本文件缺失或清单为空 = 检查 C 跳过(装户无感升级,同 lane-surfaces.txt 口径)。
4
+ # 正则语法错误行 → 门禁 fail-closed 阻断(防静默空洞)。
5
+ #
6
+ # 语义(与 lane-surfaces.txt 的「触达面」不同一档):命中本清单 = **改动门禁/授权/权限系统本身**。
7
+ # 此时必须有活跃(draft/approved / open/fixed)L2/L3 入口罩着,**否则 hard 拦**——
8
+ # 与检查 A 的唯一区别是「无任何活跃入口」这一档:检查 A 出 advisory(docs/bootstrap 豁免的合法形态),
9
+ # 检查 C 出 BLOCK。理由(2026-10-09 P0-A):此前 `.agents/` 脚本与 commands 属 new-task.md §5
10
+ # 「docs 级豁免 → 直接 commit」,而检查 A 在无活跃入口时只 advisory,两条各自合理的规则**组合起来**
11
+ # 等于「改 check-loop.mjs 削弱某条检查 → 不需要入口文档 → 不触发同步确认门 → 提交通过」;
12
+ # CI 侧又跑被测分支自己的 check-loop(自指裁判)。本清单把这条通路从「advisory」抬到「必过同步确认门」。
13
+ # 边界(诚实声明):仍是本地信任边界内的**归因**机制——整文件删清单即可摘掉本门(该文件本身在清单内,
14
+ # 删它同样要过门,但门是可以被 `--no-verify` / 不挂 hooksPath 绕过的);堵的是「顺手改弱」,
15
+ # 不宣称关闭「刻意绕过」。事后对质面 = 台账 + git 历史 + check-engine-integrity 的基线比对。
16
+ #
17
+ # 覆盖哪些面(为什么各条目都在控制面上):
18
+ # 引擎判据本体(包源 + 装副本两面都列——单边改动同样要过门)
19
+ ^templates/_agents/scripts/
20
+ ^\.agents/scripts/
21
+ # git 钩子面(本地唯一机器门的编排)与钩子内的门禁实现件
22
+ ^templates/_githooks/
23
+ ^\.githooks/
24
+ ^templates/_agents/hooks/
25
+ ^\.agents/hooks/
26
+ # CI 远端门定义(GitHub 面;owned 件,删一行等于摘一道门)
27
+ ^templates/_github/
28
+ ^\.github/
29
+ # 权限与门禁配置本体(deny 表 / 自治信任 / 触达面与豁免清单 / 引擎基线 / 检查豁免登记)
30
+ # .json 件为装户安装时生成、无包源双生子;.txt 单源件包源与装副本两面都列
31
+ ^\.agents/settings\.json$
32
+ ^\.agents/trust-mode\.json$
33
+ ^\.agents/kit\.json$
34
+ ^\.agents/lane-surfaces\.txt$
35
+ ^templates/_agents/lane-surfaces\.txt$
36
+ ^\.agents/control-surfaces\.txt$
37
+ ^templates/_agents/control-surfaces\.txt$
38
+ ^\.agents/engine-lock\.json$
39
+ ^templates/_agents/engine-lock\.json$
40
+ ^\.agents/scripts-test-exempt\.txt$
41
+ ^templates/_agents/scripts-test-exempt\.txt$
42
+ ^\.agents/workflow-enums\.txt$
43
+ ^templates/_agents/workflow-enums\.txt$
44
+ ^\.agents/ledger-line-exempt\.json$
45
+ ^\.agents/rule-budgets\.txt$
46
+ ^templates/_agents/rule-budgets\.txt$
47
+ ^\.agents/metric-claims\.txt$
48
+ ^templates/_agents/metric-claims\.txt$
49
+ # 常驻规则面(AI 行为契约本体——改 prompt 面与改判据面同档;体积预算只约束「多大」不约束「改了什么」)
50
+ ^AGENTS\.md$
51
+ ^templates/AGENTS\.md$
52
+ # 可装门禁模块(红线守卫的判据)
53
+ ^modules/gates/