@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
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import { validate, createExampleCheckpoint, createExampleResolutions } from '../validate';
6
+ import { captureIO, type CapturingIO } from '../../command-contract';
6
7
  import * as fs from 'fs/promises';
7
8
  import { CheckpointValidator } from '../../../core/validators/checkpoint';
8
9
  import * as yaml from 'js-yaml';
@@ -42,25 +43,22 @@ const MockCheckpointValidator = CheckpointValidator as jest.Mocked<typeof Checkp
42
43
  const mockYaml = yaml as jest.Mocked<typeof yaml>;
43
44
 
44
45
  describe('validate command', () => {
45
- let consoleSpy: jest.SpyInstance;
46
+ let io: CapturingIO;
46
47
 
47
48
  beforeEach(() => {
48
- jest.clearAllMocks();
49
- consoleSpy = jest.spyOn(console, 'log').mockImplementation();
50
- process.exitCode = 0;
51
- });
52
49
 
53
- afterEach(() => {
54
- consoleSpy.mockRestore();
55
- process.exitCode = 0;
50
+ io = captureIO();
51
+ jest.clearAllMocks();
56
52
  });
57
53
 
58
54
  describe('validate', () => {
59
55
  it('应该跳过无检查点的情况', async () => {
60
56
  mockFs.readFile.mockRejectedValue(new Error('file not found'));
61
57
 
62
- await validate({});
63
- expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('没有定义检查点'));
58
+ const result = await validate({}, io);
59
+
60
+ expect(io.outText()).toContain('没有定义检查点');
61
+ expect(result).toEqual({ kind: 'skip', reason: expect.stringContaining('没有定义检查点') });
64
62
  });
65
63
 
66
64
  it('应该通过所有检查点', async () => {
@@ -76,8 +74,10 @@ describe('validate command', () => {
76
74
  };
77
75
  (MockCheckpointValidator.getInstance as jest.Mock).mockReturnValue(mockValidator);
78
76
 
79
- await validate({});
80
- expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('所有检查点验证通过'));
77
+ const result = await validate({}, io);
78
+
79
+ expect(io.outText()).toContain('所有检查点验证通过');
80
+ expect(result).toEqual({ kind: 'ok' });
81
81
  });
82
82
 
83
83
  it('应该显示失败的检查点', async () => {
@@ -97,12 +97,11 @@ describe('validate command', () => {
97
97
  (MockCheckpointValidator.getInstance as jest.Mock).mockReturnValue(mockValidator);
98
98
 
99
99
  // 工单 23:检查点失败一律 exit 1(门控语义)
100
- const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => undefined as never);
101
100
 
102
- await validate({});
103
- expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('失败'));
104
- expect(mockExit).toHaveBeenCalledWith(1);
105
- mockExit.mockRestore();
101
+ const result = await validate({}, io);
102
+
103
+ expect(io.outText()).toContain('失败');
104
+ expect(result).toEqual({ kind: 'fail', reason: '1 个检查点未通过: test-1' });
106
105
  });
107
106
 
108
107
  it('应该在严格模式下退出', async () => {
@@ -121,11 +120,10 @@ describe('validate command', () => {
121
120
  };
122
121
  (MockCheckpointValidator.getInstance as jest.Mock).mockReturnValue(mockValidator);
123
122
 
124
- const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => undefined as never);
123
+ const result = await validate({ strict: true }, io);
125
124
 
126
- await validate({ strict: true });
127
- expect(mockExit).toHaveBeenCalledWith(1);
128
- mockExit.mockRestore();
125
+ // 工单 23 语义:--strict 与否都是 fail;退出码映射在 bin
126
+ expect(result).toEqual({ kind: 'fail', reason: '1 个检查点未通过: test-1' });
129
127
  });
130
128
  });
131
129
 
@@ -136,20 +134,20 @@ describe('validate command', () => {
136
134
  mockFs.writeFile.mockResolvedValue(undefined);
137
135
  mockYaml.dump.mockReturnValue('yaml content');
138
136
 
139
- await createExampleCheckpoint('/project');
137
+ await createExampleCheckpoint('/project', io);
140
138
 
141
139
  expect(mockFs.mkdir).toHaveBeenCalled();
142
140
  expect(mockFs.writeFile).toHaveBeenCalled();
143
- expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('已创建示例检查点'));
141
+ expect(io.outText()).toContain('已创建示例检查点');
144
142
  });
145
143
 
