@dommaker/harness 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (517) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +0 -2
  3. package/bin/harness.js +86 -32
  4. package/dist/cli/command-contract.d.ts +83 -0
  5. package/dist/cli/command-contract.d.ts.map +1 -0
  6. package/dist/cli/command-contract.js +66 -0
  7. package/dist/cli/command-contract.js.map +1 -0
  8. package/dist/cli/commands/acceptance.d.ts +3 -2
  9. package/dist/cli/commands/acceptance.d.ts.map +1 -1
  10. package/dist/cli/commands/acceptance.js +28 -24
  11. package/dist/cli/commands/acceptance.js.map +1 -1
  12. package/dist/cli/commands/analyze-sessions.d.ts +2 -1
  13. package/dist/cli/commands/analyze-sessions.d.ts.map +1 -1
  14. package/dist/cli/commands/analyze-sessions.js +25 -24
  15. package/dist/cli/commands/analyze-sessions.js.map +1 -1
  16. package/dist/cli/commands/check.d.ts +14 -2
  17. package/dist/cli/commands/check.d.ts.map +1 -1
  18. package/dist/cli/commands/check.js +76 -61
  19. package/dist/cli/commands/check.js.map +1 -1
  20. package/dist/cli/commands/command.d.ts +5 -1
  21. package/dist/cli/commands/command.d.ts.map +1 -1
  22. package/dist/cli/commands/command.js +26 -17
  23. package/dist/cli/commands/command.js.map +1 -1
  24. package/dist/cli/commands/constraints-report.d.ts +2 -1
  25. package/dist/cli/commands/constraints-report.d.ts.map +1 -1
  26. package/dist/cli/commands/constraints-report.js +37 -33
  27. package/dist/cli/commands/constraints-report.js.map +1 -1
  28. package/dist/cli/commands/constraints-retire.d.ts +6 -5
  29. package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
  30. package/dist/cli/commands/constraints-retire.js +50 -52
  31. package/dist/cli/commands/constraints-retire.js.map +1 -1
  32. package/dist/cli/commands/constraints.d.ts +2 -1
  33. package/dist/cli/commands/constraints.d.ts.map +1 -1
  34. package/dist/cli/commands/constraints.js +10 -27
  35. package/dist/cli/commands/constraints.js.map +1 -1
  36. package/dist/cli/commands/contract.d.ts +3 -2
  37. package/dist/cli/commands/contract.d.ts.map +1 -1
  38. package/dist/cli/commands/contract.js +45 -42
  39. package/dist/cli/commands/contract.js.map +1 -1
  40. package/dist/cli/commands/definitions.d.ts +11 -11
  41. package/dist/cli/commands/definitions.d.ts.map +1 -1
  42. package/dist/cli/commands/definitions.js +29 -214
  43. package/dist/cli/commands/definitions.js.map +1 -1
  44. package/dist/cli/commands/failure.d.ts +5 -4
  45. package/dist/cli/commands/failure.d.ts.map +1 -1
  46. package/dist/cli/commands/failure.js +31 -25
  47. package/dist/cli/commands/failure.js.map +1 -1
  48. package/dist/cli/commands/init.d.ts +7 -5
  49. package/dist/cli/commands/init.d.ts.map +1 -1
  50. package/dist/cli/commands/init.js +147 -211
  51. package/dist/cli/commands/init.js.map +1 -1
  52. package/dist/cli/commands/knowledge.d.ts +21 -15
  53. package/dist/cli/commands/knowledge.d.ts.map +1 -1
  54. package/dist/cli/commands/knowledge.js +214 -199
  55. package/dist/cli/commands/knowledge.js.map +1 -1
  56. package/dist/cli/commands/passes-gate.d.ts +8 -2
  57. package/dist/cli/commands/passes-gate.d.ts.map +1 -1
  58. package/dist/cli/commands/passes-gate.js +44 -66
  59. package/dist/cli/commands/passes-gate.js.map +1 -1
  60. package/dist/cli/commands/performance.d.ts +2 -1
  61. package/dist/cli/commands/performance.d.ts.map +1 -1
  62. package/dist/cli/commands/performance.js +20 -18
  63. package/dist/cli/commands/performance.js.map +1 -1
  64. package/dist/cli/commands/posteval-plan.d.ts +5 -1
  65. package/dist/cli/commands/posteval-plan.d.ts.map +1 -1
  66. package/dist/cli/commands/posteval-plan.js +26 -15
  67. package/dist/cli/commands/posteval-plan.js.map +1 -1
  68. package/dist/cli/commands/release.d.ts +6 -1
  69. package/dist/cli/commands/release.d.ts.map +1 -1
  70. package/dist/cli/commands/release.js +69 -59
  71. package/dist/cli/commands/release.js.map +1 -1
  72. package/dist/cli/commands/report.d.ts +2 -1
  73. package/dist/cli/commands/report.d.ts.map +1 -1
  74. package/dist/cli/commands/report.js +9 -5
  75. package/dist/cli/commands/report.js.map +1 -1
  76. package/dist/cli/commands/review.d.ts +3 -2
  77. package/dist/cli/commands/review.d.ts.map +1 -1
  78. package/dist/cli/commands/review.js +33 -29
  79. package/dist/cli/commands/review.js.map +1 -1
  80. package/dist/cli/commands/sdd.d.ts +2 -1
  81. package/dist/cli/commands/sdd.d.ts.map +1 -1
  82. package/dist/cli/commands/sdd.js +7 -5
  83. package/dist/cli/commands/sdd.js.map +1 -1
  84. package/dist/cli/commands/security.d.ts +3 -2
  85. package/dist/cli/commands/security.d.ts.map +1 -1
  86. package/dist/cli/commands/security.js +29 -26
  87. package/dist/cli/commands/security.js.map +1 -1
  88. package/dist/cli/commands/spec-baseline-check.d.ts +2 -1
  89. package/dist/cli/commands/spec-baseline-check.d.ts.map +1 -1
  90. package/dist/cli/commands/spec-baseline-check.js +15 -11
  91. package/dist/cli/commands/spec-baseline-check.js.map +1 -1
  92. package/dist/cli/commands/spec.d.ts +7 -2
  93. package/dist/cli/commands/spec.d.ts.map +1 -1
  94. package/dist/cli/commands/spec.js +61 -51
  95. package/dist/cli/commands/spec.js.map +1 -1
  96. package/dist/cli/commands/status.d.ts +2 -1
  97. package/dist/cli/commands/status.d.ts.map +1 -1
  98. package/dist/cli/commands/status.js +57 -53
  99. package/dist/cli/commands/status.js.map +1 -1
  100. package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
  101. package/dist/cli/commands/sync-docs/agents-syncer.js +5 -2
  102. package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
  103. package/dist/cli/commands/sync-docs/context-syncer.d.ts.map +1 -1
  104. package/dist/cli/commands/sync-docs/context-syncer.js +3 -2
  105. package/dist/cli/commands/sync-docs/context-syncer.js.map +1 -1
  106. package/dist/cli/commands/sync-docs/index.d.ts +2 -1
  107. package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
  108. package/dist/cli/commands/sync-docs/index.js +68 -88
  109. package/dist/cli/commands/sync-docs/index.js.map +1 -1
  110. package/dist/cli/commands/sync-docs/project-reader.d.ts +6 -1
  111. package/dist/cli/commands/sync-docs/project-reader.d.ts.map +1 -1
  112. package/dist/cli/commands/sync-docs/project-reader.js +14 -16
  113. package/dist/cli/commands/sync-docs/project-reader.js.map +1 -1
  114. package/dist/cli/commands/update-user-model.d.ts +2 -1
  115. package/dist/cli/commands/update-user-model.d.ts.map +1 -1
  116. package/dist/cli/commands/update-user-model.js +31 -45
  117. package/dist/cli/commands/update-user-model.js.map +1 -1
  118. package/dist/cli/commands/validate.d.ts +4 -3
  119. package/dist/cli/commands/validate.d.ts.map +1 -1
  120. package/dist/cli/commands/validate.js +28 -26
  121. package/dist/cli/commands/validate.js.map +1 -1
  122. package/dist/cli/session-mining/transcript.d.ts +7 -1
  123. package/dist/cli/session-mining/transcript.d.ts.map +1 -1
  124. package/dist/cli/session-mining/transcript.js +21 -14
  125. package/dist/cli/session-mining/transcript.js.map +1 -1
  126. package/dist/context/session-manager.d.ts.map +1 -1
  127. package/dist/context/session-manager.js +19 -32
  128. package/dist/context/session-manager.js.map +1 -1
  129. package/dist/core/constraints/agent-prompt-renderer.d.ts.map +1 -1
  130. package/dist/core/constraints/agent-prompt-renderer.js +1 -4
  131. package/dist/core/constraints/agent-prompt-renderer.js.map +1 -1
  132. package/dist/core/constraints/capabilities-parser.d.ts +24 -7
  133. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  134. package/dist/core/constraints/capabilities-parser.js +32 -28
  135. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  136. package/dist/core/constraints/capabilities-reconcile.d.ts +67 -0
  137. package/dist/core/constraints/capabilities-reconcile.d.ts.map +1 -0
  138. package/dist/core/constraints/capabilities-reconcile.js +105 -0
  139. package/dist/core/constraints/capabilities-reconcile.js.map +1 -0
  140. package/dist/core/constraints/checker.d.ts +21 -43
  141. package/dist/core/constraints/checker.d.ts.map +1 -1
  142. package/dist/core/constraints/checker.js +64 -115
  143. package/dist/core/constraints/checker.js.map +1 -1
  144. package/dist/core/constraints/checkers/capability-sync.d.ts +4 -3
  145. package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
  146. package/dist/core/constraints/checkers/capability-sync.js +29 -69
  147. package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
  148. package/dist/core/constraints/checkers/context-doc-sync.d.ts +4 -2
  149. package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
  150. package/dist/core/constraints/checkers/context-doc-sync.js +13 -22
  151. package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
  152. package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
  153. package/dist/core/constraints/checkers/docs-freshness.js +37 -63
  154. package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
  155. package/dist/core/constraints/checkers/governance-presence.d.ts +2 -2
  156. package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
  157. package/dist/core/constraints/checkers/governance-presence.js +7 -28
  158. package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
  159. package/dist/core/constraints/checkers/no-test-simplification.d.ts.map +1 -1
  160. package/dist/core/constraints/checkers/no-test-simplification.js +4 -3
  161. package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
  162. package/dist/core/constraints/checkers/types.d.ts +2 -2
  163. package/dist/core/constraints/context-builder.d.ts +15 -14
  164. package/dist/core/constraints/context-builder.d.ts.map +1 -1
  165. package/dist/core/constraints/context-builder.js +32 -96
  166. package/dist/core/constraints/context-builder.js.map +1 -1
  167. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  168. package/dist/core/constraints/doc-freshness/runner.js +2 -1
  169. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  170. package/dist/core/constraints/git-evidence.d.ts +60 -0
  171. package/dist/core/constraints/git-evidence.d.ts.map +1 -0
  172. package/dist/core/constraints/git-evidence.js +88 -0
  173. package/dist/core/constraints/git-evidence.js.map +1 -0
  174. package/dist/core/constraints/injection-drift.d.ts +3 -20
  175. package/dist/core/constraints/injection-drift.d.ts.map +1 -1
  176. package/dist/core/constraints/injection-drift.js +10 -57
  177. package/dist/core/constraints/injection-drift.js.map +1 -1
  178. package/dist/core/constraints/injection-writer.d.ts +113 -0
  179. package/dist/core/constraints/injection-writer.d.ts.map +1 -0
  180. package/dist/core/constraints/injection-writer.js +228 -0
  181. package/dist/core/constraints/injection-writer.js.map +1 -0
  182. package/dist/core/constraints/usage-report.d.ts +22 -1
  183. package/dist/core/constraints/usage-report.d.ts.map +1 -1
  184. package/dist/core/constraints/usage-report.js +27 -20
  185. package/dist/core/constraints/usage-report.js.map +1 -1
  186. package/dist/core/index.d.ts +2 -2
  187. package/dist/core/index.d.ts.map +1 -1
  188. package/dist/core/index.js +4 -2
  189. package/dist/core/index.js.map +1 -1
  190. package/dist/core/project-config-loader.d.ts +32 -1
  191. package/dist/core/project-config-loader.d.ts.map +1 -1
  192. package/dist/core/project-config-loader.js +39 -9
  193. package/dist/core/project-config-loader.js.map +1 -1
  194. package/dist/core/validators/passes-gate.d.ts +15 -64
  195. package/dist/core/validators/passes-gate.d.ts.map +1 -1
  196. package/dist/core/validators/passes-gate.js +84 -222
  197. package/dist/core/validators/passes-gate.js.map +1 -1
  198. package/dist/core/validators/test-output.d.ts +58 -0
  199. package/dist/core/validators/test-output.d.ts.map +1 -0
  200. package/dist/core/validators/test-output.js +109 -0
  201. package/dist/core/validators/test-output.js.map +1 -0
  202. package/dist/failure/recorder.d.ts +6 -0
  203. package/dist/failure/recorder.d.ts.map +1 -1
  204. package/dist/failure/recorder.js +12 -8
  205. package/dist/failure/recorder.js.map +1 -1
  206. package/dist/gates/acceptance.d.ts +1 -48
  207. package/dist/gates/acceptance.d.ts.map +1 -1
  208. package/dist/gates/acceptance.js +6 -38
  209. package/dist/gates/acceptance.js.map +1 -1
  210. package/dist/gates/checker-gate.d.ts.map +1 -1
  211. package/dist/gates/checker-gate.js +6 -11
  212. package/dist/gates/checker-gate.js.map +1 -1
  213. package/dist/gates/command.d.ts.map +1 -1
  214. package/dist/gates/command.js +10 -22
  215. package/dist/gates/command.js.map +1 -1
  216. package/dist/gates/contract.d.ts.map +1 -1
  217. package/dist/gates/contract.js +22 -61
  218. package/dist/gates/contract.js.map +1 -1
  219. package/dist/gates/performance.d.ts.map +1 -1
  220. package/dist/gates/performance.js +13 -31
  221. package/dist/gates/performance.js.map +1 -1
  222. package/dist/gates/review.d.ts.map +1 -1
  223. package/dist/gates/review.js +21 -53
  224. package/dist/gates/review.js.map +1 -1
  225. package/dist/gates/security.d.ts.map +1 -1
  226. package/dist/gates/security.js +14 -32
  227. package/dist/gates/security.js.map +1 -1
  228. package/dist/gates/types.d.ts +28 -5
  229. package/dist/gates/types.d.ts.map +1 -1
  230. package/dist/gates/types.js +38 -1
  231. package/dist/gates/types.js.map +1 -1
  232. package/dist/hooks/bootstrap.d.ts +2 -0
  233. package/dist/hooks/bootstrap.d.ts.map +1 -1
  234. package/dist/hooks/bootstrap.js +5 -2
  235. package/dist/hooks/bootstrap.js.map +1 -1
  236. package/dist/index.d.ts +1 -2
  237. package/dist/index.d.ts.map +1 -1
  238. package/dist/index.js.map +1 -1
  239. package/dist/knowledge/audit.d.ts +7 -2
  240. package/dist/knowledge/audit.d.ts.map +1 -1
  241. package/dist/knowledge/audit.js +59 -79
  242. package/dist/knowledge/audit.js.map +1 -1
  243. package/dist/knowledge/flywheel-metrics.d.ts +39 -0
  244. package/dist/knowledge/flywheel-metrics.d.ts.map +1 -0
  245. package/dist/knowledge/flywheel-metrics.js +47 -0
  246. package/dist/knowledge/flywheel-metrics.js.map +1 -0
  247. package/dist/knowledge/import.d.ts.map +1 -1
  248. package/dist/knowledge/import.js +4 -14
  249. package/dist/knowledge/import.js.map +1 -1
  250. package/dist/knowledge/index-generator.d.ts.map +1 -1
  251. package/dist/knowledge/index-generator.js +17 -14
  252. package/dist/knowledge/index-generator.js.map +1 -1
  253. package/dist/knowledge/ingest.d.ts.map +1 -1
  254. package/dist/knowledge/ingest.js +9 -4
  255. package/dist/knowledge/ingest.js.map +1 -1
  256. package/dist/knowledge/migration.d.ts.map +1 -1
  257. package/dist/knowledge/migration.js +11 -8
  258. package/dist/knowledge/migration.js.map +1 -1
  259. package/dist/knowledge/reference-tracker.d.ts.map +1 -1
  260. package/dist/knowledge/reference-tracker.js +12 -9
  261. package/dist/knowledge/reference-tracker.js.map +1 -1
  262. package/dist/knowledge/store.d.ts +15 -0
  263. package/dist/knowledge/store.d.ts.map +1 -1
  264. package/dist/knowledge/store.js +59 -10
  265. package/dist/knowledge/store.js.map +1 -1
  266. package/dist/monitoring/context-tracker.d.ts.map +1 -1
  267. package/dist/monitoring/context-tracker.js +11 -20
  268. package/dist/monitoring/context-tracker.js.map +1 -1
  269. package/dist/monitoring/index.d.ts +1 -1
  270. package/dist/monitoring/index.d.ts.map +1 -1
  271. package/dist/monitoring/index.js +1 -2
  272. package/dist/monitoring/index.js.map +1 -1
  273. package/dist/monitoring/trace-analyzer.d.ts +15 -0
  274. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  275. package/dist/monitoring/trace-analyzer.js +19 -2
  276. package/dist/monitoring/trace-analyzer.js.map +1 -1
  277. package/dist/monitoring/traces.d.ts +21 -2
  278. package/dist/monitoring/traces.d.ts.map +1 -1
  279. package/dist/monitoring/traces.js +38 -29
  280. package/dist/monitoring/traces.js.map +1 -1
  281. package/dist/release/index.d.ts +2 -2
  282. package/dist/release/index.d.ts.map +1 -1
  283. package/dist/release/index.js +1 -5
  284. package/dist/release/index.js.map +1 -1
  285. package/dist/sdd/index-generator.js +10 -11
  286. package/dist/sdd/index-generator.js.map +1 -1
  287. package/dist/test-setup/project-fixture.d.ts +48 -0
  288. package/dist/test-setup/project-fixture.d.ts.map +1 -0
  289. package/dist/test-setup/project-fixture.js +106 -0
  290. package/dist/test-setup/project-fixture.js.map +1 -0
  291. package/dist/types/constraint.d.ts +0 -18
  292. package/dist/types/constraint.d.ts.map +1 -1
  293. package/dist/types/constraint.js.map +1 -1
  294. package/dist/types/passes-gate.d.ts +10 -25
  295. package/dist/types/passes-gate.d.ts.map +1 -1
  296. package/dist/types/project-config.d.ts +1 -1
  297. package/dist/types/session.d.ts +6 -5
  298. package/dist/types/session.d.ts.map +1 -1
  299. package/dist/utils/exec.d.ts +10 -0
  300. package/dist/utils/exec.d.ts.map +1 -1
  301. package/dist/utils/exec.js +12 -0
  302. package/dist/utils/exec.js.map +1 -1
  303. package/dist/utils/file-walk.d.ts +9 -1
  304. package/dist/utils/file-walk.d.ts.map +1 -1
  305. package/dist/utils/file-walk.js +10 -1
  306. package/dist/utils/file-walk.js.map +1 -1
  307. package/dist/utils/frontmatter.d.ts +73 -0
  308. package/dist/utils/frontmatter.d.ts.map +1 -0
  309. package/dist/utils/frontmatter.js +141 -0
  310. package/dist/utils/frontmatter.js.map +1 -0
  311. package/dist/utils/jsonl.d.ts +92 -0
  312. package/dist/utils/jsonl.d.ts.map +1 -0
  313. package/dist/utils/jsonl.js +166 -0
  314. package/dist/utils/jsonl.js.map +1 -0
  315. package/dist/utils/package-version.d.ts +16 -0
  316. package/dist/utils/package-version.d.ts.map +1 -0
  317. package/dist/utils/package-version.js +63 -0
  318. package/dist/utils/package-version.js.map +1 -0
  319. package/package.json +1 -1
  320. package/src/CONTEXT.md +7 -2
  321. package/src/__tests__/acceptance-gate.test.ts +3 -1
  322. package/src/__tests__/checker-extra.test.ts +7 -1218
  323. package/src/__tests__/context-files-resolution.test.ts +116 -0
  324. package/src/__tests__/effective-constraints.test.ts +10 -13
  325. package/src/__tests__/exec.test.ts +35 -1
  326. package/src/__tests__/layering.test.ts +152 -0
  327. package/src/__tests__/passes-gate-extra.test.ts +0 -145
  328. package/src/__tests__/passes-gate.test.ts +142 -106
  329. package/src/__tests__/skip-semantics.test.ts +5 -9
  330. package/src/cli/__tests__/command-contract.test.ts +170 -0
  331. package/src/cli/command-contract.ts +108 -0
  332. package/src/cli/commands/CONTEXT.md +22 -7
  333. package/src/cli/commands/__tests__/acceptance.test.ts +40 -33
  334. package/src/cli/commands/__tests__/analyze-sessions.test.ts +35 -29
  335. package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +101 -0
  336. package/src/cli/commands/__tests__/check-drift.test.ts +27 -34
  337. package/src/cli/commands/__tests__/check-skip-output.test.ts +21 -24
  338. package/src/cli/commands/__tests__/check.test.ts +378 -382
  339. package/src/cli/commands/__tests__/command.test.ts +85 -70
  340. package/src/cli/commands/__tests__/constraints-report.test.ts +100 -71
  341. package/src/cli/commands/__tests__/constraints-retire.test.ts +152 -95
  342. package/src/cli/commands/__tests__/contract.test.ts +27 -34
  343. package/src/cli/commands/__tests__/failure.test.ts +31 -34
  344. package/src/cli/commands/__tests__/init-injection.test.ts +58 -28
  345. package/src/cli/commands/__tests__/init.test.ts +77 -48
  346. package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +108 -0
  347. package/src/cli/commands/__tests__/knowledge.test.ts +69 -43
  348. package/src/cli/commands/__tests__/passes-gate.test.ts +43 -45
  349. package/src/cli/commands/__tests__/performance.test.ts +22 -23
  350. package/src/cli/commands/__tests__/posteval-plan.test.ts +39 -38
  351. package/src/cli/commands/__tests__/project-path-anchoring.test.ts +157 -0
  352. package/src/cli/commands/__tests__/project-path-convention.test.ts +169 -0
  353. package/src/cli/commands/__tests__/registry.test.ts +69 -35
  354. package/src/cli/commands/__tests__/release.test.ts +209 -80
  355. package/src/cli/commands/__tests__/report-extra.test.ts +16 -16
  356. package/src/cli/commands/__tests__/report.test.ts +25 -25
  357. package/src/cli/commands/__tests__/review.test.ts +33 -31
  358. package/src/cli/commands/__tests__/security.test.ts +23 -27
  359. package/src/cli/commands/__tests__/spec-baseline-check.test.ts +20 -20
  360. package/src/cli/commands/__tests__/spec.test.ts +41 -29
  361. package/src/cli/commands/__tests__/status-extra.test.ts +18 -18
  362. package/src/cli/commands/__tests__/status-skipped-lines.test.ts +72 -0
  363. package/src/cli/commands/__tests__/status.test.ts +34 -34
  364. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +77 -78
  365. package/src/cli/commands/__tests__/sync-docs.test.ts +72 -74
  366. package/src/cli/commands/__tests__/update-user-model.test.ts +62 -33
  367. package/src/cli/commands/__tests__/validate.test.ts +27 -29
  368. package/src/cli/commands/acceptance.ts +35 -24
  369. package/src/cli/commands/analyze-sessions.ts +25 -24
  370. package/src/cli/commands/check.ts +88 -65
  371. package/src/cli/commands/command.ts +30 -17
  372. package/src/cli/commands/constraints-report.ts +37 -34
  373. package/src/cli/commands/constraints-retire.ts +58 -55
  374. package/src/cli/commands/constraints.ts +10 -27
  375. package/src/cli/commands/contract.ts +51 -42
  376. package/src/cli/commands/definitions.ts +36 -222
  377. package/src/cli/commands/failure.ts +33 -26
  378. package/src/cli/commands/init.ts +166 -220
  379. package/src/cli/commands/knowledge.ts +225 -203
  380. package/src/cli/commands/passes-gate.ts +58 -68
  381. package/src/cli/commands/performance.ts +23 -18
  382. package/src/cli/commands/posteval-plan.ts +29 -15
  383. package/src/cli/commands/release.ts +70 -59
  384. package/src/cli/commands/report.ts +9 -5
  385. package/src/cli/commands/review.ts +39 -29
  386. package/src/cli/commands/sdd.ts +7 -5
  387. package/src/cli/commands/security.ts +35 -26
  388. package/src/cli/commands/spec-baseline-check.ts +16 -11
  389. package/src/cli/commands/spec.ts +66 -53
  390. package/src/cli/commands/status.ts +59 -53
  391. package/src/cli/commands/sync-docs/agents-syncer.ts +5 -2
  392. package/src/cli/commands/sync-docs/context-syncer.ts +3 -2
  393. package/src/cli/commands/sync-docs/index.ts +73 -100
  394. package/src/cli/commands/sync-docs/project-reader.ts +17 -18
  395. package/src/cli/commands/update-user-model.ts +33 -45
  396. package/src/cli/commands/validate.ts +31 -25
  397. package/src/cli/session-mining/__tests__/transcript.test.ts +118 -0
  398. package/src/cli/session-mining/transcript.ts +26 -8
  399. package/src/context/session-manager.ts +19 -33
  400. package/src/core/CONTEXT.md +9 -6
  401. package/src/core/constraints/__tests__/capabilities-reconcile.test.ts +200 -0
  402. package/src/core/constraints/__tests__/capability-counts.test.ts +94 -0
  403. package/src/core/constraints/__tests__/git-evidence.test.ts +208 -0
  404. package/src/core/constraints/__tests__/injection-drift.test.ts +3 -10
  405. package/src/core/constraints/__tests__/injection-writer.test.ts +231 -0
  406. package/src/core/constraints/__tests__/trace-injection.test.ts +71 -0
  407. package/src/core/constraints/__tests__/usage-report.test.ts +112 -0
  408. package/src/core/constraints/agent-prompt-renderer.ts +2 -6
  409. package/src/core/constraints/capabilities-parser.ts +53 -35
  410. package/src/core/constraints/capabilities-reconcile.ts +166 -0
  411. package/src/core/constraints/checker.ts +75 -130
  412. package/src/core/constraints/checkers/__tests__/capability-sync.test.ts +214 -0
  413. package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +77 -0
  414. package/src/core/constraints/checkers/__tests__/docs-freshness.test.ts +117 -0
  415. package/src/{__tests__ → core/constraints/checkers/__tests__}/governance-presence.test.ts +83 -9
  416. package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +50 -0
  417. package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +76 -0
  418. package/src/{__tests__ → core/constraints/checkers/__tests__}/no-hardcoded-credentials.test.ts +4 -3
  419. package/src/core/constraints/checkers/__tests__/no-test-simplification.test.ts +58 -0
  420. package/src/core/constraints/checkers/__tests__/three-state-consistency.test.ts +145 -0
  421. package/src/core/constraints/checkers/capability-sync.ts +29 -88
  422. package/src/core/constraints/checkers/context-doc-sync.ts +15 -25
  423. package/src/core/constraints/checkers/docs-freshness.ts +40 -69
  424. package/src/core/constraints/checkers/governance-presence.ts +7 -25
  425. package/src/core/constraints/checkers/no-test-simplification.ts +4 -3
  426. package/src/core/constraints/checkers/types.ts +2 -2
  427. package/src/core/constraints/context-builder.ts +39 -91
  428. package/src/core/constraints/doc-freshness/runner.ts +2 -1
  429. package/src/core/constraints/git-evidence.ts +120 -0
  430. package/src/core/constraints/injection-drift.ts +13 -60
  431. package/src/core/constraints/injection-writer.ts +212 -0
  432. package/src/core/constraints/usage-report.ts +36 -17
  433. package/src/core/index.ts +4 -2
  434. package/src/core/project-config-loader.ts +49 -9
  435. package/src/core/validators/__tests__/passes-gate.test.ts +2 -56
  436. package/src/core/validators/__tests__/test-output.test.ts +372 -0
  437. package/src/core/validators/passes-gate.ts +86 -248
  438. package/src/core/validators/test-output.ts +128 -0
  439. package/src/failure/__tests__/recorder.test.ts +168 -0
  440. package/src/failure/recorder.ts +12 -10
  441. package/src/gates/CONTEXT.md +6 -3
  442. package/src/gates/__tests__/acceptance-types-single-source.test.ts +68 -0
  443. package/src/gates/__tests__/acceptance.test.ts +4 -86
  444. package/src/gates/__tests__/effective-gates.test.ts +3 -8
  445. package/src/gates/__tests__/test-verdict-consistency.test.ts +140 -0
  446. package/src/gates/acceptance.ts +19 -84
  447. package/src/gates/checker-gate.ts +8 -7
  448. package/src/gates/command.ts +10 -16
  449. package/src/gates/contract.ts +22 -61
  450. package/src/gates/performance.ts +10 -22
  451. package/src/gates/review.ts +21 -41
  452. package/src/gates/security.ts +10 -22
  453. package/src/gates/types.ts +70 -5
  454. package/src/hooks/CONTEXT.md +1 -1
  455. package/src/hooks/bootstrap.ts +5 -2
  456. package/src/index.ts +0 -4
  457. package/src/knowledge/CONTEXT.md +8 -3
  458. package/src/knowledge/__tests__/audit-labels.test.ts +44 -0
  459. package/src/knowledge/__tests__/flywheel-metrics.test.ts +135 -0
  460. package/src/knowledge/__tests__/import.test.ts +44 -0
  461. package/src/knowledge/__tests__/index-generator.test.ts +53 -0
  462. package/src/knowledge/__tests__/ingest.test.ts +85 -0
  463. package/src/knowledge/__tests__/migration.test.ts +47 -0
  464. package/src/knowledge/__tests__/reference-tracker.test.ts +16 -0
  465. package/src/knowledge/__tests__/store.test.ts +194 -0
  466. package/src/knowledge/audit.ts +67 -71
  467. package/src/knowledge/flywheel-metrics.ts +71 -0
  468. package/src/knowledge/import.ts +4 -18
  469. package/src/knowledge/index-generator.ts +19 -14
  470. package/src/knowledge/ingest.ts +9 -4
  471. package/src/knowledge/migration.ts +11 -8
  472. package/src/knowledge/reference-tracker.ts +12 -8
  473. package/src/knowledge/store.ts +64 -11
  474. package/src/monitoring/CONTEXT.md +8 -3
  475. package/src/monitoring/__tests__/skipped-lines-report.test.ts +178 -0
  476. package/src/monitoring/__tests__/traces.test.ts +57 -0
  477. package/src/monitoring/context-tracker.ts +11 -23
  478. package/src/monitoring/index.ts +1 -1
  479. package/src/monitoring/trace-analyzer.ts +21 -2
  480. package/src/monitoring/traces.ts +39 -36
  481. package/src/release/CONTEXT.md +1 -0
  482. package/src/release/__tests__/barrel.test.ts +60 -0
  483. package/src/release/index.ts +2 -13
  484. package/src/sdd/__tests__/index-generator.test.ts +71 -0
  485. package/src/sdd/index-generator.ts +9 -9
  486. package/src/test-setup/__tests__/project-fixture.test.ts +178 -0
  487. package/src/test-setup/project-fixture.ts +87 -0
  488. package/src/tools/CONTEXT.md +6 -0
  489. package/src/tools/__tests__/registry.test.ts +221 -0
  490. package/src/types/__tests__/types.test.ts +4 -5
  491. package/src/types/constraint.ts +0 -25
  492. package/src/types/passes-gate.ts +10 -30
  493. package/src/types/project-config.ts +1 -1
  494. package/src/types/session.ts +5 -5
  495. package/src/utils/__tests__/frontmatter.test.ts +206 -0
  496. package/src/utils/__tests__/jsonl-skip-disposition.test.ts +175 -0
  497. package/src/utils/__tests__/jsonl.test.ts +187 -0
  498. package/src/utils/__tests__/package-version.test.ts +97 -0
  499. package/src/utils/exec.ts +15 -0
  500. package/src/utils/file-walk.ts +11 -2
  501. package/src/utils/frontmatter.ts +121 -0
  502. package/src/utils/jsonl.ts +167 -0
  503. package/src/utils/package-version.ts +27 -0
  504. package/dist/cli/commands/doc-freshness-check.d.ts +0 -53
  505. package/dist/cli/commands/doc-freshness-check.d.ts.map +0 -1
  506. package/dist/cli/commands/doc-freshness-check.js +0 -352
  507. package/dist/cli/commands/doc-freshness-check.js.map +0 -1
  508. package/dist/types/index.d.ts +0 -14
  509. package/dist/types/index.d.ts.map +0 -1
  510. package/dist/types/index.js +0 -39
  511. package/dist/types/index.js.map +0 -1
  512. package/src/__tests__/long-running.test.ts +0 -17
  513. package/src/__tests__/passes-gate-extension.test.ts +0 -151
  514. package/src/cli/commands/__tests__/doc-freshness-check.test.ts +0 -168
  515. package/src/cli/commands/doc-freshness-check.ts +0 -386
  516. package/src/spec/CONTEXT.md +0 -11
  517. package/src/types/index.ts +0 -47
