agentic-flow-kit 1.8.0 → 1.8.2

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 (49) hide show
  1. package/modules/hosts/claude/commands/wf-new-task.md +1 -0
  2. package/modules/hosts/claude/commands/wf-review.md +1 -1
  3. package/modules/hosts/codex/commands/wf-new-task.md +1 -0
  4. package/modules/hosts/codex/commands/wf-review.md +1 -1
  5. package/modules/hosts/cursor/commands/wf-new-task.md +1 -0
  6. package/modules/hosts/cursor/commands/wf-review.md +1 -1
  7. package/modules/hosts/opencode/commands/wf-new-task.md +1 -0
  8. package/modules/hosts/opencode/commands/wf-review.md +1 -1
  9. package/modules/hosts/trae/commands/wf-new-task.md +1 -0
  10. package/modules/hosts/trae/commands/wf-review.md +1 -1
  11. package/package.json +1 -1
  12. package/templates/_agents/commands/new-task.md +1 -0
  13. package/templates/_agents/commands/review.md +1 -1
  14. package/templates/_agents/engine-lock.json +28 -20
  15. package/templates/_agents/hooks/commit-check.cjs +57 -30
  16. package/templates/_agents/notes/engine-internals.md +17 -0
  17. package/templates/_agents/rule-budgets.txt +7 -1
  18. package/templates/_agents/scripts/agg-judgments.mjs +77 -1
  19. package/templates/_agents/scripts/agg-judgments.test.mjs +49 -1
  20. package/templates/_agents/scripts/archive-sweep.mjs +3 -1
  21. package/templates/_agents/scripts/check-convergence.mjs +8 -3
  22. package/templates/_agents/scripts/check-lane-surface.mjs +9 -2
  23. package/templates/_agents/scripts/check-lane-surface.test.mjs +16 -2
  24. package/templates/_agents/scripts/check-ledger-invariant.mjs +30 -4
  25. package/templates/_agents/scripts/check-ledger-invariant.test.mjs +28 -0
  26. package/templates/_agents/scripts/check-loop-rev.test.mjs +8 -14
  27. package/templates/_agents/scripts/check-loop.mjs +9 -0
  28. package/templates/_agents/scripts/check-loop.test.mjs +17 -5
  29. package/templates/_agents/scripts/check-prejudgment.mjs +8 -3
  30. package/templates/_agents/scripts/closeout-l1.mjs +217 -0
  31. package/templates/_agents/scripts/closeout-l1.test.mjs +105 -0
  32. package/templates/_agents/scripts/commit-check-trigger.test.mjs +49 -0
  33. package/templates/_agents/scripts/confirm-doc.mjs +14 -2
  34. package/templates/_agents/scripts/confirm-doc.test.mjs +43 -0
  35. package/templates/_agents/scripts/gen-workflow-metrics.mjs +17 -0
  36. package/templates/_agents/scripts/ledger-shards.mjs +42 -5
  37. package/templates/_agents/scripts/ledger-shards.test.mjs +17 -0
  38. package/templates/_agents/scripts/mechanism-retire-watch.mjs +183 -0
  39. package/templates/_agents/scripts/mechanism-retire-watch.test.mjs +114 -0
  40. package/templates/_agents/scripts/pre-push.test.mjs +26 -10
  41. package/templates/_agents/scripts/rule-budget.mjs +14 -1
  42. package/templates/_agents/scripts/rule-budget.test.mjs +118 -0
  43. package/templates/_agents/scripts/verify.mjs +32 -8
  44. package/templates/_agents/scripts/verify.test.mjs +29 -0
  45. package/templates/_agents/scripts-test-exempt.txt +2 -1
  46. package/templates/_agents/skills/review-method/SKILL.md +55 -0
  47. package/templates/_githooks/pre-commit +9 -4
  48. package/templates/_githooks/pre-merge-commit +4 -3
  49. package/templates/_githooks/pre-push +129 -75