146
144
  it('已存在时不覆盖', async () => {
147
145
  mockFs.access.mockResolvedValue(undefined);
148
146
 
149
- await createExampleCheckpoint('/project');
147
+ await createExampleCheckpoint('/project', io);
150
148
 
151
149
  expect(mockFs.writeFile).not.toHaveBeenCalled();
152
- expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('checkpoints.yml 已存在,跳过'));
150
+ expect(io.outText()).toContain('checkpoints.yml 已存在,跳过');
153
151
  });
154
152
  });
155
153
 
@@ -159,20 +157,20 @@ describe('validate command', () => {
159
157
  mockFs.mkdir.mockResolvedValue(undefined);
160
158
  mockFs.writeFile.mockResolvedValue(undefined);
161
159
 
162
- await createExampleResolutions('/project');
160
+ await createExampleResolutions('/project', io);
163
161
 
164
162
  expect(mockFs.mkdir).toHaveBeenCalled();
165
163
  expect(mockFs.writeFile).toHaveBeenCalled();
166
- expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('已创建 Resolutions 文件'));
164
+ expect(io.outText()).toContain('已创建 Resolutions 文件');
167
165
  });
168
166
 
169
167
  it('已存在时不覆盖', async () => {
170
168
  mockFs.access.mockResolvedValue(undefined);
171
169
 
172
- await createExampleResolutions('/project');
170
+ await createExampleResolutions('/project', io);
173
171
 
174
172
  expect(mockFs.writeFile).not.toHaveBeenCalled();
175
- expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('resolutions.json 已存在,跳过'));
173
+ expect(io.outText()).toContain('resolutions.json 已存在,跳过');
176
174
  });
177
175
  });
178
176
  });
@@ -8,6 +8,8 @@ import chalk from 'chalk';
8
8
  import * as fs from 'fs/promises';
9
9
  import * as path from 'path';
10
10
  import { SpecAcceptanceGate } from '../../gates/acceptance';
11
+ import { log, processIO, type CommandIO, type CommandResult } from '../command-contract';
12
+
11
13
 