@@ -1,18 +1,19 @@
1
1
  /**
2
- * checker.ts 补充测试
3
- *
4
- * 目标:覆盖 checkCapabilitySync 完整流程、beforeExecution、findApplicableConstraints
2
+ * checker.ts 补充测试(engine 流程面:findApplicable/beforeExecution/快捷函数/缓存契约)
3
+ *
4
+ * 单个检查器的判定测试不放这里——在 checkers/__tests__/ 旁测,测 evaluate(env) interface
5
+ * (ADR-0009 / 架构评审候选4;全部 10 个已注册 checker 旁测就位)。
5
6
  * (ADR-0001:已退役 checker 的用例随约束数据模型 v2 移除)
6
7
  */
7
8
 
8
- import { describe, it, expect, beforeAll, afterAll, jest } from '@jest/globals';
9
+ import { describe, it, expect, beforeAll, afterAll } from '@jest/globals';
9
10
  import {
10
11
  ConstraintChecker,
11
12
  checkConstraint,
12
13
  checkConstraints,
13
14
  checkBeforeExecution,
14
15
  } from '../core/constraints/checker';
15
- import { getConstraintCheck, buildCheckEnv, type CheckEnv } from '../core/constraints/checkers';
16
+ import { buildCheckEnv } from '../core/constraints/checkers';
16
17
  import { contextEvidenceFlag } from '../core/constraints/checkers/types';
