@dommaker/harness 0.16.9 → 0.17.1

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 (337) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +33 -32
  3. package/bin/harness.js +33 -20
  4. package/dist/cli/commands/check.d.ts +3 -2
  5. package/dist/cli/commands/check.d.ts.map +1 -1
  6. package/dist/cli/commands/check.js +69 -36
  7. package/dist/cli/commands/check.js.map +1 -1
  8. package/dist/cli/commands/constraints-report.d.ts +42 -0
  9. package/dist/cli/commands/constraints-report.d.ts.map +1 -0
  10. package/dist/cli/commands/constraints-report.js +233 -0
  11. package/dist/cli/commands/constraints-report.js.map +1 -0
  12. package/dist/cli/commands/constraints-retire.d.ts +71 -0
  13. package/dist/cli/commands/constraints-retire.d.ts.map +1 -0
  14. package/dist/cli/commands/constraints-retire.js +384 -0
  15. package/dist/cli/commands/constraints-retire.js.map +1 -0
  16. package/dist/cli/commands/constraints.d.ts +1 -1
  17. package/dist/cli/commands/constraints.d.ts.map +1 -1
  18. package/dist/cli/commands/constraints.js +4 -4
  19. package/dist/cli/commands/constraints.js.map +1 -1
  20. package/dist/cli/commands/index.d.ts +2 -1
  21. package/dist/cli/commands/index.d.ts.map +1 -1
  22. package/dist/cli/commands/index.js +10 -4
  23. package/dist/cli/commands/index.js.map +1 -1
  24. package/dist/cli/commands/init.d.ts +20 -0
  25. package/dist/cli/commands/init.d.ts.map +1 -1
  26. package/dist/cli/commands/init.js +81 -59
  27. package/dist/cli/commands/init.js.map +1 -1
  28. package/dist/cli/commands/report.js +4 -4
  29. package/dist/cli/commands/report.js.map +1 -1
  30. package/dist/cli/commands/status.d.ts.map +1 -1
  31. package/dist/cli/commands/status.js +3 -4
  32. package/dist/cli/commands/status.js.map +1 -1
  33. package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts +5 -1
  34. package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts.map +1 -1
  35. package/dist/cli/commands/sync-docs/capabilities-syncer.js +2 -10
  36. package/dist/cli/commands/sync-docs/capabilities-syncer.js.map +1 -1
  37. package/dist/cli/commands/validate.d.ts.map +1 -1
  38. package/dist/cli/commands/validate.js +1 -5
  39. package/dist/cli/commands/validate.js.map +1 -1
  40. package/dist/core/constraints/capabilities-parser.d.ts +7 -0
  41. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  42. package/dist/core/constraints/capabilities-parser.js +13 -0
  43. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  44. package/dist/core/constraints/checker.d.ts +13 -5
  45. package/dist/core/constraints/checker.d.ts.map +1 -1
  46. package/dist/core/constraints/checker.js +62 -25
  47. package/dist/core/constraints/checker.js.map +1 -1
  48. package/dist/core/constraints/checkers/capability-sync.d.ts +9 -2
  49. package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
  50. package/dist/core/constraints/checkers/capability-sync.js +55 -22
  51. package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
  52. package/dist/core/constraints/checkers/context-doc-sync.d.ts +3 -0
  53. package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
  54. package/dist/core/constraints/checkers/context-doc-sync.js +8 -2
  55. package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
  56. package/dist/core/constraints/checkers/docs-freshness.d.ts +1 -0
  57. package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
  58. package/dist/core/constraints/checkers/docs-freshness.js +39 -0
  59. package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
  60. package/dist/core/constraints/checkers/index.d.ts +8 -4
  61. package/dist/core/constraints/checkers/index.d.ts.map +1 -1
  62. package/dist/core/constraints/checkers/index.js +29 -46
  63. package/dist/core/constraints/checkers/index.js.map +1 -1
  64. package/dist/core/constraints/checkers/iron-flags.d.ts +6 -7
  65. package/dist/core/constraints/checkers/iron-flags.d.ts.map +1 -1
  66. package/dist/core/constraints/checkers/iron-flags.js +10 -17
  67. package/dist/core/constraints/checkers/iron-flags.js.map +1 -1
  68. package/dist/core/constraints/checkers/no-hardcoded-credentials.d.ts +13 -0
  69. package/dist/core/constraints/checkers/no-hardcoded-credentials.d.ts.map +1 -0
  70. package/dist/core/constraints/checkers/no-hardcoded-credentials.js +91 -0
  71. package/dist/core/constraints/checkers/no-hardcoded-credentials.js.map +1 -0
  72. package/dist/core/constraints/checkers/types.d.ts +15 -4
  73. package/dist/core/constraints/checkers/types.d.ts.map +1 -1
  74. package/dist/core/constraints/checkers/types.js +16 -4
  75. package/dist/core/constraints/checkers/types.js.map +1 -1
  76. package/dist/core/constraints/context-builder.d.ts +6 -3
  77. package/dist/core/constraints/context-builder.d.ts.map +1 -1
  78. package/dist/core/constraints/context-builder.js +29 -20
  79. package/dist/core/constraints/context-builder.js.map +1 -1
  80. package/dist/core/constraints/definitions/guidelines.d.ts +6 -2
  81. package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
  82. package/dist/core/constraints/definitions/guidelines.js +29 -572
  83. package/dist/core/constraints/definitions/guidelines.js.map +1 -1
  84. package/dist/core/constraints/definitions/iron-laws.d.ts +6 -2
  85. package/dist/core/constraints/definitions/iron-laws.d.ts.map +1 -1
  86. package/dist/core/constraints/definitions/iron-laws.js +48 -231
  87. package/dist/core/constraints/definitions/iron-laws.js.map +1 -1
  88. package/dist/core/constraints/definitions/prompts.d.ts +12 -0
  89. package/dist/core/constraints/definitions/prompts.d.ts.map +1 -0
  90. package/dist/core/constraints/definitions/prompts.js +405 -0
  91. package/dist/core/constraints/definitions/prompts.js.map +1 -0
  92. package/dist/core/constraints/definitions.d.ts +15 -9
  93. package/dist/core/constraints/definitions.d.ts.map +1 -1
  94. package/dist/core/constraints/definitions.js +18 -12
  95. package/dist/core/constraints/definitions.js.map +1 -1
  96. package/dist/core/constraints/index.d.ts +3 -2
  97. package/dist/core/constraints/index.d.ts.map +1 -1
  98. package/dist/core/constraints/index.js +8 -2
  99. package/dist/core/constraints/index.js.map +1 -1
  100. package/dist/core/constraints/injection-drift.d.ts +48 -0
  101. package/dist/core/constraints/injection-drift.d.ts.map +1 -0
  102. package/dist/core/constraints/injection-drift.js +140 -0
  103. package/dist/core/constraints/injection-drift.js.map +1 -0
  104. package/dist/core/constraints/injection-renderer.d.ts +23 -0
  105. package/dist/core/constraints/injection-renderer.d.ts.map +1 -0
  106. package/dist/core/constraints/injection-renderer.js +44 -0
  107. package/dist/core/constraints/injection-renderer.js.map +1 -0
  108. package/dist/core/constraints/interceptor.d.ts.map +1 -1
  109. package/dist/core/constraints/interceptor.js +3 -2
  110. package/dist/core/constraints/interceptor.js.map +1 -1
  111. package/dist/core/constraints/usage-report.d.ts +95 -0
  112. package/dist/core/constraints/usage-report.d.ts.map +1 -0
  113. package/dist/core/constraints/usage-report.js +206 -0
  114. package/dist/core/constraints/usage-report.js.map +1 -0
  115. package/dist/core/effective-constraints.d.ts +47 -0
  116. package/dist/core/effective-constraints.d.ts.map +1 -0
  117. package/dist/core/effective-constraints.js +60 -0
  118. package/dist/core/effective-constraints.js.map +1 -0
  119. package/dist/core/index.d.ts +1 -0
  120. package/dist/core/index.d.ts.map +1 -1
  121. package/dist/core/index.js +2 -0
  122. package/dist/core/index.js.map +1 -1
  123. package/dist/core/project-config-loader.d.ts +17 -2
  124. package/dist/core/project-config-loader.d.ts.map +1 -1
  125. package/dist/core/project-config-loader.js +55 -8
  126. package/dist/core/project-config-loader.js.map +1 -1
  127. package/dist/dashboard/data.d.ts +1 -9
  128. package/dist/dashboard/data.d.ts.map +1 -1
  129. package/dist/dashboard/data.js +7 -15
  130. package/dist/dashboard/data.js.map +1 -1
  131. package/dist/dashboard/types.d.ts +13 -1
  132. package/dist/dashboard/types.d.ts.map +1 -1
  133. package/dist/index.d.ts +0 -4
  134. package/dist/index.d.ts.map +1 -1
  135. package/dist/index.js +1 -13
  136. package/dist/index.js.map +1 -1
  137. package/dist/monitoring/index.d.ts +0 -2
  138. package/dist/monitoring/index.d.ts.map +1 -1
  139. package/dist/monitoring/index.js +0 -2
  140. package/dist/monitoring/index.js.map +1 -1
  141. package/dist/monitoring/knowledge-evolver.d.ts +1 -1
  142. package/dist/monitoring/knowledge-evolver.js +1 -1
  143. package/dist/monitoring/trace-analyzer.d.ts +1 -1
  144. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  145. package/dist/monitoring/trace-analyzer.js +16 -10
  146. package/dist/monitoring/trace-analyzer.js.map +1 -1
  147. package/dist/monitoring/traces.d.ts +2 -1
  148. package/dist/monitoring/traces.d.ts.map +1 -1
  149. package/dist/monitoring/traces.js +5 -3
  150. package/dist/monitoring/traces.js.map +1 -1
  151. package/dist/presets/standard.d.ts +3 -1
  152. package/dist/presets/standard.d.ts.map +1 -1
  153. package/dist/presets/standard.js +18 -8
  154. package/dist/presets/standard.js.map +1 -1
  155. package/dist/types/constraint.d.ts +51 -11
  156. package/dist/types/constraint.d.ts.map +1 -1
  157. package/dist/types/constraint.js +8 -4
  158. package/dist/types/constraint.js.map +1 -1
  159. package/dist/types/index.d.ts +1 -1
  160. package/dist/types/index.d.ts.map +1 -1
  161. package/dist/types/index.js.map +1 -1
  162. package/dist/types/monitoring-types.d.ts +2 -63
  163. package/dist/types/monitoring-types.d.ts.map +1 -1
  164. package/dist/types/monitoring-types.js +2 -2
  165. package/dist/types/project-config.d.ts +26 -1
  166. package/dist/types/project-config.d.ts.map +1 -1
  167. package/dist/types/trace.d.ts +16 -7
  168. package/dist/types/trace.d.ts.map +1 -1
  169. package/dist/types/trace.js +8 -0
  170. package/dist/types/trace.js.map +1 -1
  171. package/package.json +1 -1
  172. package/src/CONTEXT.md +2 -2
  173. package/src/__tests__/checker-extra.test.ts +228 -333
  174. package/src/__tests__/checker.test.ts +87 -724
  175. package/src/__tests__/constraint-doctor.test.ts +5 -5
  176. package/src/__tests__/doctor-extra.test.ts +164 -0
  177. package/src/__tests__/effective-constraints.test.ts +147 -0
  178. package/src/__tests__/extend-exceptions.test.ts +3 -3
  179. package/src/__tests__/iron-laws.test.ts +115 -91
  180. package/src/__tests__/no-hardcoded-credentials.test.ts +90 -0
  181. package/src/__tests__/presets.test.ts +7 -7
  182. package/src/__tests__/project-config-loader-extra.test.ts +41 -4
  183. package/src/__tests__/skip-semantics.test.ts +322 -0
  184. package/src/cli/commands/CONTEXT.md +5 -3
  185. package/src/cli/commands/__tests__/check-drift.test.ts +127 -0
  186. package/src/cli/commands/__tests__/check-skip-output.test.ts +128 -0
  187. package/src/cli/commands/__tests__/check.test.ts +14 -25
  188. package/src/cli/commands/__tests__/constraints-report.test.ts +327 -0
  189. package/src/cli/commands/__tests__/constraints-retire.test.ts +283 -0
  190. package/src/cli/commands/__tests__/init-injection.test.ts +234 -0
  191. package/src/cli/commands/__tests__/status-extra.test.ts +11 -11
  192. package/src/cli/commands/__tests__/status.test.ts +1 -1
  193. package/src/cli/commands/check.ts +72 -38
  194. package/src/cli/commands/constraints-report.ts +234 -0
  195. package/src/cli/commands/constraints-retire.ts +443 -0
  196. package/src/cli/commands/constraints.ts +5 -5
  197. package/src/cli/commands/index.ts +2 -1
  198. package/src/cli/commands/init.ts +94 -66
  199. package/src/cli/commands/report.ts +6 -6
  200. package/src/cli/commands/status.ts +3 -4
  201. package/src/cli/commands/sync-docs/capabilities-syncer.ts +5 -7
  202. package/src/cli/commands/validate.ts +1 -5
  203. package/src/core/CONTEXT.md +7 -4
  204. package/src/core/constraints/__tests__/injection-drift.test.ts +164 -0
  205. package/src/core/constraints/__tests__/interceptor.test.ts +4 -0
  206. package/src/core/constraints/capabilities-parser.ts +13 -0
  207. package/src/core/constraints/checker.ts +73 -28
  208. package/src/core/constraints/checkers/capability-sync.ts +72 -29
  209. package/src/core/constraints/checkers/context-doc-sync.ts +9 -2
  210. package/src/core/constraints/checkers/docs-freshness.ts +43 -0
  211. package/src/core/constraints/checkers/index.ts +38 -71
  212. package/src/core/constraints/checkers/iron-flags.ts +13 -49
  213. package/src/core/constraints/checkers/no-hardcoded-credentials.ts +93 -0
  214. package/src/core/constraints/checkers/types.ts +27 -5
  215. package/src/core/constraints/context-builder.ts +28 -19
  216. package/src/core/constraints/definitions/guidelines.ts +29 -597
  217. package/src/core/constraints/definitions/iron-laws.ts +48 -239
  218. package/src/core/constraints/definitions/prompts.ts +419 -0
  219. package/src/core/constraints/definitions.ts +18 -11
  220. package/src/core/constraints/index.ts +10 -1
  221. package/src/core/constraints/injection-drift.ts +139 -0
  222. package/src/core/constraints/injection-renderer.ts +44 -0
  223. package/src/core/constraints/interceptor.ts +3 -2
  224. package/src/core/constraints/usage-report.ts +254 -0
  225. package/src/core/effective-constraints.ts +85 -0
  226. package/src/core/index.ts +4 -1
  227. package/src/core/project-config-loader.ts +59 -9
  228. package/src/dashboard/__tests__/data.test.ts +5 -12
  229. package/src/dashboard/data.ts +9 -19
  230. package/src/dashboard/types.ts +18 -1
  231. package/src/index.ts +0 -12
  232. package/src/knowledge/CONTEXT.md +2 -2
  233. package/src/monitoring/CONTEXT.md +3 -4
  234. package/src/monitoring/index.ts +0 -2
  235. package/src/monitoring/knowledge-evolver.ts +1 -1
  236. package/src/monitoring/trace-analyzer.ts +15 -10
  237. package/src/monitoring/traces.ts +10 -6
  238. package/src/presets/__tests__/standard.test.ts +11 -9
  239. package/src/presets/standard.ts +24 -15
  240. package/src/types/constraint.ts +59 -14
  241. package/src/types/index.ts +1 -3
  242. package/src/types/monitoring-types.ts +2 -84
  243. package/src/types/project-config.ts +30 -1
  244. package/src/types/trace.ts +18 -7
  245. package/src/verification/CONTEXT.md +1 -1
  246. package/dist/cli/commands/flow.d.ts +0 -22
  247. package/dist/cli/commands/flow.d.ts.map +0 -1
  248. package/dist/cli/commands/flow.js +0 -275
  249. package/dist/cli/commands/flow.js.map +0 -1
  250. package/dist/constraints/lifecycle-runner.d.ts +0 -52
  251. package/dist/constraints/lifecycle-runner.d.ts.map +0 -1
  252. package/dist/constraints/lifecycle-runner.js +0 -151
  253. package/dist/constraints/lifecycle-runner.js.map +0 -1
  254. package/dist/constraints/registry.d.ts +0 -27
  255. package/dist/constraints/registry.d.ts.map +0 -1
  256. package/dist/constraints/registry.js +0 -128
  257. package/dist/constraints/registry.js.map +0 -1
  258. package/dist/constraints/types.d.ts +0 -53
  259. package/dist/constraints/types.d.ts.map +0 -1
  260. package/dist/constraints/types.js +0 -8
  261. package/dist/constraints/types.js.map +0 -1
  262. package/dist/core/constraints/checkers/always-pass.d.ts +0 -17
  263. package/dist/core/constraints/checkers/always-pass.d.ts.map +0 -1
  264. package/dist/core/constraints/checkers/always-pass.js +0 -23
  265. package/dist/core/constraints/checkers/always-pass.js.map +0 -1
  266. package/dist/core/constraints/checkers/guideline-flags.d.ts +0 -9
  267. package/dist/core/constraints/checkers/guideline-flags.d.ts.map +0 -1
  268. package/dist/core/constraints/checkers/guideline-flags.js +0 -13
  269. package/dist/core/constraints/checkers/guideline-flags.js.map +0 -1
  270. package/dist/core/constraints/checkers/no-any-type.d.ts +0 -6
  271. package/dist/core/constraints/checkers/no-any-type.d.ts.map +0 -1
  272. package/dist/core/constraints/checkers/no-any-type.js +0 -42
  273. package/dist/core/constraints/checkers/no-any-type.js.map +0 -1
  274. package/dist/core/constraints/checkers/no-excuse-patterns.d.ts +0 -6
  275. package/dist/core/constraints/checkers/no-excuse-patterns.d.ts.map +0 -1
  276. package/dist/core/constraints/checkers/no-excuse-patterns.js +0 -25
  277. package/dist/core/constraints/checkers/no-excuse-patterns.js.map +0 -1
  278. package/dist/core/constraints/checkers/no-fuzzy-completion-claim.d.ts +0 -6
  279. package/dist/core/constraints/checkers/no-fuzzy-completion-claim.d.ts.map +0 -1
  280. package/dist/core/constraints/checkers/no-fuzzy-completion-claim.js +0 -34
  281. package/dist/core/constraints/checkers/no-fuzzy-completion-claim.js.map +0 -1
  282. package/dist/core/constraints/checkers/no-performative-agreement.d.ts +0 -8
  283. package/dist/core/constraints/checkers/no-performative-agreement.d.ts.map +0 -1
  284. package/dist/core/constraints/checkers/no-performative-agreement.js +0 -23
  285. package/dist/core/constraints/checkers/no-performative-agreement.js.map +0 -1
  286. package/dist/core/constraints/checkers/no-simplification-without-approval.d.ts +0 -6
  287. package/dist/core/constraints/checkers/no-simplification-without-approval.d.ts.map +0 -1
  288. package/dist/core/constraints/checkers/no-simplification-without-approval.js +0 -30
  289. package/dist/core/constraints/checkers/no-simplification-without-approval.js.map +0 -1
  290. package/dist/core/constraints/checkers/test-coverage.d.ts +0 -6
  291. package/dist/core/constraints/checkers/test-coverage.d.ts.map +0 -1
  292. package/dist/core/constraints/checkers/test-coverage.js +0 -33
  293. package/dist/core/constraints/checkers/test-coverage.js.map +0 -1
  294. package/dist/core/constraints/checkers/yagni.d.ts +0 -8
  295. package/dist/core/constraints/checkers/yagni.d.ts.map +0 -1
  296. package/dist/core/constraints/checkers/yagni.js +0 -96
  297. package/dist/core/constraints/checkers/yagni.js.map +0 -1
  298. package/dist/core/constraints/definitions/tips.d.ts +0 -7
  299. package/dist/core/constraints/definitions/tips.d.ts.map +0 -1
  300. package/dist/core/constraints/definitions/tips.js +0 -62
  301. package/dist/core/constraints/definitions/tips.js.map +0 -1
  302. package/dist/evolution/auto-evolve.d.ts +0 -35
  303. package/dist/evolution/auto-evolve.d.ts.map +0 -1
  304. package/dist/evolution/auto-evolve.js +0 -64
  305. package/dist/evolution/auto-evolve.js.map +0 -1
  306. package/dist/evolution/index.d.ts +0 -2
  307. package/dist/evolution/index.d.ts.map +0 -1
  308. package/dist/evolution/index.js +0 -18
  309. package/dist/evolution/index.js.map +0 -1
  310. package/dist/monitoring/constraint-evolver.d.ts +0 -120
  311. package/dist/monitoring/constraint-evolver.d.ts.map +0 -1
  312. package/dist/monitoring/constraint-evolver.js +0 -410
  313. package/dist/monitoring/constraint-evolver.js.map +0 -1
  314. package/src/__tests__/constraint-evolver.test.ts +0 -405
  315. package/src/__tests__/doctor-evolver.test.ts +0 -381
  316. package/src/cli/commands/__tests__/flow.test.ts +0 -343
  317. package/src/cli/commands/flow.ts +0 -277
  318. package/src/constraints/__tests__/lifecycle-runner.test.ts +0 -246
  319. package/src/constraints/__tests__/registry.test.ts +0 -196
  320. package/src/constraints/lifecycle-runner.ts +0 -184
  321. package/src/constraints/registry.ts +0 -134
  322. package/src/constraints/types.ts +0 -59
  323. package/src/core/constraints/checkers/always-pass.ts +0 -22
  324. package/src/core/constraints/checkers/guideline-flags.ts +0 -30
  325. package/src/core/constraints/checkers/no-any-type.ts +0 -42
  326. package/src/core/constraints/checkers/no-excuse-patterns.ts +0 -24
  327. package/src/core/constraints/checkers/no-fuzzy-completion-claim.ts +0 -33
  328. package/src/core/constraints/checkers/no-performative-agreement.ts +0 -22
  329. package/src/core/constraints/checkers/no-simplification-without-approval.ts +0 -29
  330. package/src/core/constraints/checkers/test-coverage.ts +0 -35
  331. package/src/core/constraints/checkers/yagni.ts +0 -65
  332. package/src/core/constraints/definitions/tips.ts +0 -63
  333. package/src/evolution/CONTEXT.md +0 -22
  334. package/src/evolution/__tests__/auto-evolve.test.ts +0 -319
  335. package/src/evolution/auto-evolve.ts +0 -98
  336. package/src/evolution/index.ts +0 -1
  337. package/src/monitoring/constraint-evolver.ts +0 -501