12
14
  export interface AcceptanceOptions {
13
15
  /** 任务 ID */
@@ -25,8 +27,11 @@ export interface AcceptanceOptions {
25
27
  /**
26
28
  * 执行验收门控
27
29
  */
28
- export async function acceptance(options: AcceptanceOptions): Promise<void> {
29
- console.log(chalk.blue('📋 验收标准门控检查...'));
30
+ export async function acceptance(
31
+ options: AcceptanceOptions,
32
+ io: CommandIO = processIO,
33
+ ): Promise<CommandResult> {
34
+ log(io, chalk.blue('📋 验收标准门控检查...'));
30
35
 
31
36
  const projectPath = options.projectPath || process.cwd();
32
37
 
@@ -47,36 +52,40 @@ export async function acceptance(options: AcceptanceOptions): Promise<void> {
47
52
  const result = await gate.check(context as any);
48
53
 
49
54
  if (result.passed) {
50
- console.log();
51
- console.log(chalk.green('✅ 验收标准检查通过'));
55
+ log(io);
56
+ log(io, chalk.green('✅ 验收标准检查通过'));
52
57
  if (result.details) {
53
- console.log(chalk.gray(` 通过项: ${result.details.checkedCriteria ?? 0}`));
54
- console.log(chalk.gray(` 总项数: ${result.details.totalCriteria ?? 0}`));
58
+ log(io, chalk.gray(` 通过项: ${result.details.checkedCriteria ?? 0}`));
59
+ log(io, chalk.gray(` 总项数: ${result.details.totalCriteria ?? 0}`));
55
60
  }
56
61
  } else {
57
- console.log();
58
- console.log(chalk.red('❌ 验收标准检查失败'));
59
- console.log(chalk.red(` ${result.message}`));
62
+ log(io);
63
+ log(io, chalk.red('❌ 验收标准检查失败'));
64
+ log(io, chalk.red(` ${result.message}`));
60
65
  if (result.details?.uncheckedCriteria) {
61
66
  (result.details.uncheckedCriteria as string[]).forEach((criteria: string) => {
62
- console.log(chalk.red(` - ${criteria}`));
67
+ log(io, chalk.red(` - ${criteria}`));
63
68
  });
64
69
  }
65
- process.exit(1);
70
+ return { kind: 'fail', reason: `acceptance gate denied: ${result.message}` };
66
71
  }
72
+ return { kind: 'ok' };
67
73
  } catch (error: any) {
68
- console.log();
69
- console.log(chalk.red('❌ 验收标准检查出错'));
70
- console.log(chalk.red(` ${error.message}`));
71
- process.exit(1);
74
+ log(io);
75
+ log(io, chalk.red('❌ 验收标准检查出错'));
76
+ log(io, chalk.red(` ${error.message}`));
77
+ return { kind: 'fail', reason: `acceptance gate error: ${error.message}` };
72
78
  }
73
79
  }
74
80
 
75
81
  /**
76
82
  * 列出所有任务及其验收标准
77
83
  */
78
- export async function listAcceptanceCriteria(options: AcceptanceOptions): Promise<void> {
79
- console.log(chalk.blue('📋 任务验收标准列表...\n'));
84
+ export async function listAcceptanceCriteria(
85
+ options: AcceptanceOptions,
86
+ io: CommandIO = processIO,
87
+ ): Promise<CommandResult> {
88
+ log(io, chalk.blue('📋 任务验收标准列表...\n'));
80
89
 
81
90
  const projectPath = options.projectPath || process.cwd();
82
91
  const tasksPath = options.tasksPath || path.join(projectPath, 'tasks.yml');
@@ -87,25 +96,27 @@ export async function listAcceptanceCriteria(options: AcceptanceOptions): Promis
87
96
  const tasks = yaml.load(content) as any;
88
97
 
89
98
  if (!tasks || typeof tasks !== 'object') {
90
- console.log(chalk.yellow('⚠️ 未找到任务定义'));
91
- return;
99
+ log(io, chalk.yellow('⚠️ 未找到任务定义'));
100
+ return { kind: 'skip', reason: `未找到任务定义: ${tasksPath}` };
92
101
  }
93
102
 
94
103
  for (const [taskId, task] of Object.entries(tasks)) {
95
104
  if (typeof task === 'object' && task !== null) {
96
- console.log(chalk.cyan(`${taskId}:`));
105
+ log(io, chalk.cyan(`${taskId}:`));
97
106
  const taskObj = task as any;
98
107
  if (taskObj.acceptanceCriteria) {
99
108
  taskObj.acceptanceCriteria.forEach((criteria: string, i: number) => {
100
- console.log(chalk.gray(` ${i + 1}. ${criteria}`));
109
+ log(io, chalk.gray(` ${i + 1}. ${criteria}`));
101
110
  });
102
111
  } else {
103
- console.log(chalk.gray(' (无验收标准)'));
112
+ log(io, chalk.gray(' (无验收标准)'));
104
113
  }
105
- console.log();
114
+ log(io);
106
115
  }
107
116
  }
117
+ return { kind: 'ok' };
108
118
  } catch (error: any) {
109
- console.log(chalk.red(`❌ 读取 tasks.yml 失败: ${error.message}`));
119
+ log(io, chalk.red(`❌ 读取 tasks.yml 失败: ${error.message}`));
120
+ return { kind: 'skip', reason: `读取 tasks.yml 失败: ${error.message}` };
110
121
  }
111
122
  }
@@ -28,6 +28,7 @@ import {
28
28
  hasSemanticContent,
29
29
  STOP_WORDS,
30
30
  } from '../session-mining';
31
+ import { log, processIO, type CommandIO, type CommandResult } from '../command-contract';
31
32
 
32
33
  export interface AnalyzeSessionsOptions {
33
34
  days?: number;
@@ -52,26 +53,25 @@ interface PatternCandidate {
52
53
 
53
54
  // ── Main ──
54
55
 
55
- export async function analyzeSessions(options: AnalyzeSessionsOptions): Promise<void> {
56
+ export async function analyzeSessions(options: AnalyzeSessionsOptions, io: CommandIO = processIO): Promise<CommandResult> {
56
57
  const transcriptDir = process.env.CLAUDE_TRANSCRIPTS_DIR
57
58
  || path.join(os.homedir(), '.claude', 'projects', '-root--claude');
58
59
  const memoryDir = path.join(os.homedir(), '.claude', 'projects', '-root-projects', 'memory');
59
60
  const days = options.days || 7;
60
61
 
61
62
  if (!fs.existsSync(transcriptDir)) {
62
- console.log(chalk.yellow('No transcripts directory found'));
63
- return;
63
+ log(io, chalk.yellow('No transcripts directory found'));
64
+ return { kind: 'skip', reason: `会话记录目录不存在: ${transcriptDir}` };
64
65
  }
65
66
 
66
- // 1. Scan transcripts(按修改时间过滤 + 倒序)
67
+ // 1. Scan transcripts(since 下推 seam(harness#112):stat 级过滤,窗口外不 parse;倒序)
67
68
  const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
68
- const sessions = readTranscriptSessions(transcriptDir)
69
- .filter(s => s.mtimeMs >= cutoff)
69
+ const sessions = readTranscriptSessions(transcriptDir, { since: cutoff })
70
70
  .sort((a, b) => b.date.localeCompare(a.date));
71
71
 
72
72
  if (sessions.length === 0) {
73
- console.log(chalk.yellow(`No sessions found in the last ${days} days`));
74
- return;
73
+ log(io, chalk.yellow(`No sessions found in the last ${days} days`));
74
+ return { kind: 'skip', reason: `最近 ${days} 天没有会话` };
75
75
  }
76
76
 
77
77
  // 2. Extract corrections + N-grams
@@ -86,14 +86,15 @@ export async function analyzeSessions(options: AnalyzeSessionsOptions): Promise<
86
86
 
87
87
  // 5. Output
88
88
  if (options.json) {
89
- console.log(JSON.stringify({ sessions: sessions.length, corrections: corrections.length, candidates }, null, 2));
90
- return;
89
+ log(io, JSON.stringify({ sessions: sessions.length, corrections: corrections.length, candidates }, null, 2));
90
+ return { kind: 'ok' };
91
91
  }
92
92
 
93
- console.log(chalk.blue(`🔍 Analyzing ${sessions.length} sessions (last ${days} days)...\n`));
93
+ log(io, chalk.blue(`🔍 Analyzing ${sessions.length} sessions (last ${days} days)...\n`));
94
94
 
95
- printCorrectionSummary(corrections);
96
- printCandidates(candidates);
95
+ printCorrectionSummary(corrections, io);
96
+ printCandidates(candidates, io);
97
+ return { kind: 'ok' };
97
98
  }
98
99
 
99
100
  // ── Correction Extraction ──
@@ -301,34 +302,34 @@ function extractConcept(sentences: string[]): string {
301
302
 
302
303
  // ── Output ──
303
304
 
304
- function printCorrectionSummary(corrections: Correction[]): void {
305
+ function printCorrectionSummary(corrections: Correction[], io: CommandIO): void {
305
306
  if (corrections.length === 0) {
306
- console.log(chalk.green('No correction patterns found'));
307
+ log(io, chalk.green('No correction patterns found'));
307
308
  return;
308
309
  }
309
310
 
310
- console.log(chalk.yellow(`📢 ${corrections.length} correction signals detected\n`));
311
+ log(io, chalk.yellow(`📢 ${corrections.length} correction signals detected\n`));
311
312
  }
312
313
 
313
- function printCandidates(candidates: PatternCandidate[]): void {
314
+ function printCandidates(candidates: PatternCandidate[], io: CommandIO): void {
314
315
  if (candidates.length === 0) {
315
- console.log(chalk.green('✅ No new pattern candidates — all recurring concepts already have rules\n'));
316
+ log(io, chalk.green('✅ No new pattern candidates — all recurring concepts already have rules\n'));
316
317
  return;
317
318
  }
318
319
 
319
- console.log(chalk.bold('🔔 Pattern Candidates (suggested rules)\n'));
320
+ log(io, chalk.bold('🔔 Pattern Candidates (suggested rules)\n'));
320
321
 
321
322
  const topK = candidates.slice(0, 10);
322
323
  for (const c of topK) {
323
324
  const icon = c.source === 'correction' ? '🔴' : '🟡';
324
325
  const confBar = '█'.repeat(Math.round(c.confidence * 10)) + '░'.repeat(10 - Math.round(c.confidence * 10));
325
- console.log(chalk.bold(`${icon} ${c.pattern}`));
326
- console.log(chalk.gray(` Source: ${c.source} | Freq: ${c.frequency} | Sessions: ${c.sessions.length} | Confidence: ${confBar}`));
327
- console.log(chalk.cyan(` → Rule: ${c.suggestedRule}`));
328
- console.log();
326
+ log(io, chalk.bold(`${icon} ${c.pattern}`));
327
+ log(io, chalk.gray(` Source: ${c.source} | Freq: ${c.frequency} | Sessions: ${c.sessions.length} | Confidence: ${confBar}`));
328
+ log(io, chalk.cyan(` → Rule: ${c.suggestedRule}`));
329
+ log(io);
329
330
  }
330
331
 
331
332
  if (candidates.length > 10) {
332
- console.log(chalk.gray(`... and ${candidates.length - 10} more candidates. Run with --json for full list.`));
333
+ log(io, chalk.gray(`... and ${candidates.length - 10} more candidates. Run with --json for full list.`));
333
334
  }
334
335
  }
@@ -4,18 +4,25 @@
4
4
  * 检查约束是否满足(check 层:Iron Laws / Guidelines;prompt 层仅注入不检查)
5
5
  * 工单 23:触发条件与证据检测迁至 core/constraints/context-builder
6
6
  * ADR-0001:约束集统一走 getMergedConstraintsConfig 生效集链路(preset/config 禁用/custom/scenes)
7
+ * harness#88:本命令是 trace 记录器的组合根——core 不上行依赖 monitoring,
8
+ * 真实收集器在此经构造参数接线
7
9
  */
8
10
 
9
11
  import chalk from 'chalk';
10
12
  import * as fs from 'fs';
11
13
  import * as path from 'path';
12
- import { constraintChecker } from '../../core/constraints/checker';
14
+ import { ConstraintChecker } from '../../core/constraints/checker';
13
15
  import { IRON_LAWS, GUIDELINES, PROMPTS } from '../../core/constraints/definitions';
14
16
  import { getMergedConstraintsConfig } from '../../core/effective-constraints';
15
17
  import { buildConstraintContext } from '../../core/constraints/context-builder';
18
+ import { createGitEvidence, type GitEvidence } from '../../core/constraints/git-evidence';
16
19
  import { detectInjectionDrift } from '../../core/constraints/injection-drift';
20
+ import { GOVERNANCE_HEADING } from '../../core/constraints/injection-writer';
21
+ import { getTraceCollector } from '../../monitoring/traces';
22
+ import { countJsonlLines } from '../../utils/jsonl';
17
23
  import { DEFAULT_TRACE_FILE } from '../../types/trace';
18
24
  import type { ConstraintTrigger } from '../../types/constraint';
25
+ import { log, processIO, type CommandIO, type CommandResult } from '../command-contract';
19
26
 
20
27
  export interface CheckOptions {
21
28
  /** 预设名称 */
@@ -26,14 +33,24 @@ export interface CheckOptions {
26
33
  trigger?: ConstraintTrigger;
27
34
  /** 项目路径 */
28
35
  projectPath?: string;
36
+ /**
37
+ * git 证据 adapter(非 CLI flag;#87)
38
+ *
39
+ * 缺省 = 本 run 独占一份真 git 证据。注入则与调用方共用同一证据快照,
40
+ * 测试据此断言"同一 run 内每条 git 命令至多执行一次"。
41
+ */
42
+ evidence?: GitEvidence;
29
43
  }
30
44
 
31
45
  /**
32
46
  * 执行约束检查
33
47
  */
34
- export async function check(options: CheckOptions): Promise<void> {
35
- console.log(chalk.blue('🔍 检查约束...'));
36
- console.log(chalk.gray(`预设: ${options.preset}`));
48
+ export async function check(
49
+ options: CheckOptions,
50
+ io: CommandIO = processIO,
51
+ ): Promise<CommandResult> {
52
+ log(io, chalk.blue('🔍 检查约束...'));
53
+ log(io, chalk.gray(`预设: ${options.preset}`));
37
54
 
38
55
  try {
39
56
  const projectPath = options.projectPath || process.cwd();
@@ -43,33 +60,38 @@ export async function check(options: CheckOptions): Promise<void> {
43
60
  // 项目自定义配置优先于 CLI 预设),优先级规则收在 getMergedConstraintsConfig 一处。
44
61
  const merged = getMergedConstraintsConfig(projectPath, { preset: options.preset });
45
62
  if (merged.custom.length > 0) {
46
- console.log(chalk.gray(`自定义约束: ${merged.custom.length} 条`));
63
+ log(io, chalk.gray(`自定义约束: ${merged.custom.length} 条`));
47
64
  }
48
65
  if (merged.disabled.length > 0) {
49
- console.log(chalk.gray(`已禁用约束: ${merged.disabled.join(', ')}`));
66
+ log(io, chalk.gray(`已禁用约束: ${merged.disabled.join(', ')}`));
50
67
  }
51
68
  const unknownIds = merged.unknownIds ?? [];
52
69
  if (unknownIds.length > 0) {
53
- console.log(chalk.yellow(`⚠️ 配置中存在未知约束 id(已忽略,可清理): ${unknownIds.join(', ')}`));
70
+ log(io, chalk.yellow(`⚠️ 配置中存在未知约束 id(已忽略,可清理): ${unknownIds.join(', ')}`));
54
71
  }
55
72
 
56
73
  // 构建上下文(工单 23:触发条件与证据检测收敛至 core/constraints/context-builder)
74
+ // #87:一次 run 一份 git 证据——context-builder 与 checker 层共用同一实例
75
+ const evidence = options.evidence ?? createGitEvidence(projectPath);
57
76
  const context = await buildConstraintContext({
58
77
  projectPath: options.projectPath,
59
78
  staged: options.staged,
60
79
  trigger: options.trigger,
80
+ evidence,
61
81
  });
62
82
  const changedFiles = context.changedFiles ?? [];
63
83
  if (changedFiles.length > 0) {
64
- console.log(chalk.gray(`变更文件: ${changedFiles.length} 个`));
84
+ log(io, chalk.gray(`变更文件: ${changedFiles.length} 个`));
65
85
  }
66
- console.log(chalk.gray(`触发条件: ${[context.operation, ...(context.extraTriggers ?? [])].join(', ')}`));
86
+ log(io, chalk.gray(`触发条件: ${[context.operation, ...(context.extraTriggers ?? [])].join(', ')}`));
67
87
 
68
- // 执行三层检查(per-request 传 customConfig,避免单例状态污染)
69
- const result = await constraintChecker.checkConstraints(context, merged);
88
+ // 执行三层检查(per-request 传 customConfig,避免单例状态污染;证据同 run 同源)
89
+ // trace 记录器经构造参数接线(harness#88):一次命令一个 checker 实例
90
+ const checker = new ConstraintChecker(getTraceCollector());
91
+ const result = await checker.checkConstraints(context, merged, evidence);
70
92
 
71
93
  // 输出结果
72
- console.log();
94
+ log(io);
73
95
 
74
96
  // skipped(约定未采用/证据未接线)单独列示,不进 pass/fail 统计(ADR-0001)
75
97
  const skippedResults = [
@@ -81,39 +103,42 @@ export async function check(options: CheckOptions): Promise<void> {
81
103
  const evaluatedIronLaws = result.ironLaws.filter(r => !r.skipped);
82
104
  const ironLawViolations = evaluatedIronLaws.filter(r => !r.satisfied);
83
105
  if (ironLawViolations.length === 0 && evaluatedIronLaws.length > 0) {
84
- console.log(chalk.green(`✅ 铁律: 全部通过 (${evaluatedIronLaws.length} 条)`));
106
+ log(io, chalk.green(`✅ 铁律: 全部通过 (${evaluatedIronLaws.length} 条)`));
85
107
  } else if (ironLawViolations.length > 0) {
86
- console.log(chalk.red(`❌ 铁律违规: ${ironLawViolations.length} 条`));
108
+ log(io, chalk.red(`❌ 铁律违规: ${ironLawViolations.length} 条`));
87
109
  ironLawViolations.forEach(r => {
88
110
  if (r.constraint) {
89
- console.log(chalk.red(` - ${r.constraint.id}: ${r.constraint.message}`));
90
- console.log(chalk.red(` ${r.constraint.rule}`));
111
+ log(io, chalk.red(` - ${r.constraint.id}: ${r.constraint.message}`));
112
+ log(io, chalk.red(` ${r.constraint.rule}`));
91
113
  }
92
114
  });
93
- console.log();
94
- console.log(chalk.red('🛑 铁律检查失败,请修复后再提交'));
95
- process.exit(1);
115
+ log(io);
116
+ log(io, chalk.red('🛑 铁律检查失败,请修复后再提交'));
117
+ return {
118
+ kind: 'fail',
119
+ reason: `iron law violated: ${ironLawViolations.map(r => r.constraint?.id ?? 'unknown').join(', ')}`,
120
+ };
96
121
  }
97
122
 
98
123
  // Guidelines
99
124
  if (result.warningCount > 0) {
100
- console.log(chalk.yellow(`⚠️ 指导原则警告: ${result.warningCount} 条`));
125
+ log(io, chalk.yellow(`⚠️ 指导原则警告: ${result.warningCount} 条`));
101
126
  result.guidelines.filter(r => !r.satisfied).forEach(r => {
102
127
  if (r.constraint) {
103
- console.log(chalk.yellow(` - ${r.constraint.id}: ${r.constraint.message}`));
128
+ log(io, chalk.yellow(` - ${r.constraint.id}: ${r.constraint.message}`));
104
129
  }
105
130
  });
106
131
  } else if (result.guidelines.length > 0) {
107
132
  const evaluatedGuidelines = result.guidelines.filter(r => !r.skipped);
108
133
  const passedGuidelines = evaluatedGuidelines.filter(r => r.satisfied).length;
109
- console.log(chalk.green(`✅ 指导原则: ${passedGuidelines}/${evaluatedGuidelines.length} 通过`));
134
+ log(io, chalk.green(`✅ 指导原则: ${passedGuidelines}/${evaluatedGuidelines.length} 通过`));
110
135
  }
111
136
 
112
137
  // Skipped:约定未采用 / 证据未接线,未评估(不计通过/失败)
113
138
  if (skippedResults.length > 0) {
114
- console.log(chalk.gray(`⏭️ 跳过评估: ${skippedResults.length} 条(约定未采用或证据未接线,不计通过/失败)`));
139
+ log(io, chalk.gray(`⏭️ 跳过评估: ${skippedResults.length} 条(约定未采用或证据未接线,不计通过/失败)`));
115
140
  skippedResults.forEach(r => {
116
- console.log(chalk.gray(` - ${r.id}`));
141
+ log(io, chalk.gray(` - ${r.id}`));
117
142
  });
118
143
  }
119
144
 
@@ -122,38 +147,39 @@ export async function check(options: CheckOptions): Promise<void> {
122
147
  try {
123
148
  const drift = detectInjectionDrift(projectPath);
124
149
  if (drift.hasDrift) {
125
- console.log();
126
- console.log(chalk.yellow(`⚠️ 检测到 ${drift.injectionFile ?? '治理文档'} 约束注入漂移(仅警告,不阻断):`));
150
+ log(io);
151
+ log(io, chalk.yellow(`⚠️ 检测到 ${drift.injectionFile ?? '治理文档'} 约束注入漂移(仅警告,不阻断):`));
127
152
  if (drift.versionDrift) {
128
- console.log(chalk.yellow(` ⚠️⚠️ 注入段版本 (${drift.versionDrift.actual}) ≠ 已安装 harness 版本 (${drift.versionDrift.expected}):agent 上下文中的规则与已安装 harness 版本不一致`));
153
+ log(io, chalk.yellow(` ⚠️⚠️ 注入段版本 (${drift.versionDrift.actual}) ≠ 已安装 harness 版本 (${drift.versionDrift.expected}):agent 上下文中的规则与已安装 harness 版本不一致`));
129
154
  }
130
155
  if (drift.contentDrift) {
131
- console.log(chalk.yellow(` 内容漂移: 缺失 ${drift.contentDrift.missing.length} 条 / 多余 ${drift.contentDrift.extra.length} 条(条目级差异见 harness constraints report)`));
156
+ log(io, chalk.yellow(` 内容漂移: 缺失 ${drift.contentDrift.missing.length} 条 / 多余 ${drift.contentDrift.extra.length} 条(条目级差异见 harness constraints report)`));
132
157
  }
133
158
  if (drift.duplicateHeading) {
134
- console.log(chalk.yellow(' 检测到重复的 "## Governance Rules" 章节'));
159
+ log(io, chalk.yellow(` 检测到重复的 "${GOVERNANCE_HEADING}" 章节`));
135
160
  }
136
- console.log(chalk.yellow(` 修复: ${drift.fixHint}`));
161
+ log(io, chalk.yellow(` 修复: ${drift.fixHint}`));
137
162
  }
138
163
  } catch {
139
164
  // 漂移检测失败不影响 check 结果
140
165
  }
141
166
 
142
- console.log();
143
- console.log(chalk.green('✅ 约束检查通过'));
167
+ log(io);
168
+ log(io, chalk.green('✅ 约束检查通过'));
144
169
 
145
170
  // 智能提示
146
171
  const hint = await getSmartHint(projectPath);
147
172
  if (hint) {
148
- console.log();
149
- console.log(chalk.gray('────────────────────────────────────'));
150
- console.log(hint);
151
- console.log(chalk.gray('────────────────────────────────────'));
173
+ log(io);
174
+ log(io, chalk.gray('────────────────────────────────────'));
175
+ log(io, hint);
176
+ log(io, chalk.gray('────────────────────────────────────'));
152
177
  }
178
+ return { kind: 'ok' };
153
179
  } catch (error) {
154
- console.log();
155
- console.log(chalk.red(`❌ 约束检查异常: ${error instanceof Error ? error.message : String(error)}`));
156
- process.exit(1);
180
+ log(io);
181
+ log(io, chalk.red(`❌ 约束检查异常: ${error instanceof Error ? error.message : String(error)}`));
182
+ return { kind: 'fail', reason: `check error: ${error instanceof Error ? error.message : String(error)}` };
157
183
  }
158
184
  }
159
185
 
@@ -163,17 +189,12 @@ export async function check(options: CheckOptions): Promise<void> {
163
189
  async function getSmartHint(projectPath: string): Promise<string | null> {
164
190
  const tracesPath = path.join(projectPath, DEFAULT_TRACE_FILE);
165
191
  const statePath = path.join(projectPath, '.harness', '.state.json');
166
-
167
- // 检查 trace 文件是否存在
168
- if (!fs.existsSync(tracesPath)) {
192
+
193
+ // 只数非空行数(含坏行),零 parse——原语义不变,走 jsonl 正本(harness#82)
194
+ const traceCount = countJsonlLines(tracesPath);
195
+ if (traceCount === 0) {
169
196
  return null;
170
197
  }
171
-
172
- // 读取 trace 记录数
173
- const tracesContent = fs.readFileSync(tracesPath, 'utf-8');
174
- const lines = tracesContent.trim().split('\n').filter(Boolean);
175
- const traceCount = lines.length;
176
-
177
198
  // 读取状态
178
199
  let state: {
179
200
  shownHints?: string[];
@@ -206,33 +227,35 @@ async function getSmartHint(projectPath: string): Promise<string | null> {
206
227
  /**
207
228
  * 列出所有约束
208
229
  */
209
- export function listLaws(): void {
210
- console.log(chalk.blue('\n📜 所有约束:\n'));
230
+ /** optionRoutes 一律以 (options) 调用;--list 不消费检查选项,形参仅占位 */
231
+ export function listLaws(_options: Partial<CheckOptions> = {}, io: CommandIO = processIO): CommandResult {
232
+ log(io, chalk.blue('\n📜 所有约束:\n'));
211
233
 
212
234
  // Iron Laws
213
- console.log(chalk.red('🔴 铁律 (Iron Laws) - 绝对禁止,无例外:\n'));
235
+ log(io, chalk.red('🔴 铁律 (Iron Laws) - 绝对禁止,无例外:\n'));
214
236
  Object.values(IRON_LAWS).forEach(constraint => {
215
- console.log(chalk.red(` ${constraint.id}`));
216
- console.log(chalk.gray(` ${constraint.rule}`));
217
- console.log(chalk.gray(` ${constraint.message}`));
218
- console.log();
237
+ log(io, chalk.red(` ${constraint.id}`));
238
+ log(io, chalk.gray(` ${constraint.rule}`));
239
+ log(io, chalk.gray(` ${constraint.message}`));
240
+ log(io);
219
241
  });
220
242
 
221
243
  // Guidelines
222
- console.log(chalk.yellow('🟡 指导原则 (Guidelines) - 优先建议,违背发警告但不阻止:\n'));
244
+ log(io, chalk.yellow('🟡 指导原则 (Guidelines) - 优先建议,违背发警告但不阻止:\n'));
223
245
  Object.values(GUIDELINES).forEach(constraint => {
224
- console.log(chalk.yellow(` ${constraint.id}`));
225
- console.log(chalk.gray(` ${constraint.rule}`));
226
- console.log(chalk.gray(` ${constraint.message}`));
227
- console.log();
246
+ log(io, chalk.yellow(` ${constraint.id}`));
247
+ log(io, chalk.gray(` ${constraint.rule}`));
248
+ log(io, chalk.gray(` ${constraint.message}`));
249
+ log(io);
228
250
  });
229
251
 
230
252
  // Prompts(ADR-0001:纯注入层,不执行检查)
231
- console.log(chalk.blue('🔵 提示 (Prompts) - 纯文本注入,不参与检查:\n'));
253
+ log(io, chalk.blue('🔵 提示 (Prompts) - 纯文本注入,不参与检查:\n'));
232
254
  Object.values(PROMPTS).forEach(constraint => {
233
- console.log(chalk.blue(` ${constraint.id}`));
234
- console.log(chalk.gray(` ${constraint.rule}`));
235
- console.log(chalk.gray(` ${constraint.message}`));
236
- console.log();
255
+ log(io, chalk.blue(` ${constraint.id}`));
256
+ log(io, chalk.gray(` ${constraint.rule}`));
257
+ log(io, chalk.gray(` ${constraint.message}`));
258
+ log(io);
237
259
  });
260
+ return { kind: 'ok' };
238
261
  }