17
18
  import type { ConstraintContext } from '../types/constraint';
18
19
  import * as fs from 'fs';
@@ -44,433 +45,6 @@ describe('ConstraintChecker - 补充覆盖', () => {
44
45
  // ignore
45
46
  }
46
47
  });
47
-
48
- describe('checkCapabilitySync', () => {
49
- it('无代码变更应该通过', async () => {
50
- // 清理 staged changes
51
- execSync('git reset HEAD', { cwd: tempDir, stdio: 'pipe' });
52
-
53
- const context: ConstraintContext = {
54
- operation: 'commit',
55
- projectPath: tempDir,
56
- };
57
-
58
- const result = await checker.check(
59
- {
60
- id: 'capability_sync',
61
- kind: 'check',
62
- level: 'guideline',
63
- rule: 'CAPABILITY SYNC',
64
- message: 'test',
65
- trigger: 'commit',
66
- enforcement: 'test',
67
- },
68
- context
69
- );
70
-
71
- expect(result.satisfied).toBe(true);
72
- });
73
-
74
- it('有代码变更且有 CAPABILITIES.md 应该通过', async () => {
75
- // 创建 CAPABILITIES.md(如果不存在)
76
- const capFile = path.join(tempDir, 'CAPABILITIES.md');
77
- fs.writeFileSync(capFile, '# Capabilities\n\n- Feature: test');
78
-
79
- // 创建代码文件并 stage
80
- const codeFile = path.join(tempDir, 'feature.ts');
81
- fs.writeFileSync(codeFile, 'export function feature() {}');
82
- execSync('git add .', { cwd: tempDir });
83
-
84
- const context: ConstraintContext = {
85
- operation: 'commit',
86
- projectPath: tempDir,
87
- };
88
-
89
- const result = await checker.check(
90
- {
91
- id: 'capability_sync',
92
- kind: 'check',
93
- level: 'guideline',
94
- rule: 'CAPABILITY SYNC',
95
- message: 'test',
96
- trigger: 'commit',
97
- enforcement: 'test',
98
- },
99
- context
100
- );
101
-
102
- expect(result.satisfied).toBe(true);
103
-
104
- // Cleanup
105
- execSync('git reset HEAD', { cwd: tempDir, stdio: 'pipe' });
106
- });
107
-
108
- it('有代码变更且 CAPABILITIES.md 有表格但缺少新模块应该失败', async () => {
109
- // 创建 CAPABILITIES.md,含表格但不包含新文件
110
- const capFile = path.join(tempDir, 'CAPABILITIES.md');
111
- fs.writeFileSync(capFile, '# Capabilities\n\n| 模块 | 文件 | 功能 |\n|------|------|------|\n| 旧模块 | old/module.ts | 旧功能 |');
112
-
113
- // 创建新的代码文件并 stage
114
- const codeFile = path.join(tempDir, 'new-module.ts');
115
- fs.writeFileSync(codeFile, 'export function newModule() {}');
116
- execSync('git add .', { cwd: tempDir, stdio: 'pipe' });
117
-
118
- const context: ConstraintContext = {
119
- operation: 'commit',
120
- projectPath: tempDir,
121
- };
122
-
123
- const result = await checker.check(
124
- {
125
- id: 'capability_sync',
126
- kind: 'check',
127
- level: 'guideline',
128
- rule: 'CAPABILITY SYNC',
129
- message: 'test',
130
- trigger: 'commit',
131
- enforcement: 'test',
132
- },
133
- context
134
- );
135
-
136
- // CAPABILITIES.md 有表格但新文件未被覆盖,应该失败
137
- expect(result.satisfied).toBe(false);
138
-
139
- // Cleanup
140
- execSync('git reset HEAD', { cwd: tempDir, stdio: 'pipe' });
141
- });
142
-
143
- it('有代码变更但无 CAPABILITIES.md 应该跳过(ADR-0001 存在性探测)', async () => {
144
- // 确保 CAPABILITIES.md 不存在
145
- const capFile = path.join(tempDir, 'CAPABILITIES.md');
146
- if (fs.existsSync(capFile)) {
147
- fs.rmSync(capFile, { force: true });
148
- }
149
-
150
- // 创建新的代码文件并 stage
151
- const newCodeFile = path.join(tempDir, 'newfeature.ts');
152
- fs.writeFileSync(newCodeFile, 'export function newfeature() {}');
153
- execSync('git add ' + newCodeFile, { cwd: tempDir });
154
-
155
- const context: ConstraintContext = {
156
- operation: 'commit',
157
- projectPath: tempDir,
158
- };
159
-
160
- const result = await checker.check(
161
- {
162
- id: 'capability_sync',
163
- kind: 'check',
164
- level: 'guideline',
165
- rule: 'CAPABILITY SYNC',
166
- message: 'test',
167
- trigger: 'commit',
168
- enforcement: 'test',
169
- },
170
- context
171
- );
172
-
173
- // 未采用 CAPABILITIES.md 约定 → skip(不计 pass/fail,不阻断)
174
- expect(result.skipped).toBe(true);
175
- expect(result.satisfied).toBe(true);
176
-
177
- // Cleanup
178
- execSync('git reset HEAD', { cwd: tempDir, stdio: 'pipe' });
179
- });
180
- });
181
-
182
- describe('checkCapabilitySync module 模式(governance.capabilities.mode)', () => {
183
- /** 建一个带 .harness/config.yml + CAPABILITIES.md + 源文件的临时项目 */
184
- const setupCapProject = (
185
- name: string,
186
- mode: 'file' | 'module' | undefined,
187
- capContent: string,
188
- files: string[]
189
- ): string => {
190
- const dir = path.join(tempDir, name);
191
- fs.mkdirSync(path.join(dir, '.harness'), { recursive: true });
192
- const yaml = require('js-yaml');
193
- const config = mode ? { governance: { capabilities: { mode } } } : {};
194
- fs.writeFileSync(path.join(dir, '.harness', 'config.yml'), yaml.dump(config));
195
- fs.writeFileSync(path.join(dir, 'CAPABILITIES.md'), capContent);
196
- for (const f of files) {
197
- const fp = path.join(dir, f);
198
- fs.mkdirSync(path.dirname(fp), { recursive: true });
199
- fs.writeFileSync(fp, 'export const x = 1;');
200
- }
201
- return dir;
202
- };
203
-
204
- const runCapCheck = (projectPath: string) =>
205
- checker.check(
206
- {
207
- id: 'capability_sync',
208
- kind: 'check',
209
- level: 'guideline',
210
- rule: 'CAPABILITY SYNC',
211
- message: 'test',
212
- trigger: 'commit',
213
- enforcement: 'test',
214
- },
215
- { operation: 'commit', projectPath }
216
- );
217
-
218
- beforeAll(() => {
219
- // 清理 staged 状态,隔离 step 1(git diff 增量)对本组用例的干扰
220
- try {
221
- execSync('git reset HEAD', { cwd: tempDir, stdio: 'pipe' });
222
- } catch {
223
- // ignore
224
- }
225
- });
226
-
227
- it('module 模式:目录条目覆盖源文件应该通过', async () => {
228
- const dir = setupCapProject(
229
- 'capmod-covered',
230
- 'module',
231
- '# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n| 核心 | src/core/ | 核心模块 |',
232
- ['src/core/foo.ts', 'src/core/bar.ts']
233
- );
234
-
235
- const result = await runCapCheck(dir);
236
- expect(result.satisfied).toBe(true);
237
-
238
- fs.rmSync(dir, { recursive: true, force: true });
239
- });
240
-
241
- it('module 模式:文件条目精确匹配也算覆盖', async () => {
242
- const dir = setupCapProject(
243
- 'capmod-file-entry',
244
- 'module',
245
- '# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n| foo | src/core/foo.ts | foo |',
246
- ['src/core/foo.ts']
247
- );
248
-
249
- const result = await runCapCheck(dir);
250
- expect(result.satisfied).toBe(true);
251
-
252
- fs.rmSync(dir, { recursive: true, force: true });
253
- });
254
-
255
- it('module 模式:新目录未登记应该失败', async () => {
256
- const dir = setupCapProject(
257
- 'capmod-uncovered',
258
- 'module',
259
- '# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n| 核心 | src/core/ | 核心模块 |',
260
- ['src/core/foo.ts', 'src/newdir/bar.ts']
261
- );
262
-
263
- const result = await runCapCheck(dir);
264
- expect(result.satisfied).toBe(false);
265
-
266
- fs.rmSync(dir, { recursive: true, force: true });
267
- });
268
-
269
- it('file 模式原行为不变:目录条目不参与覆盖判定', async () => {
270
- // file 模式下 step2 要求逐文件精确匹配,src/core/ 目录条目不能 rescue bar.ts
271
- const dir = setupCapProject(
272
- 'capfile-dir-no-rescue',
273
- 'file',
274
- '# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n| foo | src/core/foo.ts | foo |\n| 目录 | src/core/ | 目录条目 |',
275
- ['src/core/foo.ts', 'src/core/bar.ts']
276
- );
277
-
278
- const result = await runCapCheck(dir);
279
- expect(result.satisfied).toBe(false);
280
-
281
- fs.rmSync(dir, { recursive: true, force: true });
282
- });
283
-
284
- it('step2 全量扫描(file 模式):全部源文件已登记应该通过', async () => {
285
- const dir = setupCapProject(
286
- 'capfile-step2-ok',
287
- undefined,
288
- '# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n| foo | src/foo.ts | foo |',
289
- ['src/foo.ts']
290
- );
291
-
292
- const result = await runCapCheck(dir);
293
- expect(result.satisfied).toBe(true);
294
-
295
- fs.rmSync(dir, { recursive: true, force: true });
296
- });
297
-
298
- it('step2 全量扫描(file 模式):有未登记源文件应该失败', async () => {
299
- const dir = setupCapProject(
300
- 'capfile-step2-missing',
301
- undefined,
302
- '# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n| foo | src/foo.ts | foo |',
303
- ['src/foo.ts', 'src/unlisted.ts']
304
- );
305
-
306
- const result = await runCapCheck(dir);
307
- expect(result.satisfied).toBe(false);
308
-
309
- fs.rmSync(dir, { recursive: true, force: true });
310
- });
311
- });
312
-
313
- describe('checkCapabilitySync 遗留问题回归(2026-08-08 评估稿第四节)', () => {
314
- const capCheck = getConstraintCheck('capability_sync')!;
315
-
316
- const setupDir = (name: string, capContent: string, files: string[] = []): string => {
317
- const dir = path.join(tempDir, name);
318
- fs.mkdirSync(dir, { recursive: true });
319
- fs.writeFileSync(path.join(dir, 'CAPABILITIES.md'), capContent);
320
- for (const f of files) {
321
- const fp = path.join(dir, f);
322
- fs.mkdirSync(path.dirname(fp), { recursive: true });
323
- fs.writeFileSync(fp, 'export const x = 1;');
324
- }
325
- return dir;
326
- };
327
-
328
- /** 直接构造 CheckEnv:stub stagedDiffNames / srcScan,不走真实 git */
329
- const makeEnv = (
330
- projectPath: string,
331
- staged: string[],
332
- scan: Record<string, string[]> = {}
333
- ): CheckEnv => ({
334
- context: { operation: 'commit', projectPath },
335
- projectPath,
336
- stagedDiff: async () => '',
337
- stagedDiffNames: async () => staged.join('\n'),
338
- srcScan: (root: string) => scan[root] ?? [],
339
- });
340
-
341
- const tableWith = (row: string): string =>
342
- '# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n' + row + '\n';
343
-
344
- const EMPTY_TABLE = '# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n';
345
-
346
- it('问题1:step1 应要求每个变更文件都被覆盖(every 而非 some)', async () => {
347
- // 文档登记 src/foo.ts;同时 staged 已登记的 src/foo.ts 与未登记的 scripts/bar.ts
348
- // some() 下只要有 foo.ts 命中即整体通过,bar.ts 漏网
349
- const dir = setupDir('legacy-step1-some', tableWith('| foo | src/foo.ts | foo |'));
350
- try {
351
- expect(await capCheck.evaluate(makeEnv(dir, ['src/foo.ts', 'scripts/bar.ts']))).toBe(false);
352
- } finally {
353
- fs.rmSync(dir, { recursive: true, force: true });
354
- }
355
- });
356
-
357
- it('问题3a:step1 不得因 endsWith 造成后缀碰撞(xfoo.ts 不应被 foo.ts 覆盖)', async () => {
358
- const dir = setupDir('legacy-step1-endswith', tableWith('| foo | foo.ts | foo |'));
359
- try {
360
- expect(await capCheck.evaluate(makeEnv(dir, ['web/xfoo.ts']))).toBe(false);
361
- } finally {
362
- fs.rmSync(dir, { recursive: true, force: true });
363
- }
364
- });
365
-
366
- it('问题3b:step1 不得因 includes 造成子串误配(docs/src/foo.tsx 不应被 src/foo.ts 覆盖)', async () => {
367
- const dir = setupDir('legacy-step1-includes', tableWith('| foo | src/foo.ts | foo |'));
368
- try {
369
- expect(await capCheck.evaluate(makeEnv(dir, ['docs/src/foo.tsx']))).toBe(false);
370
- } finally {
371
- fs.rmSync(dir, { recursive: true, force: true });
372
- }
373
- });
374
-
375
- it('兼容:basename 条目仍可按路径边界后缀匹配(src/foo.ts 被 foo.ts 覆盖)', async () => {
376
- const dir = setupDir('legacy-step1-suffix-ok', tableWith('| foo | foo.ts | foo |'));
377
- try {
378
- expect(await capCheck.evaluate(makeEnv(dir, ['src/foo.ts']))).toBe(true);
379
- } finally {
380
- fs.rmSync(dir, { recursive: true, force: true });
381
- }
382
- });
383
-
384
- it('问题2:空表格 + 有 staged 变更时不得直接放行', async () => {
385
- const dir = setupDir('legacy-empty-table-diff', EMPTY_TABLE);
386
- try {
387
- expect(await capCheck.evaluate(makeEnv(dir, ['src/foo.ts']))).toBe(false);
388
- } finally {
389
- fs.rmSync(dir, { recursive: true, force: true });
390
- }
391
- });
392
-
393
- it('问题2:空表格 + 源码根有文件时 step2 不得直接放行', async () => {
394
- const dir = setupDir('legacy-empty-table-scan', EMPTY_TABLE, ['src/foo.ts']);
395
- try {
396
- expect(await capCheck.evaluate(makeEnv(dir, [], { src: ['src/foo.ts'] }))).toBe(false);
397
- } finally {
398
- fs.rmSync(dir, { recursive: true, force: true });
399
- }
400
- });
401
-
402
- it('问题2 兼容:清单格式(计数行)无文件表,仍直接放行', async () => {
403
- const dir = setupDir(
404
- 'legacy-listing-format',
405
- '# Harness Capabilities\n\n## CLI Commands (25)\ncheck, validate\n'
406
- );
407
- try {
408
- expect(await capCheck.evaluate(makeEnv(dir, ['src/foo.ts']))).toBe(true);
409
- } finally {
410
- fs.rmSync(dir, { recursive: true, force: true });
411
- }
412
- });
413
-
414
- it('问题4:检查异常默认放行时应输出 warn 而非静默吞错', async () => {
415
- const dir = setupDir('legacy-failopen', tableWith('| foo | src/foo.ts | foo |'));
416
- const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
417
- try {
418
- const env: CheckEnv = {
419
- ...makeEnv(dir, []),
420
- stagedDiffNames: async () => {
421
- throw new Error('git boom');
422
- },
423
- };
424
- // fail-open 语义保留:异常时仍放行
425
- expect(await capCheck.evaluate(env)).toBe(true);
426
- expect(warnSpy).toHaveBeenCalled();
427
- } finally {
428
- warnSpy.mockRestore();
429
- fs.rmSync(dir, { recursive: true, force: true });
430
- }
431
- });
432
-
433
- it('step2(file 模式)与 step1 语义一致:basename 条目可按路径边界后缀覆盖', async () => {
434
- // 文档第二列写 basename foo.ts;step1 认可它(边界后缀),
435
- // step2 裸精确匹配却永远不命中 srcScan 返回的 src/foo.ts → 永远红
436
- const dir = setupDir('legacy-step2-basename', tableWith('| foo | foo.ts | foo |'), [
437
- 'src/foo.ts',
438
- ]);
439
- try {
440
- expect(await capCheck.evaluate(makeEnv(dir, [], { src: ['src/foo.ts'] }))).toBe(true);
441
- } finally {
442
- fs.rmSync(dir, { recursive: true, force: true });
443
- }
444
- });
445
-
446
- it('step2(file 模式):不同路径的同后缀文件不得被误覆盖(lib/foo.ts 不覆盖 src/foo.ts)', async () => {
447
- const dir = setupDir('legacy-step2-boundary', tableWith('| foo | lib/foo.ts | foo |'), [
448
- 'src/foo.ts',
449
- ]);
450
- try {
451
- expect(await capCheck.evaluate(makeEnv(dir, [], { src: ['src/foo.ts'] }))).toBe(false);
452
- } finally {
453
- fs.rmSync(dir, { recursive: true, force: true });
454
- }
455
- });
456
-
457
- it('step2(module 模式)文件条目同样按路径边界后缀覆盖', async () => {
458
- const dir = setupDir('legacy-step2-module-suffix', tableWith('| foo | foo.ts | foo |'), [
459
- 'src/foo.ts',
460
- ]);
461
- fs.mkdirSync(path.join(dir, '.harness'), { recursive: true });
462
- fs.writeFileSync(
463
- path.join(dir, '.harness', 'config.yml'),
464
- 'governance:\n capabilities:\n mode: module\n'
465
- );
466
- try {
467
- expect(await capCheck.evaluate(makeEnv(dir, [], { src: ['src/foo.ts'] }))).toBe(true);
468
- } finally {
469
- fs.rmSync(dir, { recursive: true, force: true });
470
- }
471
- });
472
- });
473
-
474
48
  describe('findApplicableConstraints', () => {
475
49
  it('应该过滤出匹配 trigger 的约束', () => {
476
50
  const context: ConstraintContext = {
@@ -505,14 +79,10 @@ describe('ConstraintChecker - 补充覆盖', () => {
505
79
  operation: 'code_implementation',
506
80
  hasTest: true,
507
81
  hasVerificationEvidence: true,
508
- hasWorktree: true,
509
82
  hasRequirement: true,
510
83
  taskDescription: 'Test task — single focused change',
511
84
  hasSingleTask: true,
512
- hasRequirementReview: true,
513
- hasTwoStageReview: true,
514
- completionClaimText: 'All 142 tests passed, coverage 87.3%',
515
- } as any;
85
+ };
516
86
 
517
87
  // 不应该抛出异常
518
88
  await expect(checkBeforeExecution(context)).resolves.not.toThrow();
@@ -653,68 +223,6 @@ describe('ConstraintChecker - 补充覆盖', () => {
653
223
 
654
224
  });
655
225
 
656
- describe('checkNoBypassCheckpoint 文件内容检查', () => {
657
- it('changedFiles 包含 skip 内容应该失败', async () => {
658
- const bypassFile = path.join(tempDir, 'bypass-test.ts');
659
- fs.writeFileSync(bypassFile, 'test.skip("skipped", () => {});');
660
- execSync('git add .', { cwd: tempDir, stdio: 'pipe' });
661
-
662
- const context: ConstraintContext = {
663
- operation: 'code_implementation',
664
- projectPath: tempDir,
665
- changedFiles: [bypassFile],
666
- };
667
-
668
- const result = await checker.check(
669
- {
670
- id: 'no_bypass_checkpoint',
671
- kind: 'check',
672
- level: 'iron_law',
673
- rule: 'NO BYPASS',
674
- message: 'test',
675
- trigger: 'code_implementation',
676
- enforcement: 'test',
677
- },
678
- context
679
- );
680
-
681
- expect(result.satisfied).toBe(false);
682
-
683
- fs.unlinkSync(bypassFile);
684
- execSync('git reset HEAD -- ' + bypassFile, { cwd: tempDir, stdio: 'pipe' });
685
- });
686
-
687
- it('changedFiles 不包含 bypass 内容应该通过', async () => {
688
- const cleanFile = path.join(tempDir, 'clean-test.ts');
689
- fs.writeFileSync(cleanFile, 'test("clean", () => { expect(true).toBe(true); });');
690
- execSync('git add .', { cwd: tempDir, stdio: 'pipe' });
691
-
692
- const context: ConstraintContext = {
693
- operation: 'code_implementation',
694
- projectPath: tempDir,
695
- changedFiles: [cleanFile],
696
- };
697
-
698
- const result = await checker.check(
699
- {
700
- id: 'no_bypass_checkpoint',
701
- kind: 'check',
702
- level: 'iron_law',
703
- rule: 'NO BYPASS',
704
- message: 'test',
705
- trigger: 'code_implementation',
706
- enforcement: 'test',
707
- },
708
- context
709
- );
710
-
711
- expect(result.satisfied).toBe(true);
712
-
713
- fs.unlinkSync(cleanFile);
714
- execSync('git reset HEAD -- ' + cleanFile, { cwd: tempDir, stdio: 'pipe' });
715
- });
716
- });
717
-
718
226
  describe('checkConstraint 快捷函数', () => {
719
227
  it('未知约束应该返回不满足', async () => {
720
228
  const { checkConstraint } = await import('../core/constraints/checker');
@@ -753,725 +261,6 @@ describe('ConstraintChecker - 补充覆盖', () => {
753
261
  await expect(checkConstraints(context)).rejects.toThrow();
754
262
  });
755
263
  });
756
-
757
- describe('checkNoTestSimplification git diff 失败', () => {
758
- it('git diff 失败应该默认通过', async () => {
759
- // 非 git 目录
760
- const nonGitDir = path.join(tempDir, 'non-git');
761
- fs.mkdirSync(nonGitDir, { recursive: true });
762
-
763
- const context: ConstraintContext = {
764
- operation: 'test_creation',
765
- projectPath: nonGitDir,
766
- };
767
-
768
- const result = await checker.check(
769
- {
770
- id: 'no_test_simplification',
771
- kind: 'check',
772
- level: 'iron_law',
773
- rule: 'NO SIMPLIFY',
774
- message: 'test',
775
- trigger: 'test_creation',
776
- enforcement: 'test',
777
- },
778
- context
779
- );
780
-
781
- expect(result.satisfied).toBe(true);
782
-
783
- fs.rmSync(nonGitDir, { recursive: true, force: true });
784
- });
785
- });
786
-
787
- describe('checkContextDocSync', () => {
788
- it('无 governance 配置应该通过', async () => {
789
- // 无 .harness/config.yml
790
- const noConfigDir = path.join(tempDir, 'no-config');
791
- fs.mkdirSync(noConfigDir, { recursive: true });
792
-
793
- const context: ConstraintContext = {
794
- operation: 'module_modification',
795
- projectPath: noConfigDir,
796
- };
797
-
798
- const result = await checker.check(
799
- {
800
- id: 'context_doc_sync',
801
- kind: 'check',
802
- level: 'guideline',
803
- rule: 'CONTEXT DOC SYNC',
804
- message: 'test',
805
- trigger: 'module_modification',
806
- enforcement: 'test',
807
- },
808
- context
809
- );
810
-
811
- expect(result.satisfied).toBe(true);
812
-
813
- fs.rmSync(noConfigDir, { recursive: true, force: true });
814
- });
815
-
816
- it('有配置但 CONTEXT.md 存在应该通过', async () => {
817
- const configDir = path.join(tempDir, 'with-context');
818
- const harnessDir = path.join(configDir, '.harness');
819
- const srcDir = path.join(configDir, 'src');
820
- fs.mkdirSync(harnessDir, { recursive: true });
821
- fs.mkdirSync(srcDir, { recursive: true });
822
-
823
- // 写入 governance 配置
824
- const yaml = require('js-yaml');
825
- const config = {
826
- governance: {
827
- context_files: {
828
- enabled: true,
829
- required_dirs: ['src'],
830
- },
831
- },
832
- };
833
- fs.writeFileSync(path.join(harnessDir, 'config.yml'), yaml.dump(config));
834
-
835
- // 创建 CONTEXT.md
836
- fs.writeFileSync(path.join(srcDir, 'CONTEXT.md'), '# src\n\nTest context');
837
-
838
- const context: ConstraintContext = {
839
- operation: 'module_modification',
840
- projectPath: configDir,
841
- };
842
-
843
- const result = await checker.check(
844
- {
845
- id: 'context_doc_sync',
846
- kind: 'check',
847
- level: 'guideline',
848
- rule: 'CONTEXT DOC SYNC',
849
- message: 'test',
850
- trigger: 'module_modification',
851
- enforcement: 'test',
852
- },
853
- context
854
- );
855
-
856
- expect(result.satisfied).toBe(true);
857
-
858
- fs.rmSync(configDir, { recursive: true, force: true });
859
- });
860
-
861
- it('有配置但缺少 CONTEXT.md 应该失败', async () => {
862
- const configDir = path.join(tempDir, 'missing-context');
863
- const harnessDir = path.join(configDir, '.harness');
864
- const srcDir = path.join(configDir, 'src');
865
- fs.mkdirSync(harnessDir, { recursive: true });
866
- fs.mkdirSync(srcDir, { recursive: true });
867
-
868
- const yaml = require('js-yaml');
869
- const config = {
870
- governance: {
871
- context_files: {
872
- enabled: true,
873
- required_dirs: ['src'],
874
- },
875
- },
876
- };
877
- fs.writeFileSync(path.join(harnessDir, 'config.yml'), yaml.dump(config));
878
- // 不创建 CONTEXT.md
879
-
880
- const context: ConstraintContext = {
881
- operation: 'module_modification',
882
- projectPath: configDir,
883
- };
884
-
885
- const result = await checker.check(
886
- {
887
- id: 'context_doc_sync',
888
- kind: 'check',
889
- level: 'guideline',
890
- rule: 'CONTEXT DOC SYNC',
891
- message: 'test',
892
- trigger: 'module_modification',
893
- enforcement: 'test',
894
- },
895
- context
896
- );
897
-
898
- expect(result.satisfied).toBe(false);
899
-
900
- fs.rmSync(configDir, { recursive: true, force: true });
901
- });
902
-
903
- it('context_files.enabled 为 false 应该跳过', async () => {
904
- const configDir = path.join(tempDir, 'disabled-context');
905
- const harnessDir = path.join(configDir, '.harness');
906
- fs.mkdirSync(harnessDir, { recursive: true });
907
-
908
- const yaml = require('js-yaml');
909
- const config = {
910
- governance: {
911
- context_files: {
912
- enabled: false,
913
- required_dirs: ['src'],
914
- },
915
- },
916
- };
917
- fs.writeFileSync(path.join(harnessDir, 'config.yml'), yaml.dump(config));
918
-
919
- const context: ConstraintContext = {
920
- operation: 'module_modification',
921
- projectPath: configDir,
922
- };
923
-
924
- const result = await checker.check(
925
- {
926
- id: 'context_doc_sync',
927
- kind: 'check',
928
- level: 'guideline',
929
- rule: 'CONTEXT DOC SYNC',
930
- message: 'test',
931
- trigger: 'module_modification',
932
- enforcement: 'test',
933
- },
934
- context
935
- );
936
-
937
- expect(result.satisfied).toBe(true); // enabled: false → 跳过检查
938
-
939
- fs.rmSync(configDir, { recursive: true, force: true });
940
- });
941
- });
942
-
943
- describe('checkDocsFreshness', () => {
944
- it('无 CAPABILITIES.md 应该通过', async () => {
945
- const noCapDir = path.join(tempDir, 'no-cap-freshness');
946
- fs.mkdirSync(noCapDir, { recursive: true });
947
-
948
- const context: ConstraintContext = {
949
- operation: 'file_modification',
950
- projectPath: noCapDir,
951
- };
952
-
953
- const result = await checker.check(
954
- {
955
- id: 'docs_freshness',
956
- kind: 'check',
957
- level: 'guideline',
958
- rule: 'DOCS FRESHNESS',
959
- message: 'test',
960
- trigger: 'file_modification',
961
- enforcement: 'test',
962
- },
963
- context
964
- );
965
-
966
- expect(result.satisfied).toBe(true);
967
-
968
- fs.rmSync(noCapDir, { recursive: true, force: true });
969
- });
970
-
971
- it('CAPABILITIES.md 无表格应该通过', async () => {
972
- const capDir = path.join(tempDir, 'cap-no-table');
973
- fs.mkdirSync(capDir, { recursive: true });
974
- fs.writeFileSync(path.join(capDir, 'CAPABILITIES.md'), '# Capabilities\n\nNo table here.');
975
-
976
- const context: ConstraintContext = {
977
- operation: 'file_modification',
978
- projectPath: capDir,
979
- };
980
-
981
- const result = await checker.check(
982
- {
983
- id: 'docs_freshness',
984
- kind: 'check',
985
- level: 'guideline',
986
- rule: 'DOCS FRESHNESS',
987
- message: 'test',
988
- trigger: 'file_modification',
989
- enforcement: 'test',
990
- },
991
- context
992
- );
993
-
994
- expect(result.satisfied).toBe(true);
995
-
996
- fs.rmSync(capDir, { recursive: true, force: true });
997
- });
998
-
999
- it('CAPABILITIES.md 表格包含所有 src 文件应该通过', async () => {
1000
- const capDir = path.join(tempDir, 'cap-synced');
1001
- const srcDir = path.join(capDir, 'src');
1002
- fs.mkdirSync(srcDir, { recursive: true });
1003
-
1004
- // 创建源文件
1005
- fs.writeFileSync(path.join(srcDir, 'module.ts'), 'export const x = 1;');
1006
-
1007
- // 创建 CAPABILITIES.md,包含该文件
1008
- fs.writeFileSync(
1009
- path.join(capDir, 'CAPABILITIES.md'),
1010
- '# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n| module | src/module.ts | test |'
1011
- );
1012
-
1013
- const context: ConstraintContext = {
1014
- operation: 'file_modification',
1015
- projectPath: capDir,
1016
- };
1017
-
1018
- const result = await checker.check(
1019
- {
1020
- id: 'docs_freshness',
1021
- kind: 'check',
1022
- level: 'guideline',
1023
- rule: 'DOCS FRESHNESS',
1024
- message: 'test',
1025
- trigger: 'file_modification',
1026
- enforcement: 'test',
1027
- },
1028
- context
1029
- );
1030
-
1031
- expect(result.satisfied).toBe(true);
1032
-
1033
- fs.rmSync(capDir, { recursive: true, force: true });
1034
- });
1035
-
1036
- it('CAPABILITIES.md 列出的文件存在应该通过', async () => {
1037
- const capDir = path.join(tempDir, 'cap-valid');
1038
- const srcDir = path.join(capDir, 'src');
1039
- fs.mkdirSync(srcDir, { recursive: true });
1040
-
1041
- // 创建源文件
1042
- fs.writeFileSync(path.join(srcDir, 'old.ts'), 'export const x = 1;');
1043
- fs.writeFileSync(path.join(srcDir, 'new.ts'), 'export const y = 2;');
1044
-
1045
- // CAPABILITIES.md 包含 old.ts(存在)和 new.ts(也存在)
1046
- fs.writeFileSync(
1047
- path.join(capDir, 'CAPABILITIES.md'),
1048
- '# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n| old | src/old.ts | old |\n| new | src/new.ts | new |'
1049
- );
1050
-
1051
- const context: ConstraintContext = {
1052
- operation: 'file_modification',
1053
- projectPath: capDir,
1054
- };
1055
-
1056
- const result = await checker.check(
1057
- {
1058
- id: 'docs_freshness',
1059
- kind: 'check',
1060
- level: 'guideline',
1061
- rule: 'DOCS FRESHNESS',
1062
- message: 'test',
1063
- trigger: 'file_modification',
1064
- enforcement: 'test',
1065
- },
1066
- context
1067
- );
1068
-
1069
- expect(result.satisfied).toBe(true); // 列出的文件都存在 → 通过
1070
-
1071
- fs.rmSync(capDir, { recursive: true, force: true });
1072
- });
1073
-
1074
- it('CAPABILITIES.md 列出的文件已删除应该失败', async () => {
1075
- const capDir = path.join(tempDir, 'cap-ghost');
1076
- const srcDir = path.join(capDir, 'src');
1077
- fs.mkdirSync(srcDir, { recursive: true });
1078
-
1079
- // 只创建 old.ts,不创建 deleted.ts
1080
- fs.writeFileSync(path.join(srcDir, 'old.ts'), 'export const x = 1;');
1081
-
1082
- // CAPABILITIES.md 列出了已删除的 deleted.ts
1083
- fs.writeFileSync(
1084
- path.join(capDir, 'CAPABILITIES.md'),
1085
- '# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n| old | src/old.ts | old |\n| deleted | src/deleted.ts | deleted |'
1086
- );
1087
-
1088
- const context: ConstraintContext = {
1089
- operation: 'file_modification',
1090
- projectPath: capDir,
1091
- };
1092
-
1093
- const result = await checker.check(
1094
- {
1095
- id: 'docs_freshness',
1096
- kind: 'check',
1097
- level: 'guideline',
1098
- rule: 'DOCS FRESHNESS',
1099
- message: 'test',
1100
- trigger: 'file_modification',
1101
- enforcement: 'test',
1102
- },
1103
- context
1104
- );
1105
-
1106
- expect(result.satisfied).toBe(false); // deleted.ts 不存在 → 失败
1107
-
1108
- fs.rmSync(capDir, { recursive: true, force: true });
1109
- });
1110
-
1111
- // CLAUDE.md Domain Packages 检查
1112
- it('CLAUDE.md Domain Packages 包含不存在的目录应该失败', async () => {
1113
- const projDir = path.join(tempDir, 'claude-stale-pkg');
1114
- fs.mkdirSync(projDir, { recursive: true });
1115
- // 不创建 packages/studio-xxx 目录
1116
-
1117
- fs.writeFileSync(
1118
- path.join(projDir, 'CLAUDE.md'),
1119
- '## Domain Packages\n\n- `packages/studio-ghost` — Ghost package\n'
1120
- );
1121
-
1122
- const context: ConstraintContext = {
1123
- operation: 'code_implementation',
1124
- projectPath: projDir,
1125
- };
1126
-
1127
- const result = await checker.check(
1128
- {
1129
- id: 'docs_freshness',
1130
- kind: 'check',
1131
- level: 'guideline',
1132
- rule: 'DOCS FRESHNESS',
1133
- message: 'test',
1134
- trigger: ['file_modification', 'code_implementation'],
1135
- enforcement: 'test',
1136
- },
1137
- context
1138
- );
1139
-
1140
- expect(result.satisfied).toBe(true);
1141
-
1142
- fs.rmSync(projDir, { recursive: true, force: true });
1143
- });
1144
-
1145
- it('CLAUDE.md Domain Packages 与实际 packages 同步应该通过', async () => {
1146
- const projDir = path.join(tempDir, 'claude-synced-pkg');
1147
- const pkgDir = path.join(projDir, 'packages', 'studio-test');
1148
- fs.mkdirSync(pkgDir, { recursive: true });
1149
-
1150
- fs.writeFileSync(
1151
- path.join(projDir, 'CLAUDE.md'),
1152
- '## Domain Packages\n\n- `packages/studio-test` — Test package\n'
1153
- );
1154
-
1155
- const context: ConstraintContext = {
1156
- operation: 'code_implementation',
1157
- projectPath: projDir,
1158
- };
1159
-
1160
- const result = await checker.check(
1161
- {
1162
- id: 'docs_freshness',
1163
- kind: 'check',
1164
- level: 'guideline',
1165
- rule: 'DOCS FRESHNESS',
1166
- message: 'test',
1167
- trigger: ['file_modification', 'code_implementation'],
1168
- enforcement: 'test',
1169
- },
1170
- context
1171
- );
1172
-
1173
- expect(result.satisfied).toBe(true);
1174
-
1175
- fs.rmSync(projDir, { recursive: true, force: true });
1176
- });
1177
-
1178
- it('实际存在包但 CLAUDE.md 未记录应该失败', async () => {
1179
- const projDir = path.join(tempDir, 'claude-missing-pkg');
1180
- const pkgDir = path.join(projDir, 'packages', 'studio-unlisted');
1181
- fs.mkdirSync(pkgDir, { recursive: true });
1182
-
1183
- // CLAUDE.md 没有提到 studio-unlisted
1184
- fs.writeFileSync(
1185
- path.join(projDir, 'CLAUDE.md'),
1186
- '## Domain Packages\n\n- `packages/studio-other` — Other\n'
1187
- );
1188
- // 创建 studio-other 以通过反向检查
1189
- fs.mkdirSync(path.join(projDir, 'packages', 'studio-other'), { recursive: true });
1190
-
1191
- const context: ConstraintContext = {
1192
- operation: 'code_implementation',
1193
- projectPath: projDir,
1194
- };
1195
-
1196
- const result = await checker.check(
1197
- {
1198
- id: 'docs_freshness',
1199
- kind: 'check',
1200
- level: 'guideline',
1201
- rule: 'DOCS FRESHNESS',
1202
- message: 'test',
1203
- trigger: ['file_modification', 'code_implementation'],
1204
- enforcement: 'test',
1205
- },
1206
- context
1207
- );
1208
-
1209
- expect(result.satisfied).toBe(true);
1210
-
1211
- fs.rmSync(projDir, { recursive: true, force: true });
1212
- });
1213
-
1214
- // CLAUDE.md Key Architecture Paths 检查
1215
- it('CLAUDE.md Key Architecture Paths 引用不存在路径应该失败', async () => {
1216
- const projDir = path.join(tempDir, 'claude-stale-paths');
1217
- fs.mkdirSync(projDir, { recursive: true });
1218
-
1219
- fs.writeFileSync(
1220
- path.join(projDir, 'CLAUDE.md'),
1221
- '## Key Architecture Paths\n\n| 组件 | 路径 |\n|---|---|\n| Ghost | `apps/api/src/modules/ghost/` |\n'
1222
- );
1223
-
1224
- const context: ConstraintContext = {
1225
- operation: 'code_implementation',
1226
- projectPath: projDir,
1227
- };
1228
-
1229
- const result = await checker.check(
1230
- {
1231
- id: 'docs_freshness',
1232
- kind: 'check',
1233
- level: 'guideline',
1234
- rule: 'DOCS FRESHNESS',
1235
- message: 'test',
1236
- trigger: ['file_modification', 'code_implementation'],
1237
- enforcement: 'test',
1238
- },
1239
- context
1240
- );
1241
-
1242
- expect(result.satisfied).toBe(true);
1243
-
1244
- fs.rmSync(projDir, { recursive: true, force: true });
1245
- });
1246
-
1247
- it('CLAUDE.md Key Architecture Paths 全部存在应该通过', async () => {
1248
- const projDir = path.join(tempDir, 'claude-valid-paths');
1249
- const moduleDir = path.join(projDir, 'apps', 'api', 'src', 'modules', 'goals');
1250
- fs.mkdirSync(moduleDir, { recursive: true });
1251
-
1252
- fs.writeFileSync(
1253
- path.join(projDir, 'CLAUDE.md'),
1254
- '## Key Architecture Paths\n\n| 组件 | 路径 |\n|---|---|\n| Goal | `apps/api/src/modules/goals/` |\n'
1255
- );
1256
-
1257
- const context: ConstraintContext = {
1258
- operation: 'code_implementation',
1259
- projectPath: projDir,
1260
- };
1261
-
1262
- const result = await checker.check(
1263
- {
1264
- id: 'docs_freshness',
1265
- kind: 'check',
1266
- level: 'guideline',
1267
- rule: 'DOCS FRESHNESS',
1268
- message: 'test',
1269
- trigger: ['file_modification', 'code_implementation'],
1270
- enforcement: 'test',
1271
- },
1272
- context
1273
- );
1274
-
1275
- expect(result.satisfied).toBe(true);
1276
-
1277
- fs.rmSync(projDir, { recursive: true, force: true });
1278
- });
1279
-
1280
- it('无 CLAUDE.md 应该通过', async () => {
1281
- const projDir = path.join(tempDir, 'claude-none');
1282
- fs.mkdirSync(projDir, { recursive: true });
1283
-
1284
- const context: ConstraintContext = {
1285
- operation: 'code_implementation',
1286
- projectPath: projDir,
1287
- };
1288
-
1289
- const result = await checker.check(
1290
- {
1291
- id: 'docs_freshness',
1292
- kind: 'check',
1293
- level: 'guideline',
1294
- rule: 'DOCS FRESHNESS',
1295
- message: 'test',
1296
- trigger: ['file_modification', 'code_implementation'],
1297
- enforcement: 'test',
1298
- },
1299
- context
1300
- );
1301
-
1302
- expect(result.satisfied).toBe(true);
1303
-
1304
- fs.rmSync(projDir, { recursive: true, force: true });
1305
- });
1306
-
1307
-
1308
- // S1: per-request customConfig 隔离
1309
- describe('S1: per-request customConfig isolation', () => {
1310
- it('使用 per-request customConfig 不应污染单例状态', () => {
1311
- const checker = ConstraintChecker.getInstance();
1312
-
1313
- // 记录原始状态
1314
- const defaultConstraints = checker.getConstraints();
1315
- const defaultIronLawKeys = Object.keys(defaultConstraints.ironLaws);
1316
-
1317
- // 创建自定义配置(只有 1 个 iron law)
1318
- const customConfig = {
1319
- ironLaws: {
1320
- test_only: {
1321
- id: 'test_only',
1322
- kind: 'prompt' as const,
1323
- level: 'iron_law' as const,
1324
- rule: 'TEST',
1325
- message: 'test',
1326
- trigger: 'file_modification',
1327
- enforcement: 'test',
1328
- },
1329
- },
1330
- guidelines: {},
1331
- disabled: [] as string[],
1332
- custom: [] as string[],
1333
- };
1334
-
1335
- // 用 per-request 参数获取约束
1336
- const requestConstraints = checker.getConstraints(customConfig);
1337
- expect(Object.keys(requestConstraints.ironLaws)).toEqual(['test_only']);
1338
-
1339
- // 单例状态应保持不变
1340
- const afterConstraints = checker.getConstraints();
1341
- expect(Object.keys(afterConstraints.ironLaws)).toEqual(defaultIronLawKeys);
1342
-
1343
- // 用 null 也应该使用默认
1344
- const nullConfigConstraints = checker.getConstraints(null);
1345
- expect(Object.keys(nullConfigConstraints.ironLaws)).toEqual(defaultIronLawKeys);
1346
- });
1347
-
1348
- it('并发场景:两个请求使用不同 customConfig 不应互相干扰', async () => {
1349
- const checker = ConstraintChecker.getInstance();
1350
-
1351
- const configA = {
1352
- ironLaws: {
1353
- rule_a: {
1354
- id: 'rule_a',
1355
- kind: 'prompt' as const,
1356
- level: 'iron_law' as const,
1357
- rule: 'A',
1358
- message: 'project A',
1359
- trigger: 'file_modification',
1360
- enforcement: 'test',
1361
- },
1362
- },
1363
- guidelines: {},
1364
- disabled: [] as string[],
1365
- custom: [] as string[],
1366
- };
1367
-
1368
- const configB = {
1369
- ironLaws: {
1370
- rule_b: {
1371
- id: 'rule_b',
1372
- kind: 'prompt' as const,
1373
- level: 'iron_law' as const,
1374
- rule: 'B',
1375
- message: 'project B',
1376
- trigger: 'file_modification',
1377
- enforcement: 'test',
1378
- },
1379
- },
1380
- guidelines: {},
1381
- disabled: [] as string[],
1382
- custom: [] as string[],
1383
- };
1384
-
1385
- const context: ConstraintContext = {
1386
- operation: 'file_modification',
1387
- projectPath: process.cwd(),
1388
- hasTest: true,
1389
- hasVerificationEvidence: true,
1390
- hasSingleTask: true,
1391
- hasExternalCapabilityVerification: true,
1392
- hasRequirementReview: true,
1393
- hasRequirement: true,
1394
- hasWorktree: true,
1395
- hasTwoStageReview: true,
1396
- };
1397
-
1398
- // 并发运行 A 和 B
1399
- const [resultA, resultB] = await Promise.all([
1400
- checker.checkConstraints(context, configA),
1401
- checker.checkConstraints(context, configB),
1402
- ]);
1403
-
1404
- // A 应该只检查 rule_a
1405
- const ironLawIdsA = resultA.ironLaws.map(r => r.id);
1406
- expect(ironLawIdsA).toContain('rule_a');
1407
- expect(ironLawIdsA).not.toContain('rule_b');
1408
-
1409
- // B 应该只检查 rule_b
1410
- const ironLawIdsB = resultB.ironLaws.map(r => r.id);
1411
- expect(ironLawIdsB).toContain('rule_b');
1412
- expect(ironLawIdsB).not.toContain('rule_a');
1413
- });
1414
-
1415
- it('findApplicableConstraints 应接受 per-request config', () => {
1416
- const checker = ConstraintChecker.getInstance();
1417
-
1418
- const customConfig = {
1419
- ironLaws: {
1420
- custom_rule: {
1421
- id: 'custom_rule',
1422
- kind: 'prompt' as const,
1423
- level: 'iron_law' as const,
1424
- rule: 'CUSTOM',
1425
- message: 'custom',
1426
- trigger: 'file_modification',
1427
- enforcement: 'test',
1428
- },
1429
- },
1430
- guidelines: {},
1431
- disabled: [] as string[],
1432
- custom: [] as string[],
1433
- };
1434
-
1435
- const context: ConstraintContext = { operation: 'file_modification' };
1436
- const applicable = checker.findApplicableConstraints(context, customConfig);
1437
- expect(applicable.ironLaws.map(c => c.id)).toEqual(['custom_rule']);
1438
- });
1439
- });
1440
-
1441
- it('step_execution 触发应该生效', async () => {
1442
- const projDir = path.join(tempDir, 'claude-trigger');
1443
- fs.mkdirSync(projDir, { recursive: true });
1444
-
1445
- // 创建一个有 stale Domain Packages 的 CLAUDE.md
1446
- fs.writeFileSync(
1447
- path.join(projDir, 'CLAUDE.md'),
1448
- '## Domain Packages\n\n- `packages/studio-ghost` — Ghost\n'
1449
- );
1450
-
1451
- // 用 step_execution 触发
1452
- const context: ConstraintContext = {
1453
- operation: 'code_implementation',
1454
- projectPath: projDir,
1455
- };
1456
-
1457
- const result = await checker.check(
1458
- {
1459
- id: 'docs_freshness',
1460
- kind: 'check',
1461
- level: 'guideline',
1462
- rule: 'DOCS FRESHNESS',
1463
- message: 'test',
1464
- trigger: ['file_modification', 'code_implementation'],
1465
- enforcement: 'test',
1466
- },
1467
- context
1468
- );
1469
-
1470
- expect(result.satisfied).toBe(true);
1471
-
1472
- fs.rmSync(projDir, { recursive: true, force: true });
1473
- });
1474
- });
1475
264
  });
1476
265
 
1477
266
  describe('buildCheckEnv - 证据接线契约', () => {