@@ -1,16 +1,17 @@
1
1
  /**
2
2
  * checker.ts 补充测试
3
3
  *
4
- * 目标:覆盖 checkTestCoveragecheckNoSimplificationWithoutApproval、
5
- * checkCapabilitySync 完整流程、beforeExecution、findApplicableConstraints
4
+ * 目标:覆盖 checkCapabilitySync 完整流程、beforeExecutionfindApplicableConstraints
5
+ * (ADR-0001:已退役 checker 的用例随约束数据模型 v2 移除)
6
6
  */
7
7
 
8
- import { describe, it, expect, beforeAll, afterAll } from '@jest/globals';
8
+ import { describe, it, expect, beforeAll, afterAll, jest } from '@jest/globals';
9
9
  import {
10
10
  ConstraintChecker,
11
11
  checkConstraints,
12
12
  checkBeforeExecution,
13
13
  } from '../core/constraints/checker';
14
+ import { getConstraintCheck, type CheckEnv } from '../core/constraints/checkers';
14
15
  import type { ConstraintContext } from '../types/constraint';
15
16
  import * as fs from 'fs';
16
17
  import * as path from 'path';
@@ -42,199 +43,6 @@ describe('ConstraintChecker - 补充覆盖', () => {
42
43
  }
43
44
  });