@@ -4,7 +4,8 @@
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
+ # 2026-10-16 减重手段② 随附:rule-budget.mjs 登记撤除(补同名直测黑盒套件 T1-T8——字节语义不回归 +
8
+ # #count 条目数指令全形态;src/rule-budget.test.mjs 仍是包源预算自检门,职责不同并行保留)。
8
9
  ensure-board.mjs | 看板启动器 | 永久工具豁免(需真实端口/进程环境,自动化覆盖无性价比——2026-10-09 engine-quality-round2 A2 评估定性)
9
10
  policy.mjs | 库件 | 被 check-loop.test.mjs 直接 import 覆盖
10
11
  # 2026-10-12 needs-clarification:stage-gates.mjs 已建同名直测(stage-gates.test.mjs 15 断言),
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: review-method
3
+ description: 代码复核走查方法(Standards×Spec 双轴 + 坏味道清单 + 合入标尺)。供 independent-reviewer 或主智能体复核 diff 时使用;是 .agents/commands/review.md 执行流程的方法层单源,不依赖宿主全局技能。
4
+ ---
5
+
6
+ # review-method — 复核走查方法
7
+
8
+ ## 定位与分工
9
+ 复核三层各自单源,本文件只管「怎么走查」:
10
+
11
+ | 层 | 单源 |
12
+ |---|---|
13
+ | 拦什么(不变量) | `.agents/commands/review.md` P0/P1 表 |
14
+ | 以什么身份查(证据纪律) | `.agents/roles/independent-reviewer.md` Constraints |
15
+ | 怎么走查(本文件) | 双轴框架 + 坏味道清单 + 合入标尺 |
16
+
17
+ 宿主有全局 `code-review` / `review-verification-protocol` 技能时可叠加;无则本文件兜底。
18
+
19
+ ## 双轴框架(分轴报告,禁合并排序)
20
+ | 轴 | 问的问题 | 依据 |
21
+ |---|---|---|
22
+ | **Standards** | 是否符合本仓成文标准 | 根/目录级 `AGENTS.md` + review.md P0/P1 表 |
23
+ | **Spec** | 是否忠实实现入口文档承诺 | intent/plan「验收标准」+ spec §测试用例 |
24
+
25
+ 两轴独立成立:合规范但做错事(Standards 过、Spec 挂)与做对事但破规范(Spec 过、Standards 挂)都存在——分开报告防一轴掩盖另一轴。Spec 轴报三类发现:承诺缺失/部分落地、超范围(scope creep)、实现走样。
26
+
27
+ ## 坏味道走查清单(Standards 轴补充)
28
+ P0/P1 表之外的判断项。约束:仓库成文标准优先(成文认可即压制本清单);始终是判断项非硬违规;门禁/tooling 已拦的跳过。每条 = 是什么 $\rightarrow$ 怎么修:
29
+
30
+ | 坏味道 | 是什么 $\rightarrow$ 怎么修 |
31
+ |---|---|
32
+ | Mysterious Name 神秘命名 | 名字不揭示用途 $\rightarrow$ 重命名;起不出诚实名字 = 设计混浊 |
33
+ | Duplicated Code 重复代码 | 同形逻辑在多处出现 $\rightarrow$ 提取共享实现,两处调用 |
34
+ | Feature Envy 依恋情结 | 方法够别家数据多过自己 $\rightarrow$ 把方法移到所依恋的数据上 |
35
+ | Data Clumps 数据泥团 | 几个字段/参数总是结伴出行 $\rightarrow$ 捆成一个类型传递 |
36
+ | Primitive Obsession 基本类型偏执 | 原始值顶替领域概念 $\rightarrow$ 为概念建专属小类型 |
37
+ | Repeated Switches 重复开关 | 对同一类型的 switch/级联 if 反复出现 $\rightarrow$ 多态或共享映射 |
38
+ | Shotgun Surgery 霰弹式修改 | 一个逻辑变更迫使多文件分散编辑 $\rightarrow$ 聚拢到同一模块 |
39
+ | Divergent Change 发散式变化 | 一个模块因多个不相关原因被改 $\rightarrow$ 按单一变化理由拆分 |
40
+ | Speculative Generality 投机泛化 | 为 spec 没有的需求加抽象/参数/钩子 $\rightarrow$ 删掉内联,等真实需求出现 |
41
+ | Message Chains 消息链 | 长 `a.b().c().d()` 导航 $\rightarrow$ 在首对象上藏一个方法 |
42
+ | Middle Man 中间人 | 类/函数几乎只做转发 $\rightarrow$ 删掉,直调真目标 |
43
+ | Refused Bequest 拒绝继承 | 子类忽略/覆盖大部分继承物 $\rightarrow$ 弃继承改组合 |
44
+
45
+ 性能三项(P1 分页条款之外):N+1 查询、无界取数/循环、热路径大对象。
46
+
47
+ ## 结构化修复动作
48
+ 报结构问题时给命名动作,禁只说「这里复杂」让作者猜:消条件链建类型/分发器、折叠重复分支、编排与业务分离、特性逻辑移回所属层、复用权威 helper 禁近亲复制、显式类型边界消下游分支、删无澄清价值的透传包装、抽 helper 或拆大文件。
49
+
50
+ ## 合入标尺(防 P2 纠缠)
51
+ 确定改善整体代码健康即建议合入,不因「不是我会这么写」而拦——完美不存在,持续改进是目标。样式偏好与假设性风险禁报(证据纪律见角色契约 Constraints)。
52
+
53
+ ## 出处
54
+ - 双轴框架与坏味道清单:提炼自宿主全局 `code-review` 技能(Fowler《Refactoring》ch.3 基线)。
55
+ - 结构化修复动作与合入标尺:借自 [addyosmani/agent-skills](https://github.com/addyosmani/agent-skills)(MIT)。
@@ -3,7 +3,7 @@
3
3
  // 2026-10-13 门禁去 sh 化(B2,自 Shipyard.Material 参考实现回灌):钩子体不再含 sh 逻辑——git 仍以自带
4
4
  // sh 启动本文件(钩子机制要求),但 shebang 即 node,PATH 无 sh 不再影响任何逻辑;语言单化后 CI 代理/
5
5
  // 本地行为一致。写法为 CommonJS——无扩展名文件 node 缺省按 CJS 解析,不依赖 ≥22.7 的 ESM 语法探测。
6
- // 通用门禁序:闭环配对(增量) → 泳道完整性(增量) → wiki 台账(增量) → 索引漂移(增量) → 常驻面体积预算(增量) → 确认台账不变量(暂存触台账) → 项目门禁(local-pre-commit) → 敏感信息扫描+条件构建(commit-check.cjs)
6
+ // 通用门禁序:闭环配对(增量) → 泳道完整性(增量) → wiki 台账(增量) → 索引漂移(增量) → 常驻面体积预算(增量) → 确认台账不变量(暂存触台账) → 项目门禁(local-pre-commit) → 敏感信息扫描+条件构建(commit-check.cjs;2026-10-16 起常规提交 --no-build——条件构建后移 pre-push 按推送范围执法,合并提交仍 --full 全量构建)
7
7
  // 项目专属门禁(架构红线/业务守卫等)放 .agents/hooks/local-pre-commit——sh 挂载点(add-gate 生态按行接线),
8
8
  // 存在即在本钩子内以 sh(缺则 bash)执行,返回非零即阻断提交
9
9
  // 合并提交场景由 .githooks/pre-merge-commit 原样转发到本脚本(合并时 git 调的是 pre-merge-commit,
@@ -58,7 +58,10 @@ if (branchName.startsWith('experiment/')) {
58
58
  // 检查 B「探索标记缺失」——experiment/* 分支暂存 intent 缺「阶段: exploring」→ 拦(标记零成本,
59
59
  // advisory 免纪律不免标记——标记是加固门覆盖率的前提)。
60
60
  // 脚本缺失 fail-closed 阻断(见 gate())。
61
- gate('泳道完整性(增量)', '.agents/scripts/check-lane-surface.mjs');
61
+ // `git merge` 自动提交由 pre-merge-commit 传入 `--merge`(此时 MERGE_HEAD 已不可靠,不能靠探测);
62
+ // 冲突解决后的手动 `git commit` 仍由脚本自己看 MERGE_HEAD(2026-10-16 自源仓回灌)。
63
+ const mergeCommit = process.argv.includes('--merge');
64
+ gate('泳道完整性(增量)', '.agents/scripts/check-lane-surface.mjs', mergeCommit ? ['--merge'] : []);
62
65
 
63
66
  // wiki 台账门禁(增量,check-wiki-ledger.mjs:暂存区触及 wiki/ 时跑 verify-wiki-consistency 三方一致性;
64
67
  // 无 wiki/ 变更即跳过。脚本缺失 fail-closed)
@@ -105,12 +108,14 @@ if (fs.existsSync('.agents/hooks/local-pre-commit')) {
105
108
  }
106
109
 
107
110
  // 合并提交 → 构建验证覆盖【完整合并树】,不做按扩展名的条件裁剪。
111
+ // 常规提交 --no-build(2026-10-16 构建门后移 pre-push,自源仓回灌):提交内环只跑敏感扫描,条件构建在
112
+ // 推送时按 old..new 范围执法(.githooks/pre-push);合并结果是没人验证过的新状态,仍全量构建。
108
113
  // 两条路径:`git merge` 自动提交由 .githooks/pre-merge-commit 显式传 `--full` 转发到本脚本;
109
114
  // 冲突解决后手动 `git commit` 由 git 直接调本脚本,此处按 MERGE_HEAD 探测补上(此时合并仍在进行)。
110
- let fullArgs = process.argv.slice(2);
115
+ let fullArgs = process.argv.slice(2).filter((a) => a !== '--merge');
111
116
  if (fullArgs.length === 0) {
112
117
  const mergeHead = spawnSync('git', ['rev-parse', '-q', '--verify', 'MERGE_HEAD'], { encoding: 'utf8' });
113
- if (mergeHead.status === 0) fullArgs = ['--full'];
118
+ fullArgs = mergeHead.status === 0 ? ['--full'] : ['--no-build'];
114
119
  }
115
120
 
116
121
  const cc = spawnSync(process.execPath, ['.agents/hooks/commit-check.cjs', ...fullArgs], { stdio: 'inherit' });
@@ -8,10 +8,11 @@
8
8
  // 靠人工补跑静态门 + 全量测试才覆盖到)。
9
9
  //
10
10
  // 做法:转发到 .githooks/pre-commit(门禁清单只此一份,避免两份拷贝日后分叉),
11
- // 并显式传 `--full`——本钩子「只对 merge 触发」这一身份就是最强信号,无需再去探测
11
+ // 并显式传 `--full` 与 `--merge`——本钩子「只对 merge 触发」这一身份就是最强信号,无需再去探测
12
12
  // MERGE_HEAD(2026-09-19 实测:本时点 MERGE_HEAD 已不可靠)。`--full` 令编译验证
13
13
  // 覆盖【完整合并树】、不按扩展名的条件裁剪:合并结果是没人验证过的新状态,且其 diff
14
- // 相对第一父,裁剪会漏掉需要重跑的一侧。
14
+ // 相对第一父,裁剪会漏掉需要重跑的一侧。`--merge` 令泳道检查 A 跳过(2026-10-16 自源仓回灌:
15
+ // 同窗口 MERGE_HEAD 已不在,已关单产品路径会被误判「触达面判低」)。
15
16
  //
16
17
  // 冲突解决后手动 `git commit` 走的是 pre-commit 自身,那条路径由 pre-commit 内部的
17
18
  // MERGE_HEAD 探测覆盖(此时合并仍在进行,MERGE_HEAD 确定存在)。
@@ -24,5 +25,5 @@ if (top.status !== 0 || !top.stdout.trim()) {
24
25
  process.exit(1);
25
26
  }
26
27
  const hook = path.join(top.stdout.trim(), '.githooks', 'pre-commit');
27
- const r = spawnSync(process.execPath, [hook, '--full'], { stdio: 'inherit' });
28
+ const r = spawnSync(process.execPath, [hook, '--full', '--merge'], { stdio: 'inherit' });
28
29
  process.exit(r.status === null ? 1 : r.status);
@@ -1,82 +1,136 @@
1
- #!/bin/sh
2
- # Git 原生 pre-push hook(经 core.hooksPath 挂载,git push 时触发)
3
- # 跑 .agents/scripts/check-loop.sh:扫 workflow/ 闭环骨架断档(2026-10-13 分道——默认只扫硬门,源仓同款)
4
- # - 硬阻断(exit 1)= 配对断裂 / 回路断档 等硬门违例 → push 立即阻断
5
- # - 卫生项(索引/宿主/词表/体积预算等 WARN 面)不进 push 门——要看时手跑 check-loop.mjs --hygiene
6
- # (--all 两面都跑 = 分道前的裸跑全量)
7
- # stdin 有行时,每个非删除的本地 sha 用 --rev 扫那棵树。没有行时(手跑钩子)仍无参数扫工作区。
8
- #
9
- # experiment/* 泳道(2026-09-30 hybrid-governance-explore-hardening):**目标分支**为 experiment/* 时,
10
- # 闭环扫描降级 advisory(报告不阻断)——快速 PoC 推送不因闭环断档被拦;转正(目标为 main)由加固门兜底。
11
- # 加固门(同日;2026-10-01 v09-review-defects 修正判定键):推送**目标**为 refs/heads/main 时以
12
- # --hardening 运行(无论来源分支——experiment/* 直推 main 同样覆盖),exploring 任务须先收口
13
- # (intent approved/done + 同名 plan 在场 + L2/L3 另须同名 spec;判据见 check-loop.mjs 加固门段),
14
- # hard 失败即阻断 push,防「未文档化的探索件」进 main。
15
- #
16
- # 机器门:本地钩子跑 check-loop.sh。装户仓的远端门是随包下发的 .github/workflows/kit-ci.yml(verify.mjs)。
17
- # kit 源仓:.github/workflows/ci.yml 于 push main / 全部 PR 跑 npm test(lint + 全量套件)。
18
- # 装户仓无包源 templates/:sync-hosts 等包源命令不适用。本地钩子仍是提交时刻的机器门。
19
- # 不要用 `git push --no-verify` 绕开——发现 hard-block 应先修 workflow 文档再推。
1
+ #!/usr/bin/env node
2
+ // Git 原生 pre-push hook(经 core.hooksPath 挂载,git push 时触发)— agentic-flow-kit 通用编排
3
+ // 2026-10-16 node 化(自源仓 2ff869a9/13f67ef7 回灌 pre-push 面):钩子体不再含 sh 逻辑——git 仍以自带
4
+ // sh 启动本文件(钩子机制要求),但 shebang 即 node,PATH 无 sh 不影响任何逻辑。写法为 CommonJS——
5
+ // 无扩展名文件 node 缺省按 CJS 解析,不依赖 ≥22.7 的 ESM 语法探测。
6
+ // 跑 .agents/scripts/check-loop.mjs:扫 workflow/ 闭环骨架断档(默认只扫硬门)
7
+ // - 硬阻断(exit 1)= 配对断裂 / 回路断档 / 证据与确认链 / 发版未收口 → push 立即阻断
8
+ // - 卫生段(模板占位 / 引用漂移 / 宿主副本 / 索引)不在本钩子。要看时跑 --hygiene 或 --all
9
+ // stdin 有行时,每个非删除的本地 sha 用 --rev 扫那棵树。没有行时(手跑钩子)仍无参数扫工作区。
10
+ //
11
+ // 构建门(2026-10-16 自 pre-commit 后移):另跑 commit-check.cjs --range <old> <new> 按推送范围条件构建
12
+ // (常规提交时刻只做敏感扫描——提交内环提速;构建执法集中到推送与关单)。范围匹配逻辑单源 =
13
+ // commit-check.cjs。experiment/* 目标降级 advisory。手动运行(无 stdin 推送行)无范围事实,构建门跳过。
14
+ //
15
+ // experiment/* 泳道:**目标分支**为 experiment/* 时,闭环扫描与构建门降级 advisory(报告不阻断)——
16
+ // 快速 PoC 推送不因闭环断档/构建失败被拦;转正(目标为 main)由加固门兜底。
17
+ // 加固门(2026-10-01 v09-review-defects):推送**目标**为 refs/heads/main 时以 --hardening 运行
18
+ // (无论来源分支——experiment/* 直推 main 同样覆盖),exploring 任务须先收口(intent approved/done +
19
+ // 同名 plan 在场 + L2/L3 另须同名 spec;判据见 check-loop.mjs 加固门段),hard 失败即阻断 push。
20
+ //
21
+ // 回路日检(2026-10-16 自源仓 13f67ef7 回灌):退役催办与 papercut 报债此前无调度,机制空转。每个
22
+ // 本地日在推送成功路径跑一次,advisory,失败不改退出码。摘要落 .agents/cache/loop-watch.jsonl(gitignore)。
23
+ //
24
+ // 机器门:本地钩子跑 check-loop.mjs。装户仓的远端门是随包下发的 .github/workflows/kit-ci.yml(verify.mjs)。
25
+ // kit 源仓:.github/workflows/ci.yml 于 push main / 全部 PR 跑 npm test(lint + 全量套件)。
26
+ // 不要用 `git push --no-verify` 绕开——发现 hard-block 应先修 workflow 文档再推。
27
+ const { spawnSync } = require('node:child_process');
28
+ const fs = require('node:fs');
20
29
 
21
- cd "$(git rev-parse --show-toplevel)" || exit 1
30
+ const top = spawnSync('git', ['rev-parse', '--show-toplevel'], { encoding: 'utf8' });
31
+ if (top.status !== 0 || !top.stdout.trim()) {
32
+ console.error('pre-push: 无法定位仓库根——推送已阻止');
33
+ process.exit(1);
34
+ }
35
+ process.chdir(top.stdout.trim());
36
+
37
+ const LOOP = '.agents/scripts/check-loop.mjs';
38
+ if (!fs.existsSync(LOOP)) {
39
+ console.error(`pre-push: 未找到 ${LOOP}——闭环扫描无法执行,推送已阻止(脚本随包下发;flow-kit sync 恢复后重试)`);
40
+ process.exit(1);
41
+ }
42
+ // 构建门脚本(2026-10-16 自 pre-commit 后移):缺失 fail-closed
43
+ const BUILD_CHECK = '.agents/hooks/commit-check.cjs';
44
+ if (!fs.existsSync(BUILD_CHECK)) {
45
+ console.error(`pre-push: 未找到 ${BUILD_CHECK}——构建门无法执行,推送已阻止(脚本随包下发;git checkout -- ${BUILD_CHECK} 恢复后重试)`);
46
+ process.exit(1);
47
+ }
22
48
 
23
- SCRIPT=".agents/scripts/check-loop.sh"
24
- if [ ! -f "$SCRIPT" ]; then
25
- echo "pre-push: 未找到 $SCRIPT,跳过闭环扫描" >&2
26
- exit 0
27
- fi
49
+ const runLoop = (args = []) => spawnSync(process.execPath, [LOOP, ...args], { stdio: 'inherit' });
50
+ // 范围参数:远端全零 sha(新分支首推)→ 全量构建语义(commit-check 认全零 old)
51
+ const rangeArgsOf = (remoteSha, localSha) => ['--range', remoteSha && !/^0+$/.test(remoteSha) ? remoteSha : '0'.repeat(40), localSha];
28
52
 
29
- run_loop() {
30
- if command -v sh >/dev/null 2>&1; then
31
- sh "$SCRIPT" "$@"
32
- else
33
- bash "$SCRIPT" "$@"
34
- fi
53
+ let stdinText = '';
54
+ try { stdinText = fs.readFileSync(0, 'utf8'); } catch { stdinText = ''; }
55
+
56
+ let saw = 0;
57
+ let rc = 0;
58
+ for (const line of stdinText.split(/\r?\n/)) {
59
+ if (!line.trim()) continue;
60
+ const parts = line.trim().split(/\s+/);
61
+ if (parts.length < 4) continue;
62
+ const [localRef, localSha, remoteRef, remoteSha] = parts;
63
+ saw = 1;
64
+ if (/^0+$/.test(localSha)) continue; // 删除推送不扫
65
+ // 按目标分支分派(判定键 = remote_ref):
66
+ // main → 加固门(必带 --hardening);experiment/* → advisory(报告不阻断);其他 → 全量扫描(硬门)
67
+ if (/^refs\/heads\/main$|^main$/.test(remoteRef)) {
68
+ const r = runLoop(['--rev', localSha, '--hardening']);
69
+ if (r.status !== 0 && rc === 0) rc = r.status === null ? 1 : r.status;
70
+ const rb = spawnSync(process.execPath, [BUILD_CHECK, ...rangeArgsOf(remoteSha, localSha)], { stdio: 'inherit' });
71
+ if (rb.status !== 0 && rc === 0) rc = rb.status === null ? 1 : rb.status;
72
+ } else if (/^refs\/heads\/experiment\/|^experiment\//.test(remoteRef)) {
73
+ console.error(`pre-push: experiment/* 目标分支(${remoteRef})——闭环扫描与构建门降级 advisory,不阻断`);
74
+ const adv = spawnSync(process.execPath, [LOOP, '--rev', localSha], { stdio: 'inherit' });
75
+ if (adv.status !== 0) console.error('pre-push: (advisory)闭环断档已在上方列出——experiment 泳道可推送,转正(推 main)须过加固门');
76
+ const advB = spawnSync(process.execPath, [BUILD_CHECK, ...rangeArgsOf(remoteSha, localSha)], { stdio: 'inherit' });
77
+ if (advB.status !== 0) console.error('pre-push: (advisory)构建门未过已在上方列出——experiment 泳道不阻断推送');
78
+ } else {
79
+ const r = runLoop(['--rev', localSha]);
80
+ if (r.status !== 0 && rc === 0) rc = r.status === null ? 1 : r.status;
81
+ const rb = spawnSync(process.execPath, [BUILD_CHECK, ...rangeArgsOf(remoteSha, localSha)], { stdio: 'inherit' });
82
+ if (rb.status !== 0 && rc === 0) rc = rb.status === null ? 1 : rb.status;
83
+ }
35
84
  }
36
85
 
37
- saw=0
38
- rc=0
39
- while read -r local_ref local_sha remote_ref _remote_sha; do
40
- saw=1
41
- case "$local_sha" in
42
- *[!0]*) ;;
43
- *) continue ;;
44
- esac
45
- # 按目标分支三态分派(2026-10-01 v09-review-defects:判定键从 local_ref 改为 remote_ref——
46
- # 修复 experiment/*→main 直推被 advisory continue 吞掉加固门的漏口):
47
- # main → 加固门(必带 --hardening)
48
- # experiment/* → advisory(报告不阻断)
49
- # 其他 → 全量扫描
50
- case "$remote_ref" in
51
- refs/heads/main|main)
52
- run_loop --rev "$local_sha" --hardening
53
- ;;
54
- refs/heads/experiment/*|experiment/*)
55
- echo "pre-push: experiment/* 目标分支($remote_ref)——闭环扫描降级 advisory,不阻断" >&2
56
- if command -v sh >/dev/null 2>&1; then
57
- sh "$SCRIPT" --rev "$local_sha" >&2 || echo "pre-push: (advisory)闭环断档已在上方列出——experiment 泳道可推送,转正(推 main)须过加固门" >&2
58
- else
59
- bash "$SCRIPT" --rev "$local_sha" >&2 || echo "pre-push: (advisory)闭环断档已在上方列出——experiment 泳道可推送,转正(推 main)须过加固门" >&2
60
- fi
61
- ;;
62
- *)
63
- run_loop --rev "$local_sha"
64
- ;;
65
- esac
66
- one=$?
67
- if [ "$one" -ne 0 ] && [ "$rc" -eq 0 ]; then
68
- rc=$one
69
- fi
70
- done
86
+ if (saw === 0) {
87
+ const r = runLoop();
88
+ if (r.status !== 0) rc = r.status === null ? 1 : r.status;
89
+ console.error('pre-push: 手动运行无推送范围——构建门跳过(真实推送时按 old..new 范围执法)');
90
+ }
71
91
 
72
- if [ "$saw" -eq 0 ]; then
73
- run_loop
74
- rc=$?
75
- fi
92
+ if (rc !== 0) {
93
+ console.error('pre-push: 闭环骨架断档 / 构建未过(hard-block)未通过,push 已阻断。修 workflow 文档 / 构建错误后重推——不要用 --no-verify 绕过(本钩子是唯一机器门,无服务端兜底,见头部说明)');
94
+ console.error('pre-push: 若为「加固未过」= experiment 泳道件转正前须收口:intent approved/done + 同名 plan(L2/L3 另须 spec)且状态非 draft');
95
+ process.exit(rc);
96
+ }
76
97
 
77
- if [ "$rc" -ne 0 ]; then
78
- echo "pre-push: 闭环骨架断档(hard-block)未通过,push 已阻断。修 workflow 文档后重推——不要用 --no-verify 绕过(本钩子是唯一机器门,无服务端兜底,见头部说明)" >&2
79
- echo "pre-push: 若为「加固未过」= experiment 泳道件转正前须收口:intent approved/done + 同名 plan(L2/L3 另须 spec)且状态非 draft" >&2
80
- exit "$rc"
81
- fi
82
- exit 0
98
+ // 回路日检(2026-10-16 自源仓 13f67ef7 回灌):每个本地日在推送成功路径跑一次,advisory,失败不改退出码。
99
+ try {
100
+ const cacheDir = '.agents/cache';
101
+ const stampPath = `${cacheDir}/loop-watch-stamp`;
102
+ const ledgerPath = `${cacheDir}/loop-watch.jsonl`;
103
+ const now = new Date();
104
+ const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
105
+ let prev = '';
106
+ try { prev = fs.readFileSync(stampPath, 'utf8').trim(); } catch { /* 首跑 */ }
107
+ if (prev !== today) {
108
+ fs.mkdirSync(cacheDir, { recursive: true });
109
+ // 退役催办:mechanism-retire-watch(--json 产 actions);papercut 报债:kit 复用 sensor --json 取 debt
110
+ const jobs = [
111
+ ['retire-watch', '.agents/scripts/mechanism-retire-watch.mjs'],
112
+ ['papercut-report', '.agents/scripts/papercut-debt-sensor.mjs'],
113
+ ];
114
+ const runs = [];
115
+ for (const [id, script] of jobs) {
116
+ if (!fs.existsSync(script)) { runs.push({ id, missing: true }); continue; }
117
+ const r = spawnSync(process.execPath, [script, '--json'], { encoding: 'utf8', maxBuffer: 8 * 1024 * 1024 });
118
+ let summary = {};
119
+ try { summary = JSON.parse(r.stdout || '{}'); } catch { summary = { parseError: true }; }
120
+ runs.push({
121
+ id,
122
+ exit: r.status,
123
+ actions: Array.isArray(summary.actions) ? summary.actions.length : null,
124
+ debt: typeof summary.debt === 'number' ? summary.debt : null,
125
+ });
126
+ }
127
+ fs.appendFileSync(ledgerPath, `${JSON.stringify({ ts: now.toISOString(), day: today, runs })}\n`);
128
+ fs.writeFileSync(stampPath, `${today}\n`);
129
+ const retire = runs.find((x) => x.id === 'retire-watch');
130
+ const paper = runs.find((x) => x.id === 'papercut-report');
131
+ console.error(`pre-push: (advisory)回路日检已记 ${ledgerPath}(退役动作 ${retire && retire.actions != null ? retire.actions : '?'},papercut debt ${paper && paper.debt != null ? paper.debt : '?'})`);
132
+ }
133
+ } catch (e) {
134
+ console.error(`pre-push: (advisory)回路日检跳过——${e.message}`);
135
+ }
136
+ process.exit(0);