44
45
 
45
- describe('checkTestCoverage', () => {
46
- it('无 coverage 报告应该通过', async () => {
47
- const context: ConstraintContext = {
48
- operation: 'commit',
49
- projectPath: tempDir,
50
- };
51
-
52
- const result = await checker.check(
53
- {
54
- id: 'test_coverage_required',
55
- level: 'guideline',
56
- rule: 'COVERAGE',
57
- message: 'test',
58
- trigger: 'commit',
59
- enforcement: 'test',
60
- },
61
- context
62
- );
63
-
64
- expect(result.satisfied).toBe(true);
65
- });
66
-
67
- it('有 coverage-summary.json 且达标应该通过', async () => {
68
- // 创建 coverage 目录和报告
69
- const coverageDir = path.join(tempDir, 'coverage');
70
- fs.mkdirSync(coverageDir, { recursive: true });
71
-
72
- const summaryPath = path.join(coverageDir, 'coverage-summary.json');
73
- fs.writeFileSync(
74
- summaryPath,
75
- JSON.stringify({
76
- total: {
77
- lines: { pct: 80 },
78
- statements: { pct: 80 },
79
- branches: { pct: 75 },
80
- functions: { pct: 85 },
81
- },
82
- })
83
- );
84
-
85
- const context: ConstraintContext = {
86
- operation: 'commit',
87
- projectPath: tempDir,
88
- };
89
-
90
- const result = await checker.check(
91
- {
92
- id: 'test_coverage_required',
93
- level: 'guideline',
94
- rule: 'COVERAGE',
95
- message: 'test',
96
- trigger: 'commit',
97
- enforcement: 'test',
98
- },
99
- context
100
- );
101
-
102
- expect(result.satisfied).toBe(true);
103
-
104
- // 清理
105
- fs.rmSync(coverageDir, { recursive: true, force: true });
106
- });
107
-
108
- it('覆盖率低于 50% 应该失败', async () => {
109
- const coverageDir = path.join(tempDir, 'coverage');
110
- fs.mkdirSync(coverageDir, { recursive: true });
111
-
112
- const summaryPath = path.join(coverageDir, 'coverage-summary.json');
113
- fs.writeFileSync(
114
- summaryPath,
115
- JSON.stringify({
116
- total: {
117
- lines: { pct: 30 },
118
- },
119
- })
120
- );
121
-
122
- const context: ConstraintContext = {
123
- operation: 'commit',
124
- projectPath: tempDir,
125
- };
126
-
127
- const result = await checker.check(
128
- {
129
- id: 'test_coverage_required',
130
- level: 'guideline',
131
- rule: 'COVERAGE',
132
- message: 'test',
133
- trigger: 'commit',
134
- enforcement: 'test',
135
- },
136
- context
137
- );
138
-
139
- expect(result.satisfied).toBe(false);
140
-
141
- fs.rmSync(coverageDir, { recursive: true, force: true });
142
- });
143
-
144
- it('coverage-summary.json 解析失败应该通过', async () => {
145
- const coverageDir = path.join(tempDir, 'coverage');
146
- fs.mkdirSync(coverageDir, { recursive: true });
147
-
148
- const summaryPath = path.join(coverageDir, 'coverage-summary.json');
149
- fs.writeFileSync(summaryPath, 'invalid json {');
150
-
151
- const context: ConstraintContext = {
152
- operation: 'commit',
153
- projectPath: tempDir,
154
- };
155
-
156
- const result = await checker.check(
157
- {
158
- id: 'test_coverage_required',
159
- level: 'guideline',
160
- rule: 'COVERAGE',
161
- message: 'test',
162
- trigger: 'commit',
163
- enforcement: 'test',
164
- },
165
- context
166
- );
167
-
168
- // 解析失败默认通过
169
- expect(result.satisfied).toBe(true);
170
-
171
- fs.rmSync(coverageDir, { recursive: true, force: true });
172
- });
173
- });
174
-
175
- describe('checkNoSimplificationWithoutApproval', () => {
176
- it('正常 diff 应该通过', async () => {
177
- // clean state
178
- execSync('git checkout .', { cwd: tempDir, stdio: 'pipe' });
179
-
180
- const normalFile = path.join(tempDir, 'normal.ts');
181
- fs.writeFileSync(normalFile, 'export const x = 1;');
182
-
183
- const context: ConstraintContext = {
184
- operation: 'commit',
185
- projectPath: tempDir,
186
- };
187
-
188
- const result = await checker.check(
189
- {
190
- id: 'no_simplification_without_approval',
191
- level: 'guideline',
192
- rule: 'NO SIMPLIFICATION',
193
- message: 'test',
194
- trigger: 'commit',
195
- enforcement: 'test',
196
- },
197
- context
198
- );
199
-
200
- expect(result.satisfied).toBe(true);
201
- });
202
-
203
- it('包含简化关键词应该失败', async () => {
204
- // 先完成之前的提交
205
- execSync('git add .', { cwd: tempDir });
206
- execSync('git commit -m "cleanup" --allow-empty', { cwd: tempDir });
207
-
208
- // 创建包含简化关键词的文件并 stage
209
- const simplifyFile = path.join(tempDir, 'simplify-msg.ts');
210
- fs.writeFileSync(simplifyFile, '// removed test for simplicity\nexport const y = 2;');
211
- execSync('git add .', { cwd: tempDir });
212
-
213
- const context: ConstraintContext = {
214
- operation: 'commit',
215
- projectPath: tempDir,
216
- };
217
-
218
- const result = await checker.check(
219
- {
220
- id: 'no_simplification_without_approval',
221
- level: 'guideline',
222
- rule: 'NO SIMPLIFICATION',
223
- message: 'test',
224
- trigger: 'commit',
225
- enforcement: 'test',
226
- },
227
- context
228
- );
229
-
230
- expect(result.satisfied).toBe(false);
231
-
232
- // cleanup - unstage and remove
233
- execSync('git reset HEAD -- ' + simplifyFile, { cwd: tempDir, stdio: 'pipe' });
234
- fs.rmSync(simplifyFile, { force: true });
235
- });
236
- });
237
-
238
46
  describe('checkCapabilitySync', () => {
239
47
  it('无代码变更应该通过', async () => {
240
48
  // 清理 staged changes
@@ -248,6 +56,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
248
56
  const result = await checker.check(
249
57
  {
250
58
  id: 'capability_sync',
59
+ kind: 'check',
251
60
  level: 'guideline',
252
61
  rule: 'CAPABILITY SYNC',
253
62
  message: 'test',
@@ -278,6 +87,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
278
87
  const result = await checker.check(
279
88
  {
280
89
  id: 'capability_sync',
90
+ kind: 'check',
281
91
  level: 'guideline',
282
92
  rule: 'CAPABILITY SYNC',
283
93
  message: 'test',
@@ -311,6 +121,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
311
121
  const result = await checker.check(
312
122
  {
313
123
  id: 'capability_sync',
124
+ kind: 'check',
314
125
  level: 'guideline',
315
126
  rule: 'CAPABILITY SYNC',
316
127
  message: 'test',
@@ -327,13 +138,13 @@ describe('ConstraintChecker - 补充覆盖', () => {
327
138
  execSync('git reset HEAD', { cwd: tempDir, stdio: 'pipe' });
328
139
  });
329
140
 
330
- it('有代码变更但无 CAPABILITIES.md 应该失败', async () => {
141
+ it('有代码变更但无 CAPABILITIES.md 应该跳过(ADR-0001 存在性探测)', async () => {
331
142
  // 确保 CAPABILITIES.md 不存在
332
143
  const capFile = path.join(tempDir, 'CAPABILITIES.md');
333
144
  if (fs.existsSync(capFile)) {
334
145
  fs.rmSync(capFile, { force: true });
335
146
  }
336
-
147
+
337
148
  // 创建新的代码文件并 stage
338
149
  const newCodeFile = path.join(tempDir, 'newfeature.ts');
339
150
  fs.writeFileSync(newCodeFile, 'export function newfeature() {}');
@@ -347,6 +158,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
347
158
  const result = await checker.check(
348
159
  {
349
160
  id: 'capability_sync',
161
+ kind: 'check',
350
162
  level: 'guideline',
351
163
  rule: 'CAPABILITY SYNC',
352
164
  message: 'test',
@@ -356,8 +168,9 @@ describe('ConstraintChecker - 补充覆盖', () => {
356
168
  context
357
169
  );
358
170
 
359
- // 没有 CAPABILITIES.md,应该失败
360
- expect(result.satisfied).toBe(false);
171
+ // 未采用 CAPABILITIES.md 约定 → skip(不计 pass/fail,不阻断)
172
+ expect(result.skipped).toBe(true);
173
+ expect(result.satisfied).toBe(true);
361
174
 
362
175
  // Cleanup
363
176
  execSync('git reset HEAD', { cwd: tempDir, stdio: 'pipe' });
@@ -390,6 +203,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
390
203
  checker.check(
391
204
  {
392
205
  id: 'capability_sync',
206
+ kind: 'check',
393
207
  level: 'guideline',
394
208
  rule: 'CAPABILITY SYNC',
395
209
  message: 'test',
@@ -494,17 +308,178 @@ describe('ConstraintChecker - 补充覆盖', () => {
494
308
  });
495
309
  });
496
310
 
311
+ describe('checkCapabilitySync 遗留问题回归(2026-08-08 评估稿第四节)', () => {
312
+ const capCheck = getConstraintCheck('capability_sync')!;
313
+
314
+ const setupDir = (name: string, capContent: string, files: string[] = []): string => {
315
+ const dir = path.join(tempDir, name);
316
+ fs.mkdirSync(dir, { recursive: true });
317
+ fs.writeFileSync(path.join(dir, 'CAPABILITIES.md'), capContent);
318
+ for (const f of files) {
319
+ const fp = path.join(dir, f);
320
+ fs.mkdirSync(path.dirname(fp), { recursive: true });
321
+ fs.writeFileSync(fp, 'export const x = 1;');
322
+ }
323
+ return dir;
324
+ };
325
+
326
+ /** 直接构造 CheckEnv:stub stagedDiffNames / srcScan,不走真实 git */
327
+ const makeEnv = (
328
+ projectPath: string,
329
+ staged: string[],
330
+ scan: Record<string, string[]> = {}
331
+ ): CheckEnv => ({
332
+ context: { operation: 'commit', projectPath },
333
+ projectPath,
334
+ stagedDiff: async () => '',
335
+ stagedDiffNames: async () => staged.join('\n'),
336
+ srcScan: (root: string) => scan[root] ?? [],
337
+ });
338
+
339
+ const tableWith = (row: string): string =>
340
+ '# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n' + row + '\n';
341
+
342
+ const EMPTY_TABLE = '# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n';
343
+
344
+ it('问题1:step1 应要求每个变更文件都被覆盖(every 而非 some)', async () => {
345
+ // 文档登记 src/foo.ts;同时 staged 已登记的 src/foo.ts 与未登记的 scripts/bar.ts
346
+ // some() 下只要有 foo.ts 命中即整体通过,bar.ts 漏网
347
+ const dir = setupDir('legacy-step1-some', tableWith('| foo | src/foo.ts | foo |'));
348
+ try {
349
+ expect(await capCheck.evaluate(makeEnv(dir, ['src/foo.ts', 'scripts/bar.ts']))).toBe(false);
350
+ } finally {
351
+ fs.rmSync(dir, { recursive: true, force: true });
352
+ }
353
+ });
354
+
355
+ it('问题3a:step1 不得因 endsWith 造成后缀碰撞(xfoo.ts 不应被 foo.ts 覆盖)', async () => {
356
+ const dir = setupDir('legacy-step1-endswith', tableWith('| foo | foo.ts | foo |'));
357
+ try {
358
+ expect(await capCheck.evaluate(makeEnv(dir, ['web/xfoo.ts']))).toBe(false);
359
+ } finally {
360
+ fs.rmSync(dir, { recursive: true, force: true });
361
+ }
362
+ });
363
+
364
+ it('问题3b:step1 不得因 includes 造成子串误配(docs/src/foo.tsx 不应被 src/foo.ts 覆盖)', async () => {
365
+ const dir = setupDir('legacy-step1-includes', tableWith('| foo | src/foo.ts | foo |'));
366
+ try {
367
+ expect(await capCheck.evaluate(makeEnv(dir, ['docs/src/foo.tsx']))).toBe(false);
368
+ } finally {
369
+ fs.rmSync(dir, { recursive: true, force: true });
370
+ }
371
+ });
372
+
373
+ it('兼容:basename 条目仍可按路径边界后缀匹配(src/foo.ts 被 foo.ts 覆盖)', async () => {
374
+ const dir = setupDir('legacy-step1-suffix-ok', tableWith('| foo | foo.ts | foo |'));
375
+ try {
376
+ expect(await capCheck.evaluate(makeEnv(dir, ['src/foo.ts']))).toBe(true);
377
+ } finally {
378
+ fs.rmSync(dir, { recursive: true, force: true });
379
+ }
380
+ });
381
+
382
+ it('问题2:空表格 + 有 staged 变更时不得直接放行', async () => {
383
+ const dir = setupDir('legacy-empty-table-diff', EMPTY_TABLE);
384
+ try {
385
+ expect(await capCheck.evaluate(makeEnv(dir, ['src/foo.ts']))).toBe(false);
386
+ } finally {
387
+ fs.rmSync(dir, { recursive: true, force: true });
388
+ }
389
+ });
390
+
391
+ it('问题2:空表格 + 源码根有文件时 step2 不得直接放行', async () => {
392
+ const dir = setupDir('legacy-empty-table-scan', EMPTY_TABLE, ['src/foo.ts']);
393
+ try {
394
+ expect(await capCheck.evaluate(makeEnv(dir, [], { src: ['src/foo.ts'] }))).toBe(false);
395
+ } finally {
396
+ fs.rmSync(dir, { recursive: true, force: true });
397
+ }
398
+ });
399
+
400
+ it('问题2 兼容:清单格式(计数行)无文件表,仍直接放行', async () => {
401
+ const dir = setupDir(
402
+ 'legacy-listing-format',
403
+ '# Harness Capabilities\n\n## CLI Commands (25)\ncheck, validate\n'
404
+ );
405
+ try {
406
+ expect(await capCheck.evaluate(makeEnv(dir, ['src/foo.ts']))).toBe(true);
407
+ } finally {
408
+ fs.rmSync(dir, { recursive: true, force: true });
409
+ }
410
+ });
411
+
412
+ it('问题4:检查异常默认放行时应输出 warn 而非静默吞错', async () => {
413
+ const dir = setupDir('legacy-failopen', tableWith('| foo | src/foo.ts | foo |'));
414
+ const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
415
+ try {
416
+ const env: CheckEnv = {
417
+ ...makeEnv(dir, []),
418
+ stagedDiffNames: async () => {
419
+ throw new Error('git boom');
420
+ },
421
+ };
422
+ // fail-open 语义保留:异常时仍放行
423
+ expect(await capCheck.evaluate(env)).toBe(true);
424
+ expect(warnSpy).toHaveBeenCalled();
425
+ } finally {
426
+ warnSpy.mockRestore();
427
+ fs.rmSync(dir, { recursive: true, force: true });
428
+ }
429
+ });
430
+
431
+ it('step2(file 模式)与 step1 语义一致:basename 条目可按路径边界后缀覆盖', async () => {
432
+ // 文档第二列写 basename foo.ts;step1 认可它(边界后缀),
433
+ // step2 裸精确匹配却永远不命中 srcScan 返回的 src/foo.ts → 永远红
434
+ const dir = setupDir('legacy-step2-basename', tableWith('| foo | foo.ts | foo |'), [
435
+ 'src/foo.ts',
436
+ ]);
437
+ try {
438
+ expect(await capCheck.evaluate(makeEnv(dir, [], { src: ['src/foo.ts'] }))).toBe(true);
439
+ } finally {
440
+ fs.rmSync(dir, { recursive: true, force: true });
441
+ }
442
+ });
443
+
444
+ it('step2(file 模式):不同路径的同后缀文件不得被误覆盖(lib/foo.ts 不覆盖 src/foo.ts)', async () => {
445
+ const dir = setupDir('legacy-step2-boundary', tableWith('| foo | lib/foo.ts | foo |'), [
446
+ 'src/foo.ts',
447
+ ]);
448
+ try {
449
+ expect(await capCheck.evaluate(makeEnv(dir, [], { src: ['src/foo.ts'] }))).toBe(false);
450
+ } finally {
451
+ fs.rmSync(dir, { recursive: true, force: true });
452
+ }
453
+ });
454
+
455
+ it('step2(module 模式)文件条目同样按路径边界后缀覆盖', async () => {
456
+ const dir = setupDir('legacy-step2-module-suffix', tableWith('| foo | foo.ts | foo |'), [
457
+ 'src/foo.ts',
458
+ ]);
459
+ fs.mkdirSync(path.join(dir, '.harness'), { recursive: true });
460
+ fs.writeFileSync(
461
+ path.join(dir, '.harness', 'config.yml'),
462
+ 'governance:\n capabilities:\n mode: module\n'
463
+ );
464
+ try {
465
+ expect(await capCheck.evaluate(makeEnv(dir, [], { src: ['src/foo.ts'] }))).toBe(true);
466
+ } finally {
467
+ fs.rmSync(dir, { recursive: true, force: true });
468
+ }
469
+ });
470
+ });
471
+
497
472
  describe('findApplicableConstraints', () => {
498
473
  it('应该过滤出匹配 trigger 的约束', () => {
499
474
  const context: ConstraintContext = {
500
- operation: 'task_completion_claim',
475
+ operation: 'code_implementation',
501
476
  };
502
477
 
503
478
  const result = checker.findApplicableConstraints(context);
504
479
 
505
480
  expect(result.ironLaws.length).toBeGreaterThan(0);
506
481
  expect(
507
- result.ironLaws.some((c: any) => c.id === 'no_self_approval')
482
+ result.ironLaws.some((c: any) => c.id === 'no_completion_without_verification')
508
483
  ).toBe(true);
509
484
  });
510
485
 
@@ -604,6 +579,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
604
579
  const result = await checker.check(
605
580
  {
606
581
  id: 'test_severity_iron',
582
+ kind: 'prompt',
607
583
  level: 'iron_law',
608
584
  rule: 'TEST',
609
585
  message: 'test',
@@ -624,6 +600,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
624
600
  const result = await checker.check(
625
601
  {
626
602
  id: 'test_severity_guideline',
603
+ kind: 'prompt',
627
604
  level: 'guideline',
628
605
  rule: 'TEST',
629
606
  message: 'test',
@@ -644,6 +621,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
644
621
  const result = await checker.check(
645
622
  {
646
623
  id: 'test_severity_tip',
624
+ kind: 'prompt',
647
625
  level: 'tip',
648
626
  rule: 'TEST',
649
627
  message: 'test',
@@ -666,9 +644,10 @@ describe('ConstraintChecker - 补充覆盖', () => {
666
644
 
667
645
  const result = await checker.check(
668
646
  {
669
- id: 'no_fix_without_root_cause',
647
+ id: 'incremental_progress',
648
+ kind: 'check',
670
649
  level: 'guideline',
671
- rule: 'NO FIXES WITHOUT ROOT CAUSE',
650
+ rule: 'ONE TASK PER SESSION',
672
651
  message: 'test',
673
652
  trigger: 'code_implementation',
674
653
  enforcement: 'test',
@@ -690,9 +669,10 @@ describe('ConstraintChecker - 补充覆盖', () => {
690
669
 
691
670
  const result = await checker.check(
692
671
  {
693
- id: 'no_fix_without_root_cause',
672
+ id: 'incremental_progress',
673
+ kind: 'check',
694
674
  level: 'guideline',
695
- rule: 'NO FIXES WITHOUT ROOT CAUSE',
675
+ rule: 'ONE TASK PER SESSION',
696
676
  message: 'test',
697
677
  trigger: 'code_implementation',
698
678
  enforcement: 'test',
@@ -701,20 +681,23 @@ describe('ConstraintChecker - 补充覆盖', () => {
701
681
  context
702
682
  );
703
683
 
704
- expect(result.satisfied).toBe(false);
684
+ // hasSingleTask 未接线(undefined)→ skip(ADR-0001:flag 未接线不评估,不误报违规)
685
+ expect(result.skipped).toBe(true);
686
+ expect(result.satisfied).toBe(true);
705
687
  });
706
688
 
707
689
  it('iron_law 不应该检查例外条件', async () => {
708
690
  const context: ConstraintContext = {
709
691
  operation: 'code_implementation',
710
- hasTest: false,
692
+ isSimpleTypo: true,
711
693
  };
712
694
 
713
695
  const result = await checker.check(
714
696
  {
715
- id: 'no_self_approval',
697
+ id: 'incremental_progress',
698
+ kind: 'check',
716
699
  level: 'iron_law',
717
- rule: 'NO SELF APPROVAL',
700
+ rule: 'ONE TASK PER SESSION',
718
701
  message: 'test',
719
702
  trigger: 'code_implementation',
720
703
  enforcement: 'test',
@@ -723,20 +706,23 @@ describe('ConstraintChecker - 补充覆盖', () => {
723
706
  context
724
707
  );
725
708
 
726
- expect(result.satisfied).toBe(false);
709
+ // iron_law 不适用例外;hasSingleTask 未接线(undefined)→ skip(ADR-0001)
710
+ expect(result.skipped).toBe(true);
711
+ expect(result.satisfied).toBe(true);
727
712
  });
728
713
 
729
714
  it('无 exceptions 字段应该正常检查', async () => {
730
715
  const context: ConstraintContext = {
731
716
  operation: 'code_implementation',
732
- hasRootCauseInvestigation: true,
717
+ hasVerificationEvidence: true,
733
718
  };
734
719
 
735
720
  const result = await checker.check(
736
721
  {
737
- id: 'no_fix_without_root_cause',
738
- level: 'guideline',
739
- rule: 'NO FIXES WITHOUT ROOT CAUSE',
722
+ id: 'no_completion_without_verification',
723
+ kind: 'check',
724
+ level: 'iron_law',
725
+ rule: 'NO COMPLETION WITHOUT VERIFICATION',
740
726
  message: 'test',
741
727
  trigger: 'code_implementation',
742
728
  enforcement: 'test',
@@ -763,6 +749,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
763
749
  const result = await checker.check(
764
750
  {
765
751
  id: 'no_bypass_checkpoint',
752
+ kind: 'check',
766
753
  level: 'iron_law',
767
754
  rule: 'NO BYPASS',
768
755
  message: 'test',
@@ -792,6 +779,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
792
779
  const result = await checker.check(
793
780
  {
794
781
  id: 'no_bypass_checkpoint',
782
+ kind: 'check',
795
783
  level: 'iron_law',
796
784
  rule: 'NO BYPASS',
797
785
  message: 'test',
@@ -808,120 +796,6 @@ describe('ConstraintChecker - 补充覆盖', () => {
808
796
  });
809
797
  });
810
798
 
811
- describe('checkNoAnyType 文件检查', () => {
812
- it('.ts 文件包含 : any 应该失败', async () => {
813
- const anyFile = path.join(tempDir, 'any-test.ts');
814
- fs.writeFileSync(anyFile, 'const x: any = {};');
815
-
816
- const context: ConstraintContext = {
817
- operation: 'code_implementation',
818
- projectPath: tempDir,
819
- changedFiles: [anyFile],
820
- };
821
-
822
- const result = await checker.check(
823
- {
824
- id: 'no_any_type',
825
- level: 'guideline',
826
- rule: 'NO ANY',
827
- message: 'test',
828
- trigger: 'code_implementation',
829
- enforcement: 'test',
830
- },
831
- context
832
- );
833
-
834
- expect(result.satisfied).toBe(false);
835
-
836
- fs.unlinkSync(anyFile);
837
- });
838
-
839
- it('.ts 文件注释中的 : any 应该被忽略', async () => {
840
- const commentFile = path.join(tempDir, 'comment-any.ts');
841
- fs.writeFileSync(commentFile, '// const x: any = {};\nconst y: string = "ok";');
842
-
843
- const context: ConstraintContext = {
844
- operation: 'code_implementation',
845
- projectPath: tempDir,
846
- changedFiles: [commentFile],
847
- };
848
-
849
- const result = await checker.check(
850
- {
851
- id: 'no_any_type',
852
- level: 'guideline',
853
- rule: 'NO ANY',
854
- message: 'test',
855
- trigger: 'code_implementation',
856
- enforcement: 'test',
857
- },
858
- context
859
- );
860
-
861
- expect(result.satisfied).toBe(true);
862
-
863
- fs.unlinkSync(commentFile);
864
- });
865
-
866
- it('非 .ts 文件应该跳过检查', async () => {
867
- const jsFile = path.join(tempDir, 'any-test.js');
868
- fs.writeFileSync(jsFile, 'const x = {};');
869
-
870
- const context: ConstraintContext = {
871
- operation: 'code_implementation',
872
- projectPath: tempDir,
873
- changedFiles: [jsFile],
874
- };
875
-
876
- const result = await checker.check(
877
- {
878
- id: 'no_any_type',
879
- level: 'guideline',
880
- rule: 'NO ANY',
881
- message: 'test',
882
- trigger: 'code_implementation',
883
- enforcement: 'test',
884
- },
885
- context
886
- );
887
-
888
- expect(result.satisfied).toBe(true);
889
-
890
- fs.unlinkSync(jsFile);
891
- });
892
- });
893
-
894
- describe('checkTestCoverage coverage-final.json', () => {
895
- it('只有 coverage-final.json 应该通过', async () => {
896
- const coverageDir = path.join(tempDir, 'coverage');
897
- fs.mkdirSync(coverageDir, { recursive: true });
898
-
899
- const finalPath = path.join(coverageDir, 'coverage-final.json');
900
- fs.writeFileSync(finalPath, JSON.stringify({}));
901
-
902
- const context: ConstraintContext = {
903
- operation: 'commit',
904
- projectPath: tempDir,
905
- };
906
-
907
- const result = await checker.check(
908
- {
909
- id: 'test_coverage_required',
910
- level: 'guideline',
911
- rule: 'COVERAGE',
912
- message: 'test',
913
- trigger: 'commit',
914
- enforcement: 'test',
915
- },
916
- context
917
- );
918
-
919
- expect(result.satisfied).toBe(true);
920
-
921
- fs.rmSync(coverageDir, { recursive: true, force: true });
922
- });
923
- });
924
-
925
799
  describe('checkConstraint 快捷函数', () => {
926
800
  it('未知约束应该返回不满足', async () => {
927
801
  const { checkConstraint } = await import('../core/constraints/checker');
@@ -940,11 +814,11 @@ describe('ConstraintChecker - 补充覆盖', () => {
940
814
 
941
815
  const context: ConstraintContext = {
942
816
  operation: 'code_implementation',
943
- hasTest: true,
817
+ hasVerificationEvidence: true,
944
818
  };
945
819
 
946
- const result = await checkConstraint('no_self_approval', context);
947
- expect(result.id).toBe('no_self_approval');
820
+ const result = await checkConstraint('no_completion_without_verification', context);
821
+ expect(result.id).toBe('no_completion_without_verification');
948
822
  expect(result.satisfied).toBe(true);
949
823
  });
950
824
  });
@@ -975,6 +849,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
975
849
  const result = await checker.check(
976
850
  {
977
851
  id: 'no_test_simplification',
852
+ kind: 'check',
978
853
  level: 'iron_law',
979
854
  rule: 'NO SIMPLIFY',
980
855
  message: 'test',
@@ -1004,6 +879,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1004
879
  const result = await checker.check(
1005
880
  {
1006
881
  id: 'context_doc_sync',
882
+ kind: 'check',
1007
883
  level: 'guideline',
1008
884
  rule: 'CONTEXT DOC SYNC',
1009
885
  message: 'test',
@@ -1048,6 +924,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1048
924
  const result = await checker.check(
1049
925
  {
1050
926
  id: 'context_doc_sync',
927
+ kind: 'check',
1051
928
  level: 'guideline',
1052
929
  rule: 'CONTEXT DOC SYNC',
1053
930
  message: 'test',
@@ -1089,6 +966,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1089
966
  const result = await checker.check(
1090
967
  {
1091
968
  id: 'context_doc_sync',
969
+ kind: 'check',
1092
970
  level: 'guideline',
1093
971
  rule: 'CONTEXT DOC SYNC',
1094
972
  message: 'test',
@@ -1127,6 +1005,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1127
1005
  const result = await checker.check(
1128
1006
  {
1129
1007
  id: 'context_doc_sync',
1008
+ kind: 'check',
1130
1009
  level: 'guideline',
1131
1010
  rule: 'CONTEXT DOC SYNC',
1132
1011
  message: 'test',
@@ -1155,6 +1034,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1155
1034
  const result = await checker.check(
1156
1035
  {
1157
1036
  id: 'docs_freshness',
1037
+ kind: 'check',
1158
1038
  level: 'guideline',
1159
1039
  rule: 'DOCS FRESHNESS',
1160
1040
  message: 'test',
@@ -1182,6 +1062,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1182
1062
  const result = await checker.check(
1183
1063
  {
1184
1064
  id: 'docs_freshness',
1065
+ kind: 'check',
1185
1066
  level: 'guideline',
1186
1067
  rule: 'DOCS FRESHNESS',
1187
1068
  message: 'test',
@@ -1218,6 +1099,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1218
1099
  const result = await checker.check(
1219
1100
  {
1220
1101
  id: 'docs_freshness',
1102
+ kind: 'check',
1221
1103
  level: 'guideline',
1222
1104
  rule: 'DOCS FRESHNESS',
1223
1105
  message: 'test',
@@ -1255,6 +1137,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1255
1137
  const result = await checker.check(
1256
1138
  {
1257
1139
  id: 'docs_freshness',
1140
+ kind: 'check',
1258
1141
  level: 'guideline',
1259
1142
  rule: 'DOCS FRESHNESS',
1260
1143
  message: 'test',
@@ -1291,6 +1174,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1291
1174
  const result = await checker.check(
1292
1175
  {
1293
1176
  id: 'docs_freshness',
1177
+ kind: 'check',
1294
1178
  level: 'guideline',
1295
1179
  rule: 'DOCS FRESHNESS',
1296
1180
  message: 'test',
@@ -1324,6 +1208,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1324
1208
  const result = await checker.check(
1325
1209
  {
1326
1210
  id: 'docs_freshness',
1211
+ kind: 'check',
1327
1212
  level: 'guideline',
1328
1213
  rule: 'DOCS FRESHNESS',
1329
1214
  message: 'test',
@@ -1356,6 +1241,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1356
1241
  const result = await checker.check(
1357
1242
  {
1358
1243
  id: 'docs_freshness',
1244
+ kind: 'check',
1359
1245
  level: 'guideline',
1360
1246
  rule: 'DOCS FRESHNESS',
1361
1247
  message: 'test',
@@ -1391,6 +1277,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1391
1277
  const result = await checker.check(
1392
1278
  {
1393
1279
  id: 'docs_freshness',
1280
+ kind: 'check',
1394
1281
  level: 'guideline',
1395
1282
  rule: 'DOCS FRESHNESS',
1396
1283
  message: 'test',
@@ -1423,6 +1310,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1423
1310
  const result = await checker.check(
1424
1311
  {
1425
1312
  id: 'docs_freshness',
1313
+ kind: 'check',
1426
1314
  level: 'guideline',
1427
1315
  rule: 'DOCS FRESHNESS',
1428
1316
  message: 'test',
@@ -1455,6 +1343,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1455
1343
  const result = await checker.check(
1456
1344
  {
1457
1345
  id: 'docs_freshness',
1346
+ kind: 'check',
1458
1347
  level: 'guideline',
1459
1348
  rule: 'DOCS FRESHNESS',
1460
1349
  message: 'test',
@@ -1481,6 +1370,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1481
1370
  const result = await checker.check(
1482
1371
  {
1483
1372
  id: 'docs_freshness',
1373
+ kind: 'check',
1484
1374
  level: 'guideline',
1485
1375
  rule: 'DOCS FRESHNESS',
1486
1376
  message: 'test',
@@ -1549,6 +1439,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1549
1439
  ironLaws: {
1550
1440
  test_only: {
1551
1441
  id: 'test_only',
1442
+ kind: 'prompt' as const,
1552
1443
  level: 'iron_law' as const,
1553
1444
  rule: 'TEST',
1554
1445
  message: 'test',
@@ -1582,6 +1473,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1582
1473
  ironLaws: {
1583
1474
  rule_a: {
1584
1475
  id: 'rule_a',
1476
+ kind: 'prompt' as const,
1585
1477
  level: 'iron_law' as const,
1586
1478
  rule: 'A',
1587
1479
  message: 'project A',
@@ -1599,6 +1491,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1599
1491
  ironLaws: {
1600
1492
  rule_b: {
1601
1493
  id: 'rule_b',
1494
+ kind: 'prompt' as const,
1602
1495
  level: 'iron_law' as const,
1603
1496
  rule: 'B',
1604
1497
  message: 'project B',
@@ -1648,7 +1541,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1648
1541
  const defaultKeys = Object.keys(defaultConstraints.ironLaws);
1649
1542
 
1650
1543
  const customConfig = {
1651
- ironLaws: { compat_test: { id: 'compat_test', level: 'iron_law' as const, rule: 'C', message: 'compat', trigger: 'file_modification', enforcement: 'test' } },
1544
+ ironLaws: { compat_test: { id: 'compat_test', kind: 'prompt' as const, level: 'iron_law' as const, rule: 'C', message: 'compat', trigger: 'file_modification', enforcement: 'test' } },
1652
1545
  guidelines: {},
1653
1546
  tips: {},
1654
1547
  disabled: [] as string[],
@@ -1672,6 +1565,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1672
1565
  ironLaws: {
1673
1566
  custom_rule: {
1674
1567
  id: 'custom_rule',
1568
+ kind: 'prompt' as const,
1675
1569
  level: 'iron_law' as const,
1676
1570
  rule: 'CUSTOM',
1677
1571
  message: 'custom',
@@ -1710,6 +1604,7 @@ describe('ConstraintChecker - 补充覆盖', () => {
1710
1604
  const result = await checker.check(
1711
1605
  {
1712
1606
  id: 'docs_freshness',
1607
+ kind: 'check',
1713
1608
  level: 'guideline',
1714
1609
  rule: 'DOCS FRESHNESS',
1715
1610
  message: 'test',