@dommaker/harness 1.2.3 → 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 (550) hide show
  1. package/CHANGELOG.md +15 -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 +30 -215
  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 +33 -26
  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 +237 -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 +73 -77
  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 +31 -16
  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/completion-checkers/index.d.ts +3 -3
  127. package/dist/completion-checkers/index.js +3 -3
  128. package/dist/completion-checkers/phase-format.d.ts +1 -1
  129. package/dist/completion-checkers/phase-format.js +2 -2
  130. package/dist/completion-checkers/phase-format.js.map +1 -1
  131. package/dist/completion-checkers/tdd-chain.d.ts +1 -1
  132. package/dist/completion-checkers/tdd-chain.js +2 -2
  133. package/dist/completion-checkers/tdd-chain.js.map +1 -1
  134. package/dist/completion-checkers/types.d.ts +5 -5
  135. package/dist/completion-checkers/types.d.ts.map +1 -1
  136. package/dist/completion-checkers/types.js +2 -2
  137. package/dist/context/session-manager.d.ts.map +1 -1
  138. package/dist/context/session-manager.js +19 -32
  139. package/dist/context/session-manager.js.map +1 -1
  140. package/dist/core/constraints/agent-prompt-renderer.d.ts.map +1 -1
  141. package/dist/core/constraints/agent-prompt-renderer.js +1 -4
  142. package/dist/core/constraints/agent-prompt-renderer.js.map +1 -1
  143. package/dist/core/constraints/capabilities-parser.d.ts +24 -7
  144. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  145. package/dist/core/constraints/capabilities-parser.js +32 -28
  146. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  147. package/dist/core/constraints/capabilities-reconcile.d.ts +67 -0
  148. package/dist/core/constraints/capabilities-reconcile.d.ts.map +1 -0
  149. package/dist/core/constraints/capabilities-reconcile.js +105 -0
  150. package/dist/core/constraints/capabilities-reconcile.js.map +1 -0
  151. package/dist/core/constraints/checker.d.ts +21 -43
  152. package/dist/core/constraints/checker.d.ts.map +1 -1
  153. package/dist/core/constraints/checker.js +64 -115
  154. package/dist/core/constraints/checker.js.map +1 -1
  155. package/dist/core/constraints/checkers/capability-sync.d.ts +4 -3
  156. package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
  157. package/dist/core/constraints/checkers/capability-sync.js +29 -69
  158. package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
  159. package/dist/core/constraints/checkers/context-doc-sync.d.ts +4 -2
  160. package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
  161. package/dist/core/constraints/checkers/context-doc-sync.js +13 -22
  162. package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
  163. package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
  164. package/dist/core/constraints/checkers/docs-freshness.js +37 -63
  165. package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
  166. package/dist/core/constraints/checkers/governance-presence.d.ts +2 -2
  167. package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
  168. package/dist/core/constraints/checkers/governance-presence.js +7 -28
  169. package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
  170. package/dist/core/constraints/checkers/no-test-simplification.d.ts.map +1 -1
  171. package/dist/core/constraints/checkers/no-test-simplification.js +4 -3
  172. package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
  173. package/dist/core/constraints/checkers/types.d.ts +2 -2
  174. package/dist/core/constraints/context-builder.d.ts +15 -14
  175. package/dist/core/constraints/context-builder.d.ts.map +1 -1
  176. package/dist/core/constraints/context-builder.js +32 -96
  177. package/dist/core/constraints/context-builder.js.map +1 -1
  178. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  179. package/dist/core/constraints/doc-freshness/runner.js +2 -1
  180. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  181. package/dist/core/constraints/git-evidence.d.ts +60 -0
  182. package/dist/core/constraints/git-evidence.d.ts.map +1 -0
  183. package/dist/core/constraints/git-evidence.js +88 -0
  184. package/dist/core/constraints/git-evidence.js.map +1 -0
  185. package/dist/core/constraints/injection-drift.d.ts +3 -20
  186. package/dist/core/constraints/injection-drift.d.ts.map +1 -1
  187. package/dist/core/constraints/injection-drift.js +10 -57
  188. package/dist/core/constraints/injection-drift.js.map +1 -1
  189. package/dist/core/constraints/injection-writer.d.ts +113 -0
  190. package/dist/core/constraints/injection-writer.d.ts.map +1 -0
  191. package/dist/core/constraints/injection-writer.js +228 -0
  192. package/dist/core/constraints/injection-writer.js.map +1 -0
  193. package/dist/core/constraints/usage-report.d.ts +22 -1
  194. package/dist/core/constraints/usage-report.d.ts.map +1 -1
  195. package/dist/core/constraints/usage-report.js +27 -20
  196. package/dist/core/constraints/usage-report.js.map +1 -1
  197. package/dist/core/index.d.ts +2 -2
  198. package/dist/core/index.d.ts.map +1 -1
  199. package/dist/core/index.js +4 -2
  200. package/dist/core/index.js.map +1 -1
  201. package/dist/core/project-config-loader.d.ts +32 -1
  202. package/dist/core/project-config-loader.d.ts.map +1 -1
  203. package/dist/core/project-config-loader.js +39 -9
  204. package/dist/core/project-config-loader.js.map +1 -1
  205. package/dist/core/validators/passes-gate.d.ts +15 -64
  206. package/dist/core/validators/passes-gate.d.ts.map +1 -1
  207. package/dist/core/validators/passes-gate.js +84 -222
  208. package/dist/core/validators/passes-gate.js.map +1 -1
  209. package/dist/core/validators/test-output.d.ts +58 -0
  210. package/dist/core/validators/test-output.d.ts.map +1 -0
  211. package/dist/core/validators/test-output.js +109 -0
  212. package/dist/core/validators/test-output.js.map +1 -0
  213. package/dist/failure/recorder.d.ts +7 -1
  214. package/dist/failure/recorder.d.ts.map +1 -1
  215. package/dist/failure/recorder.js +13 -9
  216. package/dist/failure/recorder.js.map +1 -1
  217. package/dist/gates/acceptance.d.ts +1 -48
  218. package/dist/gates/acceptance.d.ts.map +1 -1
  219. package/dist/gates/acceptance.js +6 -38
  220. package/dist/gates/acceptance.js.map +1 -1
  221. package/dist/gates/checker-gate.d.ts.map +1 -1
  222. package/dist/gates/checker-gate.js +6 -11
  223. package/dist/gates/checker-gate.js.map +1 -1
  224. package/dist/gates/command.d.ts.map +1 -1
  225. package/dist/gates/command.js +10 -22
  226. package/dist/gates/command.js.map +1 -1
  227. package/dist/gates/contract.d.ts.map +1 -1
  228. package/dist/gates/contract.js +22 -61
  229. package/dist/gates/contract.js.map +1 -1
  230. package/dist/gates/performance.d.ts.map +1 -1
  231. package/dist/gates/performance.js +13 -31
  232. package/dist/gates/performance.js.map +1 -1
  233. package/dist/gates/review.d.ts.map +1 -1
  234. package/dist/gates/review.js +21 -53
  235. package/dist/gates/review.js.map +1 -1
  236. package/dist/gates/security.d.ts.map +1 -1
  237. package/dist/gates/security.js +14 -32
  238. package/dist/gates/security.js.map +1 -1
  239. package/dist/gates/types.d.ts +28 -5
  240. package/dist/gates/types.d.ts.map +1 -1
  241. package/dist/gates/types.js +38 -1
  242. package/dist/gates/types.js.map +1 -1
  243. package/dist/hooks/bootstrap.d.ts +2 -0
  244. package/dist/hooks/bootstrap.d.ts.map +1 -1
  245. package/dist/hooks/bootstrap.js +5 -2
  246. package/dist/hooks/bootstrap.js.map +1 -1
  247. package/dist/index.d.ts +4 -3
  248. package/dist/index.d.ts.map +1 -1
  249. package/dist/index.js +13 -4
  250. package/dist/index.js.map +1 -1
  251. package/dist/knowledge/audit.d.ts +7 -2
  252. package/dist/knowledge/audit.d.ts.map +1 -1
  253. package/dist/knowledge/audit.js +59 -79
  254. package/dist/knowledge/audit.js.map +1 -1
  255. package/dist/knowledge/flywheel-metrics.d.ts +39 -0
  256. package/dist/knowledge/flywheel-metrics.d.ts.map +1 -0
  257. package/dist/knowledge/flywheel-metrics.js +47 -0
  258. package/dist/knowledge/flywheel-metrics.js.map +1 -0
  259. package/dist/knowledge/import.d.ts.map +1 -1
  260. package/dist/knowledge/import.js +4 -14
  261. package/dist/knowledge/import.js.map +1 -1
  262. package/dist/knowledge/index-generator.d.ts.map +1 -1
  263. package/dist/knowledge/index-generator.js +18 -15
  264. package/dist/knowledge/index-generator.js.map +1 -1
  265. package/dist/knowledge/ingest.d.ts.map +1 -1
  266. package/dist/knowledge/ingest.js +9 -4
  267. package/dist/knowledge/ingest.js.map +1 -1
  268. package/dist/knowledge/migration.d.ts.map +1 -1
  269. package/dist/knowledge/migration.js +11 -8
  270. package/dist/knowledge/migration.js.map +1 -1
  271. package/dist/knowledge/reference-tracker.d.ts.map +1 -1
  272. package/dist/knowledge/reference-tracker.js +12 -9
  273. package/dist/knowledge/reference-tracker.js.map +1 -1
  274. package/dist/knowledge/store.d.ts +15 -0
  275. package/dist/knowledge/store.d.ts.map +1 -1
  276. package/dist/knowledge/store.js +59 -10
  277. package/dist/knowledge/store.js.map +1 -1
  278. package/dist/monitoring/context-tracker.d.ts.map +1 -1
  279. package/dist/monitoring/context-tracker.js +11 -20
  280. package/dist/monitoring/context-tracker.js.map +1 -1
  281. package/dist/monitoring/index.d.ts +1 -1
  282. package/dist/monitoring/index.d.ts.map +1 -1
  283. package/dist/monitoring/index.js +1 -2
  284. package/dist/monitoring/index.js.map +1 -1
  285. package/dist/monitoring/trace-analyzer.d.ts +15 -0
  286. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  287. package/dist/monitoring/trace-analyzer.js +19 -2
  288. package/dist/monitoring/trace-analyzer.js.map +1 -1
  289. package/dist/monitoring/traces.d.ts +21 -2
  290. package/dist/monitoring/traces.d.ts.map +1 -1
  291. package/dist/monitoring/traces.js +38 -29
  292. package/dist/monitoring/traces.js.map +1 -1
  293. package/dist/release/index.d.ts +3 -0
  294. package/dist/release/index.d.ts.map +1 -0
  295. package/dist/release/index.js +7 -0
  296. package/dist/release/index.js.map +1 -0
  297. package/dist/release/integrity.d.ts +65 -0
  298. package/dist/release/integrity.d.ts.map +1 -0
  299. package/dist/release/integrity.js +151 -0
  300. package/dist/release/integrity.js.map +1 -0
  301. package/dist/sdd/index-generator.js +10 -11
  302. package/dist/sdd/index-generator.js.map +1 -1
  303. package/dist/test-setup/project-fixture.d.ts +48 -0
  304. package/dist/test-setup/project-fixture.d.ts.map +1 -0
  305. package/dist/test-setup/project-fixture.js +106 -0
  306. package/dist/test-setup/project-fixture.js.map +1 -0
  307. package/dist/types/constraint.d.ts +0 -18
  308. package/dist/types/constraint.d.ts.map +1 -1
  309. package/dist/types/constraint.js.map +1 -1
  310. package/dist/types/failure.d.ts +7 -0
  311. package/dist/types/failure.d.ts.map +1 -1
  312. package/dist/types/failure.js +8 -1
  313. package/dist/types/failure.js.map +1 -1
  314. package/dist/types/passes-gate.d.ts +10 -25
  315. package/dist/types/passes-gate.d.ts.map +1 -1
  316. package/dist/types/project-config.d.ts +1 -1
  317. package/dist/types/session.d.ts +6 -5
  318. package/dist/types/session.d.ts.map +1 -1
  319. package/dist/utils/exec.d.ts +10 -0
  320. package/dist/utils/exec.d.ts.map +1 -1
  321. package/dist/utils/exec.js +12 -0
  322. package/dist/utils/exec.js.map +1 -1
  323. package/dist/utils/file-walk.d.ts +9 -1
  324. package/dist/utils/file-walk.d.ts.map +1 -1
  325. package/dist/utils/file-walk.js +10 -1
  326. package/dist/utils/file-walk.js.map +1 -1
  327. package/dist/utils/frontmatter.d.ts +73 -0
  328. package/dist/utils/frontmatter.d.ts.map +1 -0
  329. package/dist/utils/frontmatter.js +141 -0
  330. package/dist/utils/frontmatter.js.map +1 -0
  331. package/dist/utils/jsonl.d.ts +92 -0
  332. package/dist/utils/jsonl.d.ts.map +1 -0
  333. package/dist/utils/jsonl.js +166 -0
  334. package/dist/utils/jsonl.js.map +1 -0
  335. package/dist/utils/package-version.d.ts +16 -0
  336. package/dist/utils/package-version.d.ts.map +1 -0
  337. package/dist/utils/package-version.js +63 -0
  338. package/dist/utils/package-version.js.map +1 -0
  339. package/package.json +1 -1
  340. package/src/CONTEXT.md +7 -2
  341. package/src/__tests__/acceptance-gate.test.ts +3 -1
  342. package/src/__tests__/checker-extra.test.ts +7 -1218
  343. package/src/__tests__/context-files-resolution.test.ts +116 -0
  344. package/src/__tests__/effective-constraints.test.ts +10 -13
  345. package/src/__tests__/exec.test.ts +35 -1
  346. package/src/__tests__/failure-recorder.test.ts +9 -0
  347. package/src/__tests__/layering.test.ts +152 -0
  348. package/src/__tests__/passes-gate-extra.test.ts +0 -145
  349. package/src/__tests__/passes-gate.test.ts +142 -106
  350. package/src/__tests__/public-exports.test.ts +3 -0
  351. package/src/__tests__/skip-semantics.test.ts +5 -9
  352. package/src/cli/__tests__/command-contract.test.ts +170 -0
  353. package/src/cli/command-contract.ts +108 -0
  354. package/src/cli/commands/CONTEXT.md +22 -7
  355. package/src/cli/commands/__tests__/acceptance.test.ts +40 -33
  356. package/src/cli/commands/__tests__/analyze-sessions.test.ts +35 -29
  357. package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +101 -0
  358. package/src/cli/commands/__tests__/check-drift.test.ts +27 -34
  359. package/src/cli/commands/__tests__/check-skip-output.test.ts +21 -24
  360. package/src/cli/commands/__tests__/check.test.ts +378 -382
  361. package/src/cli/commands/__tests__/command.test.ts +85 -70
  362. package/src/cli/commands/__tests__/constraints-report.test.ts +100 -71
  363. package/src/cli/commands/__tests__/constraints-retire.test.ts +152 -95
  364. package/src/cli/commands/__tests__/contract.test.ts +27 -34
  365. package/src/cli/commands/__tests__/failure.test.ts +31 -34
  366. package/src/cli/commands/__tests__/init-injection.test.ts +58 -28
  367. package/src/cli/commands/__tests__/init.test.ts +77 -48
  368. package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +108 -0
  369. package/src/cli/commands/__tests__/knowledge.test.ts +96 -38
  370. package/src/cli/commands/__tests__/passes-gate.test.ts +43 -45
  371. package/src/cli/commands/__tests__/performance.test.ts +22 -23
  372. package/src/cli/commands/__tests__/posteval-plan.test.ts +39 -38
  373. package/src/cli/commands/__tests__/project-path-anchoring.test.ts +157 -0
  374. package/src/cli/commands/__tests__/project-path-convention.test.ts +169 -0
  375. package/src/cli/commands/__tests__/registry.test.ts +69 -35
  376. package/src/cli/commands/__tests__/release.test.ts +224 -74
  377. package/src/cli/commands/__tests__/report-extra.test.ts +16 -16
  378. package/src/cli/commands/__tests__/report.test.ts +25 -25
  379. package/src/cli/commands/__tests__/review.test.ts +33 -31
  380. package/src/cli/commands/__tests__/security.test.ts +23 -27
  381. package/src/cli/commands/__tests__/spec-baseline-check.test.ts +20 -20
  382. package/src/cli/commands/__tests__/spec.test.ts +41 -29
  383. package/src/cli/commands/__tests__/status-extra.test.ts +18 -18
  384. package/src/cli/commands/__tests__/status-skipped-lines.test.ts +72 -0
  385. package/src/cli/commands/__tests__/status.test.ts +34 -34
  386. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +108 -86
  387. package/src/cli/commands/__tests__/sync-docs.test.ts +72 -74
  388. package/src/cli/commands/__tests__/update-user-model.test.ts +62 -33
  389. package/src/cli/commands/__tests__/validate.test.ts +27 -29
  390. package/src/cli/commands/acceptance.ts +35 -24
  391. package/src/cli/commands/analyze-sessions.ts +25 -24
  392. package/src/cli/commands/check.ts +88 -65
  393. package/src/cli/commands/command.ts +30 -17
  394. package/src/cli/commands/constraints-report.ts +37 -34
  395. package/src/cli/commands/constraints-retire.ts +58 -55
  396. package/src/cli/commands/constraints.ts +10 -27
  397. package/src/cli/commands/contract.ts +51 -42
  398. package/src/cli/commands/definitions.ts +37 -223
  399. package/src/cli/commands/failure.ts +35 -27
  400. package/src/cli/commands/init.ts +166 -220
  401. package/src/cli/commands/knowledge.ts +250 -203
  402. package/src/cli/commands/passes-gate.ts +58 -68
  403. package/src/cli/commands/performance.ts +23 -18
  404. package/src/cli/commands/posteval-plan.ts +29 -15
  405. package/src/cli/commands/release.ts +75 -78
  406. package/src/cli/commands/report.ts +9 -5
  407. package/src/cli/commands/review.ts +39 -29
  408. package/src/cli/commands/sdd.ts +7 -5
  409. package/src/cli/commands/security.ts +35 -26
  410. package/src/cli/commands/spec-baseline-check.ts +16 -11
  411. package/src/cli/commands/spec.ts +66 -53
  412. package/src/cli/commands/status.ts +59 -53
  413. package/src/cli/commands/sync-docs/agents-syncer.ts +31 -15
  414. package/src/cli/commands/sync-docs/context-syncer.ts +3 -2
  415. package/src/cli/commands/sync-docs/index.ts +73 -100
  416. package/src/cli/commands/sync-docs/project-reader.ts +17 -18
  417. package/src/cli/commands/update-user-model.ts +33 -45
  418. package/src/cli/commands/validate.ts +31 -25
  419. package/src/cli/session-mining/__tests__/transcript.test.ts +118 -0
  420. package/src/cli/session-mining/transcript.ts +26 -8
  421. package/src/completion-checkers/CONTEXT.md +1 -1
  422. package/src/completion-checkers/__tests__/contract-presence.test.ts +1 -1
  423. package/src/completion-checkers/__tests__/phase-format.test.ts +1 -1
  424. package/src/completion-checkers/__tests__/tdd-chain.test.ts +2 -2
  425. package/src/completion-checkers/index.ts +3 -3
  426. package/src/completion-checkers/phase-format.ts +2 -2
  427. package/src/completion-checkers/tdd-chain.ts +2 -2
  428. package/src/completion-checkers/types.ts +5 -5
  429. package/src/context/session-manager.ts +19 -33
  430. package/src/core/CONTEXT.md +9 -6
  431. package/src/core/constraints/__tests__/capabilities-reconcile.test.ts +200 -0
  432. package/src/core/constraints/__tests__/capability-counts.test.ts +94 -0
  433. package/src/core/constraints/__tests__/git-evidence.test.ts +208 -0
  434. package/src/core/constraints/__tests__/injection-drift.test.ts +3 -10
  435. package/src/core/constraints/__tests__/injection-writer.test.ts +231 -0
  436. package/src/core/constraints/__tests__/trace-injection.test.ts +71 -0
  437. package/src/core/constraints/__tests__/usage-report.test.ts +112 -0
  438. package/src/core/constraints/agent-prompt-renderer.ts +2 -6
  439. package/src/core/constraints/capabilities-parser.ts +53 -35
  440. package/src/core/constraints/capabilities-reconcile.ts +166 -0
  441. package/src/core/constraints/checker.ts +75 -130
  442. package/src/core/constraints/checkers/__tests__/capability-sync.test.ts +214 -0
  443. package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +77 -0
  444. package/src/core/constraints/checkers/__tests__/docs-freshness.test.ts +117 -0
  445. package/src/{__tests__ → core/constraints/checkers/__tests__}/governance-presence.test.ts +83 -9
  446. package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +50 -0
  447. package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +76 -0
  448. package/src/{__tests__ → core/constraints/checkers/__tests__}/no-hardcoded-credentials.test.ts +4 -3
  449. package/src/core/constraints/checkers/__tests__/no-test-simplification.test.ts +58 -0
  450. package/src/core/constraints/checkers/__tests__/three-state-consistency.test.ts +145 -0
  451. package/src/core/constraints/checkers/capability-sync.ts +29 -88
  452. package/src/core/constraints/checkers/context-doc-sync.ts +15 -25
  453. package/src/core/constraints/checkers/docs-freshness.ts +40 -69
  454. package/src/core/constraints/checkers/governance-presence.ts +7 -25
  455. package/src/core/constraints/checkers/no-test-simplification.ts +4 -3
  456. package/src/core/constraints/checkers/types.ts +2 -2
  457. package/src/core/constraints/context-builder.ts +39 -91
  458. package/src/core/constraints/doc-freshness/runner.ts +2 -1
  459. package/src/core/constraints/git-evidence.ts +120 -0
  460. package/src/core/constraints/injection-drift.ts +13 -60
  461. package/src/core/constraints/injection-writer.ts +212 -0
  462. package/src/core/constraints/usage-report.ts +36 -17
  463. package/src/core/index.ts +4 -2
  464. package/src/core/project-config-loader.ts +49 -9
  465. package/src/core/validators/__tests__/passes-gate.test.ts +2 -56
  466. package/src/core/validators/__tests__/test-output.test.ts +372 -0
  467. package/src/core/validators/passes-gate.ts +86 -248
  468. package/src/core/validators/test-output.ts +128 -0
  469. package/src/failure/__tests__/recorder.test.ts +168 -0
  470. package/src/failure/recorder.ts +13 -11
  471. package/src/gates/CONTEXT.md +6 -3
  472. package/src/gates/__tests__/acceptance-types-single-source.test.ts +68 -0
  473. package/src/gates/__tests__/acceptance.test.ts +4 -86
  474. package/src/gates/__tests__/effective-gates.test.ts +3 -8
  475. package/src/gates/__tests__/test-verdict-consistency.test.ts +140 -0
  476. package/src/gates/acceptance.ts +19 -84
  477. package/src/gates/checker-gate.ts +8 -7
  478. package/src/gates/command.ts +10 -16
  479. package/src/gates/contract.ts +22 -61
  480. package/src/gates/performance.ts +10 -22
  481. package/src/gates/review.ts +21 -41
  482. package/src/gates/security.ts +10 -22
  483. package/src/gates/types.ts +70 -5
  484. package/src/hooks/CONTEXT.md +1 -1
  485. package/src/hooks/bootstrap.ts +5 -2
  486. package/src/index.ts +11 -6
  487. package/src/knowledge/CONTEXT.md +8 -3
  488. package/src/knowledge/__tests__/audit-labels.test.ts +44 -0
  489. package/src/knowledge/__tests__/flywheel-metrics.test.ts +135 -0
  490. package/src/knowledge/__tests__/import.test.ts +44 -0
  491. package/src/knowledge/__tests__/index-generator.test.ts +55 -2
  492. package/src/knowledge/__tests__/ingest.test.ts +85 -0
  493. package/src/knowledge/__tests__/migration.test.ts +47 -0
  494. package/src/knowledge/__tests__/reference-tracker.test.ts +16 -0
  495. package/src/knowledge/__tests__/store.test.ts +194 -0
  496. package/src/knowledge/audit.ts +67 -71
  497. package/src/knowledge/flywheel-metrics.ts +71 -0
  498. package/src/knowledge/import.ts +4 -18
  499. package/src/knowledge/index-generator.ts +20 -15
  500. package/src/knowledge/ingest.ts +9 -4
  501. package/src/knowledge/migration.ts +11 -8
  502. package/src/knowledge/reference-tracker.ts +12 -8
  503. package/src/knowledge/store.ts +64 -11
  504. package/src/monitoring/CONTEXT.md +8 -3
  505. package/src/monitoring/__tests__/skipped-lines-report.test.ts +178 -0
  506. package/src/monitoring/__tests__/traces.test.ts +57 -0
  507. package/src/monitoring/context-tracker.ts +11 -23
  508. package/src/monitoring/index.ts +1 -1
  509. package/src/monitoring/trace-analyzer.ts +21 -2
  510. package/src/monitoring/traces.ts +39 -36
  511. package/src/release/CONTEXT.md +24 -0
  512. package/src/release/__tests__/barrel.test.ts +60 -0
  513. package/src/release/__tests__/integrity.test.ts +157 -0
  514. package/src/release/index.ts +2 -0
  515. package/src/release/integrity.ts +141 -0
  516. package/src/sdd/__tests__/index-generator.test.ts +71 -0
  517. package/src/sdd/index-generator.ts +9 -9
  518. package/src/test-setup/__tests__/project-fixture.test.ts +178 -0
  519. package/src/test-setup/project-fixture.ts +87 -0
  520. package/src/tools/CONTEXT.md +6 -0
  521. package/src/tools/__tests__/registry.test.ts +221 -0
  522. package/src/types/__tests__/types.test.ts +4 -5
  523. package/src/types/constraint.ts +0 -25
  524. package/src/types/failure.ts +8 -0
  525. package/src/types/passes-gate.ts +10 -30
  526. package/src/types/project-config.ts +1 -1
  527. package/src/types/session.ts +5 -5
  528. package/src/utils/__tests__/frontmatter.test.ts +206 -0
  529. package/src/utils/__tests__/jsonl-skip-disposition.test.ts +175 -0
  530. package/src/utils/__tests__/jsonl.test.ts +187 -0
  531. package/src/utils/__tests__/package-version.test.ts +97 -0
  532. package/src/utils/exec.ts +15 -0
  533. package/src/utils/file-walk.ts +11 -2
  534. package/src/utils/frontmatter.ts +121 -0
  535. package/src/utils/jsonl.ts +167 -0
  536. package/src/utils/package-version.ts +27 -0
  537. package/dist/cli/commands/doc-freshness-check.d.ts +0 -53
  538. package/dist/cli/commands/doc-freshness-check.d.ts.map +0 -1
  539. package/dist/cli/commands/doc-freshness-check.js +0 -352
  540. package/dist/cli/commands/doc-freshness-check.js.map +0 -1
  541. package/dist/types/index.d.ts +0 -14
  542. package/dist/types/index.d.ts.map +0 -1
  543. package/dist/types/index.js +0 -39
  544. package/dist/types/index.js.map +0 -1
  545. package/src/__tests__/long-running.test.ts +0 -17
  546. package/src/__tests__/passes-gate-extension.test.ts +0 -151
  547. package/src/cli/commands/__tests__/doc-freshness-check.test.ts +0 -168
  548. package/src/cli/commands/doc-freshness-check.ts +0 -386
  549. package/src/spec/CONTEXT.md +0 -11
  550. package/src/types/index.ts +0 -47
@@ -5,9 +5,13 @@
5
5
  * harness command "rm -rf /"
6
6
  * harness command --level "DROP TABLE users"
7
7
  * harness command --list
8
+ *
9
+ * 判定经返回值外溢(架构评审候选7):风险等级/黑名单裁决译成 kind,
10
+ * 退出码映射收敛在 bin 一处。
8
11
  */
9
12
 
10
13
  import { createCommandGate, getCommandRiskLevel, DEFAULT_COMMAND_BLACKLIST, type CommandBlacklistRule } from '../../gates';
14
+ import { log, logError, processIO, type CommandIO, type CommandResult } from '../command-contract';
11
15
 
12
16
  export interface CommandCheckOptions {
13
17
  level?: boolean;
@@ -19,17 +23,21 @@ export interface CommandCheckOptions {
19
23
  /**
20
24
  * 执行命令黑名单检查(供 bin/harness.js 调用)
21
25
  */
22
- export async function executeCommand(cmd: string | undefined, options: CommandCheckOptions): Promise<void> {
26
+ export async function executeCommand(
27
+ cmd: string | undefined,
28
+ options: CommandCheckOptions,
29
+ io: CommandIO = processIO,
30
+ ): Promise<CommandResult> {
23
31
  if (options.list) {
24
32
  if (options.json) {
25
- console.log(JSON.stringify(DEFAULT_COMMAND_BLACKLIST.map((r: CommandBlacklistRule) => ({
33
+ log(io, JSON.stringify(DEFAULT_COMMAND_BLACKLIST.map((r: CommandBlacklistRule) => ({
26
34
  id: r.id,
27
35
  level: r.level,
28
36
  message: r.message,
29
37
  category: r.category,
30
38
  })), null, 2));
31
39
  } else {
32
- console.log('\n命令黑名单规则:\n');
40
+ log(io, '\n命令黑名单规则:\n');
33
41
 
34
42
  const byCategory = DEFAULT_COMMAND_BLACKLIST.reduce((acc: Record<string, CommandBlacklistRule[]>, rule: CommandBlacklistRule) => {
35
43
  if (!acc[rule.category]) acc[rule.category] = [];
@@ -38,23 +46,23 @@ export async function executeCommand(cmd: string | undefined, options: CommandCh
38
46
  }, {});
39
47
 
40
48
  for (const [category, rules] of Object.entries(byCategory)) {
41
- console.log(`[${category}]`);
49
+ log(io, `[${category}]`);
42
50
  for (const rule of rules as CommandBlacklistRule[]) {
43
51
  const levelIcon = rule.level === 'block' ? '🚫' : rule.level === 'warn' ? '⚠️' : '📋';
44
- console.log(` ${levelIcon} ${rule.id}: ${rule.message} (${rule.level})`);
52
+ log(io, ` ${levelIcon} ${rule.id}: ${rule.message} (${rule.level})`);
45
53
  }
46
- console.log();
54
+ log(io);
47
55
  }
48
56
 
49
- console.log(`共 ${DEFAULT_COMMAND_BLACKLIST.length} 条规则`);
57
+ log(io, `共 ${DEFAULT_COMMAND_BLACKLIST.length} 条规则`);
50
58
  }
51
- return;
59
+ return { kind: 'ok' };
52
60
  }
53
61
 
54
62
  if (!cmd) {
55
- console.error('错误:请提供要检查的命令');
56
- console.error('用法:harness command "your command"');
57
- process.exit(1);
63
+ logError(io, '错误:请提供要检查的命令');
64
+ logError(io, '用法:harness command "your command"');
65
+ return { kind: 'usage-error', reason: '缺少要检查的命令参数' };
58
66
  }
59
67
 
60
68
  const gate = createCommandGate({ strict: options.strict });
@@ -63,28 +71,33 @@ export async function executeCommand(cmd: string | undefined, options: CommandCh
63
71
  if (options.level) {
64
72
  const level = getCommandRiskLevel(cmd);
65
73
  if (options.json) {
66
- console.log(JSON.stringify({ level, command: cmd }));
74
+ log(io, JSON.stringify({ level, command: cmd }));
67
75
  } else {
68
76
  const levelColors: Record<string, string> = {
69
77
  high: '\x1b[31m',
70
78
  medium: '\x1b[33m',
71
79
  low: '\x1b[32m',
72
80
  };
73
- console.log(`${levelColors[level]}${level}\x1b[0m ${cmd}`);
81
+ log(io, `${levelColors[level]}${level}\x1b[0m ${cmd}`);
74
82
  }
75
- process.exit(level === 'high' ? 1 : 0);
83
+ // 严重级闸门(历史条件式):high 非零退出
84
+ return level === 'high'
85
+ ? { kind: 'fail', reason: `command risk level is high: ${cmd}` }
86
+ : { kind: 'ok' };
76
87
  }
77
88
 
78
89
  if (options.json) {
79
- console.log(JSON.stringify({
90
+ log(io, JSON.stringify({
80
91
  command: cmd,
81
92
  passed: result.passed,
82
93
  message: result.message,
83
94
  details: result.details,
84
95
  }, null, 2));
85
96
  } else {
86
- console.log(result.passed ? `\x1b[32m✓\x1b[0m ${result.message}` : `\x1b[31m✗\x1b[0m ${result.message}`);
97
+ log(io, result.passed ? `\x1b[32m✓\x1b[0m ${result.message}` : `\x1b[31m✗\x1b[0m ${result.message}`);
87
98
  }
88
99
 
89
- process.exit(result.passed ? 0 : 1);
100
+ return result.passed
101
+ ? { kind: 'ok' }
102
+ : { kind: 'fail', reason: `command gate denied: ${result.message}` };
90
103
  }
@@ -14,14 +14,16 @@ import * as path from 'path';
14
14
  import chalk from 'chalk';
15
15
  import {
16
16
  buildConstraintsUsageReport,
17
+ CANDIDATE_KIND_LABEL,
17
18
  DEFAULT_DIAGNOSE_THRESHOLDS,
18
19
  type ConstraintsUsageReport,
19
20
  type ConstraintUsageStats,
20
21
  type DiagnoseThresholds,
21
- type RetireCandidate,
22
22
  } from '../../core/constraints/usage-report';
23
23
  import { getConstraintsMeta } from './constraints';
24
24
  import { detectInjectionDrift, type InjectionDrift } from '../../core/constraints/injection-drift';
25
+ import { GOVERNANCE_HEADING } from '../../core/constraints/injection-writer';
26
+ import { log, processIO, type CommandIO, type CommandResult } from '../command-contract';
25
27
 
26
28
  export interface ConstraintsReportOptions {
27
29
  projectPath?: string;
@@ -36,13 +38,6 @@ export interface ConstraintsReportOptions {
36
38
  noiseMinTotal?: number;
37
39
  }
38
40
 
39
- const CANDIDATE_KIND_LABEL: Record<RetireCandidate['kind'], string> = {
40
- zero_trigger: '零触发',
41
- unevaluable: '不可评估',
42
- high_noise: '高噪',
43
- zero_intercept: '零拦截',
44
- };
45
-
46
41
  function formatTime(ts: number | undefined): string {
47
42
  return ts === undefined ? '-' : new Date(ts).toISOString().slice(0, 10);
48
43
  }
@@ -74,6 +69,10 @@ export function renderExportMarkdown(
74
69
  lines.push('');
75
70
  lines.push(`- harness 版本: ${version}`);
76
71
  lines.push(`- 日期: ${now.toISOString().slice(0, 10)}`);
72
+ if (report.skippedLines > 0) {
73
+ // 脱敏:只报条数,不报路径、不报坏行内容
74
+ lines.push(`- ⚠️ trace 文件有 ${report.skippedLines} 行损坏已跳过,下表统计只含其余合法记录`);
75
+ }
77
76
  lines.push('');
78
77
 
79
78
  lines.push('## 统计表(check 层)');
@@ -120,7 +119,7 @@ export function renderInjectionDriftLines(drift: InjectionDrift): string[] {
120
119
  if (drift.notInjected) {
121
120
  lines.push(' CLAUDE.md / AGENTS.md 均无约束注入标记段(未注入,不算漂移);可运行 npx @dommaker/harness init 注入');
122
121
  if (drift.duplicateHeading) {
123
- lines.push(' 另检测到多个 "## Governance Rules" 标题(历史遗留重复章节),建议手工清理后重跑 npx @dommaker/harness init');
122
+ lines.push(` 另检测到多个 "${GOVERNANCE_HEADING}" 标题(历史遗留重复章节),建议手工清理后重跑 npx @dommaker/harness init`);
124
123
  }
125
124
  return lines;
126
125
  }
@@ -141,7 +140,7 @@ export function renderInjectionDriftLines(drift: InjectionDrift): string[] {
141
140
  }
142
141
  }
143
142
  if (drift.duplicateHeading) {
144
- lines.push(' 重复章节: 标记段之外还存在另一个 "## Governance Rules" 标题');
143
+ lines.push(` 重复章节: 标记段之外还存在另一个 "${GOVERNANCE_HEADING}" 标题`);
145
144
  }
146
145
  lines.push(` 修复: ${drift.fixHint}`);
147
146
  return lines;
@@ -150,7 +149,7 @@ export function renderInjectionDriftLines(drift: InjectionDrift): string[] {
150
149
  /**
151
150
  * CLI handler: harness constraints report
152
151
  */
153
- export async function constraintsReport(options: ConstraintsReportOptions = {}): Promise<void> {
152
+ export async function constraintsReport(options: ConstraintsReportOptions = {}, io: CommandIO = processIO): Promise<CommandResult> {
154
153
  const projectRoot = options.projectPath || process.cwd();
155
154
  const thresholds: Partial<DiagnoseThresholds> = {};
156
155
  if (options.zeroInterceptMin !== undefined) thresholds.zeroInterceptMinEvaluated = options.zeroInterceptMin;
@@ -160,58 +159,61 @@ export async function constraintsReport(options: ConstraintsReportOptions = {}):
160
159
  const report = buildConstraintsUsageReport(projectRoot, thresholds);
161
160
 
162
161
  if (options.json) {
163
- console.log(JSON.stringify(report, null, 2));
164
- return;
162
+ log(io, JSON.stringify(report, null, 2));
163
+ return { kind: 'ok' };
165
164
  }
166
165
 
167
- console.log(chalk.blue('📊 约束使用报告'));
166
+ log(io, chalk.blue('📊 约束使用报告'));
168
167
  if (!report.traceFileExists) {
169
- console.log(chalk.gray(' (trace 文件不存在,全部按零触发统计)'));
168
+ log(io, chalk.gray(' (trace 文件不存在,全部按零触发统计)'));
169
+ }
170
+ if (report.skippedLines > 0) {
171
+ log(io, chalk.yellow(` ⚠️ trace 文件有 ${report.skippedLines} 行损坏已跳过,统计只含其余合法记录(数据不完整)`));
170
172
  }
171
- console.log();
173
+ log(io);
172
174
 
173
175
  // 统计表
174
- console.log(chalk.bold('统计表(check 层):'));
176
+ log(io, chalk.bold('统计表(check 层):'));
175
177
  for (const s of report.stats) {
176
- console.log(` ${renderStatsRow(s)}`);
178
+ log(io, ` ${renderStatsRow(s)}`);
177
179
  }
178
- console.log();
180
+ log(io);
179
181
 
180
182
  // 候选诊断
181
- console.log(chalk.bold(`退役候选(${report.candidates.length} 条,阈值: 零拦截≥${thresholds.zeroInterceptMinEvaluated ?? DEFAULT_DIAGNOSE_THRESHOLDS.zeroInterceptMinEvaluated} 次评估, 高噪 fail率>${Math.round((thresholds.highNoiseFailRate ?? DEFAULT_DIAGNOSE_THRESHOLDS.highNoiseFailRate) * 100)}% 且 ≥${thresholds.highNoiseMinEvaluated ?? DEFAULT_DIAGNOSE_THRESHOLDS.highNoiseMinEvaluated} 次评估):`));
183
+ log(io, chalk.bold(`退役候选(${report.candidates.length} 条,阈值: 零拦截≥${thresholds.zeroInterceptMinEvaluated ?? DEFAULT_DIAGNOSE_THRESHOLDS.zeroInterceptMinEvaluated} 次评估, 高噪 fail率>${Math.round((thresholds.highNoiseFailRate ?? DEFAULT_DIAGNOSE_THRESHOLDS.highNoiseFailRate) * 100)}% 且 ≥${thresholds.highNoiseMinEvaluated ?? DEFAULT_DIAGNOSE_THRESHOLDS.highNoiseMinEvaluated} 次评估):`));
182
184
  if (report.candidates.length === 0) {
183
- console.log(chalk.green(' (无候选)'));
185
+ log(io, chalk.green(' (无候选)'));
184
186
  } else {
185
187
  for (const c of report.candidates) {
186
- console.log(` [${CANDIDATE_KIND_LABEL[c.kind]}] ${c.id} — ${c.reason}`);
188
+ log(io, ` [${CANDIDATE_KIND_LABEL[c.kind]}] ${c.id} — ${c.reason}`);
187
189
  }
188
- console.log(chalk.gray(' 可运行 `harness constraints retire` 交互式处理候选'));
190
+ log(io, chalk.gray(' 可运行 `harness constraints retire` 交互式处理候选'));
189
191
  }
190
- console.log();
192
+ log(io);
191
193
 
192
194
  // 注入清单
193
- console.log(chalk.bold(`当前生效 prompt 注入(${report.activePromptIds.length} 条):`));
195
+ log(io, chalk.bold(`当前生效 prompt 注入(${report.activePromptIds.length} 条):`));
194
196
  for (const id of report.activePromptIds) {
195
- console.log(` ${id}`);
197
+ log(io, ` ${id}`);
196
198
  }
197
- console.log();
199
+ log(io);
198
200
 
199
201
  // 配置健康
200
202
  if (report.lint.unknownIds.length > 0) {
201
- console.log(chalk.yellow('⚠️ 配置健康:'));
203
+ log(io, chalk.yellow('⚠️ 配置健康:'));
202
204
  for (const id of report.lint.unknownIds) {
203
- console.log(chalk.yellow(` config.yml 中禁用了不存在/已移除的约束 id: ${id}(残留配置,建议清理)`));
205
+ log(io, chalk.yellow(` config.yml 中禁用了不存在/已移除的约束 id: ${id}(残留配置,建议清理)`));
204
206
  }
205
- console.log();
207
+ log(io);
206
208
  }
207
209
 
208
210
  // 注入漂移(ADR-0001 决策 7:条目级差异 + 修复指引;--export 摘要不含本地漂移状态)
209
- console.log(chalk.bold('注入漂移:'));
211
+ log(io, chalk.bold('注入漂移:'));
210
212
  const drift = detectInjectionDrift(projectRoot);
211
213
  for (const line of renderInjectionDriftLines(drift)) {
212
- console.log(drift.hasDrift ? chalk.yellow(line) : line);
214
+ log(io, drift.hasDrift ? chalk.yellow(line) : line);
213
215
  }
214
- console.log();
216
+ log(io);
215
217
 
216
218
  // --export
217
219
  if (options.export !== undefined && options.export !== false) {
@@ -229,6 +231,7 @@ export async function constraintsReport(options: ConstraintsReportOptions = {}):
229
231
 
230
232
  fs.mkdirSync(path.dirname(exportPath), { recursive: true });
231
233
  fs.writeFileSync(exportPath, markdown, 'utf-8');
232
- console.log(chalk.green(`✅ 脱敏报告已导出: ${exportPath}`));
234
+ log(io, chalk.green(`✅ 脱敏报告已导出: ${exportPath}`));
233
235
  }
236
+ return { kind: 'ok' };
234
237
  }
@@ -2,7 +2,7 @@
2
2
  * harness constraints retire —— 约束退役(ADR-0001 决策 2/5)
3
3
  *
4
4
  * 建议层全自动(候选诊断复用 report 数据层),执行层保留一次人确认。
5
- * 落盘形态(#82 D6 一处真相):
5
+ * 落盘形态(studio#82 D6 一处真相):
6
6
  *
7
7
  * - 内置约束 → config.yml `enabled: false` + `retired` 元数据:
8
8
  *
@@ -33,21 +33,24 @@
33
33
  import * as fs from 'fs';
34
34
  import * as path from 'path';
35
35
  import * as readline from 'readline';
36
+ import { log, logError, processIO, type CommandIO, type CommandResult } from '../command-contract';
36
37
  import * as yaml from 'js-yaml';
37
38
  import chalk from 'chalk';
38
39
  import { getConstraint } from '../../core/constraints/definitions';
39
- import { renderConstraintsSection, CONSTRAINTS_END_MARKER } from '../../core/constraints/injection-renderer';
40
- import { resolveInjectionTarget } from '../../core/constraints/injection-drift';
40
+ import { renderConstraintsSection, CONSTRAINTS_START_MARKER, CONSTRAINTS_END_MARKER } from '../../core/constraints/injection-renderer';
41
+ import { replaceStandaloneRange, resolveInjectionTarget } from '../../core/constraints/injection-writer';
41
42
  import { getEffectiveConstraints } from '../../core/effective-constraints';
42
43
  import { loadRawProjectConfig, ProjectConfigLoader } from '../../core/project-config-loader';
44
+ import { getHarnessPackageVersion } from '../../utils/package-version';
43
45
  import { FileKnowledgeStore } from '../../knowledge/store';
44
46
  import type { KnowledgeEntry } from '../../knowledge/types';
45
47
  import type { Constraint } from '../../types/constraint';
46
48
  import {
47
49
  buildConstraintsUsageReport,
50
+ CANDIDATE_KIND_LABEL,
48
51
  collectUsageByConstraint,
49
52
  readProjectTraces,
50
- type RetireCandidate,
53
+ readProjectTracesReport,
51
54
  } from '../../core/constraints/usage-report';
52
55
 
53
56
  export interface RetireExecuteOptions {
@@ -64,7 +67,7 @@ export interface RetireResult {
64
67
  status: RetireStatus;
65
68
  /** 是否 check 层 iron(交互模式据此追加确认) */
66
69
  isIronLaw: boolean;
67
- /** 退役落点:custom 约束落 custom-constraints.yml(#82 D6),内置落 config.yml */
70
+ /** 退役落点:custom 约束落 custom-constraints.yml(studio#82 D6),内置落 config.yml */
68
71
  landing: 'config.yml' | 'custom-constraints.yml';
69
72
  stats: { total: number; fail: number; failRate: number };
70
73
  /** 治理注入段(CLAUDE.md / AGENTS.md)是否已同步 */
@@ -128,18 +131,6 @@ function findRetireTarget(projectRoot: string, id: string): RetireTargetInfo | u
128
131
  return undefined;
129
132
  }
130
133
 
131
- /**
132
- * 读取 harness 版本(与 init.ts 同路径策略)
133
- */
134
- function getHarnessVersion(): string {
135
- try {
136
- const pkgPath = path.join(__dirname, '..', '..', '..', 'package.json');
137
- return JSON.parse(fs.readFileSync(pkgPath, 'utf-8')).version ?? 'unknown';
138
- } catch {
139
- return 'unknown';
140
- }
141
- }
142
-
143
134
  /**
144
135
  * 写 config.yml 退役段(ADR-0001 决策 5)
145
136
  *
@@ -174,7 +165,7 @@ function writeRetireConfig(
174
165
  }
175
166
 
176
167
  /**
177
- * 写 custom-constraints.yml 退役段(#82 D6 统一落点)
168
+ * 写 custom-constraints.yml 退役段(studio#82 D6 统一落点)
178
169
  *
179
170
  * 在 custom_constraints.<id> 条目内追加 retired 元数据,保留规则原文。
180
171
  * js-yaml 不保留注释:原文件含注释行时重写会丢失,console 说明。
@@ -280,12 +271,10 @@ function syncGovernanceInjection(projectRoot: string): { synced: boolean; file?:
280
271
  const target = resolveInjectionTarget(projectRoot);
281
272
  if (!target) return { synced: false };
282
273
 
283
- const filePath = path.join(projectRoot, target.file);
284
- const body = renderConstraintsSection(getEffectiveConstraints(projectRoot), getHarnessVersion());
285
- const after = target.content.slice(target.endIdx + CONSTRAINTS_END_MARKER.length).replace(/^\n+/, '');
286
- const newContent = target.content.slice(0, target.startIdx) + body + (after ? '\n' + after : '');
287
- if (newContent !== target.content) {
288
- fs.writeFileSync(filePath, newContent, 'utf-8');
274
+ const body = renderConstraintsSection(getEffectiveConstraints(projectRoot), getHarnessPackageVersion());
275
+ const write = replaceStandaloneRange(target.content, CONSTRAINTS_START_MARKER, CONSTRAINTS_END_MARKER, body);
276
+ if (write.kind === 'updated' && write.content !== target.content) {
277
+ fs.writeFileSync(path.join(projectRoot, target.file), write.content, 'utf-8');
289
278
  }
290
279
  return { synced: true, file: target.file };
291
280
  }
@@ -327,6 +316,9 @@ export function retireConstraint(
327
316
  }
328
317
 
329
318
  // 历史统计(来自 traces.log)
319
+ // 计数去向:兼容包装 readProjectTraces() 丢计数(harness#100)——落盘的 retire stats 只计
320
+ // 合法记录,把坏行数写进 RetireResult.stats 属形状变更不在本票;本函数按契约「纯执行无交互」
321
+ // 不打印,告知由两条命令入口各自负责(runRetireInteractive 顶部 / constraintsRetire 的 --yes 分支)
330
322
  const usage = collectUsageByConstraint(readProjectTraces(projectRoot)).get(id);
331
323
  const evaluated = usage ? usage.total - usage.skip : 0;
332
324
  const stats = {
@@ -366,29 +358,30 @@ export function retireConstraint(
366
358
  /**
367
359
  * 打印单条退役结果(含回滚语义提示)
368
360
  */
369
- export function printRetireResult(result: RetireResult): void {
361
+ export function printRetireResult(result: RetireResult, io: CommandIO = processIO): CommandResult {
370
362
  switch (result.status) {
371
363
  case 'unknown_id':
372
- console.log(chalk.red(`❌ ${result.id}: 约束不存在(既非内置也非 custom-constraints),未做任何变更`));
373
- return;
364
+ log(io, chalk.red(`❌ ${result.id}: 约束不存在(既非内置也非 custom-constraints),未做任何变更`));
365
+ return { kind: 'skip', reason: `${result.id}: 约束不存在,未做任何变更` };
374
366
  case 'already_retired':
375
- console.log(chalk.yellow(`⚠️ ${result.id}: 已处于退役状态(config.yml 中 enabled: false),跳过`));
376
- return;
367
+ log(io, chalk.yellow(`⚠️ ${result.id}: 已处于退役状态(config.yml 中 enabled: false),跳过`));
368
+ return { kind: 'skip', reason: `${result.id}: 已处于退役状态,跳过` };
377
369
  case 'retired': {
378
- console.log(chalk.green(`✅ ${result.id}: 已退役`));
379
- console.log(` 历史统计: total=${result.stats.total} fail=${result.stats.fail} fail率=${Math.round(result.stats.failRate * 100)}%`);
380
- console.log(` 知识沉淀: ${result.knowledgeEntryId}(.harness/knowledge)`);
370
+ log(io, chalk.green(`✅ ${result.id}: 已退役`));
371
+ log(io, ` 历史统计: total=${result.stats.total} fail=${result.stats.fail} fail率=${Math.round(result.stats.failRate * 100)}%`);
372
+ log(io, ` 知识沉淀: ${result.knowledgeEntryId}(.harness/knowledge)`);
381
373
  if (result.injectionSynced) {
382
- console.log(` 已同步 ${result.injectionFile ?? '治理文档'} 注入段`);
374
+ log(io, ` 已同步 ${result.injectionFile ?? '治理文档'} 注入段`);
383
375
  }
384
376
  const recovery =
385
377
  result.landing === 'custom-constraints.yml'
386
378
  ? `删除 custom-constraints.yml 中 custom_constraints.${result.id}.retired 段`
387
379
  : `删除 config.yml 中 constraints.${result.id} 段`;
388
- console.log(chalk.gray(` retire 不是删除——恢复方法:${recovery}`));
380
+ log(io, chalk.gray(` retire 不是删除——恢复方法:${recovery}`));
389
381
  break;
390
382
  }
391
383
  }
384
+ return { kind: 'ok' };
392
385
  }
393
386
 
394
387
  // ========================================
@@ -408,13 +401,6 @@ function createAsk(io: RetireIO): { ask: (q: string) => Promise<string>; close:
408
401
  };
409
402
  }
410
403
 
411
- const CANDIDATE_KIND_LABEL: Record<RetireCandidate['kind'], string> = {
412
- zero_trigger: '零触发',
413
- unevaluable: '不可评估',
414
- high_noise: '高噪',
415
- zero_intercept: '零拦截',
416
- };
417
-
418
404
  /**
419
405
  * 交互式退役(候选列表 → 编号多选 → 摘要 → 确认 → 执行)
420
406
  *
@@ -423,9 +409,16 @@ const CANDIDATE_KIND_LABEL: Record<RetireCandidate['kind'], string> = {
423
409
  export async function runRetireInteractive(
424
410
  projectRoot: string,
425
411
  io: RetireIO = { input: process.stdin, output: process.stdout }
426
- ): Promise<void> {
412
+ ): Promise<CommandResult> {
427
413
  const report = buildConstraintsUsageReport(projectRoot);
414
+ // 候选诊断是退役决策的依据:数据不完整必须先说(harness#100,与 report 同一降级维度);
415
+ // 告知行走 stderr,与 status / failure list / 直达分支同一去向(交互正文仍走 console.log)
416
+ if (report.skippedLines > 0) {
417
+ console.error(chalk.yellow(`⚠️ trace 文件有 ${report.skippedLines} 行损坏已跳过,候选只基于其余合法记录`));
418
+ }
428
419
  const { ask, close } = createAsk(io);
420
+ // 退役结果打印走注入流(与 readline 提示同一去向)
421
+ const out = { stdout: io.output, stderr: io.output } as unknown as CommandIO;
429
422
 
430
423
  try {
431
424
  let selectedIds: string[] = [];
@@ -435,7 +428,7 @@ export async function runRetireInteractive(
435
428
  const manual = await ask('可手动输入要退役的约束 id(留空取消): ');
436
429
  if (!manual) {
437
430
  console.log('已取消');
438
- return;
431
+ return { kind: 'skip', reason: '交互未确认,未做任何变更' };
439
432
  }
440
433
  selectedIds = [manual];
441
434
  } else {
@@ -447,7 +440,7 @@ export async function runRetireInteractive(
447
440
  const answer = await ask('输入编号(逗号分隔多选)或约束 id,留空取消: ');
448
441
  if (!answer) {
449
442
  console.log('已取消');
450
- return;
443
+ return { kind: 'skip', reason: '交互未确认,未做任何变更' };
451
444
  }
452
445
 
453
446
  for (const token of answer.split(',').map(s => s.trim()).filter(Boolean)) {
@@ -482,7 +475,7 @@ export async function runRetireInteractive(
482
475
 
483
476
  if (plan.length === 0) {
484
477
  console.log('无可执行项,已取消');
485
- return;
478
+ return { kind: 'skip', reason: '交互未确认,未做任何变更' };
486
479
  }
487
480
 
488
481
  // 变更摘要 → 最终确认
@@ -500,17 +493,18 @@ export async function runRetireInteractive(
500
493
  const finalConfirm = await ask('确认执行?(y/N) ');
501
494
  if (finalConfirm.toLowerCase() !== 'y' && finalConfirm.toLowerCase() !== 'yes') {
502
495
  console.log('已取消');
503
- return;
496
+ return { kind: 'skip', reason: '交互未确认,未做任何变更' };
504
497
  }
505
498
 
506
499
  console.log();
507
500
  for (const p of plan) {
508
501
  const result = retireConstraint(projectRoot, p.id, { reason: p.reason });
509
- printRetireResult(result);
502
+ printRetireResult(result, out);
510
503
  }
511
504
  } finally {
512
505
  close();
513
506
  }
507
+ return { kind: 'ok' };
514
508
  }
515
509
 
516
510
  /**
@@ -519,29 +513,38 @@ export async function runRetireInteractive(
519
513
  * 人确认闸门(#24,ADR-0001 决策 2):带 id 的直达路径必须显式 `--yes`,
520
514
  * 无 `--yes` 报错 + 非零退出码,提示改用 `--yes` 或交互模式;不落盘任何文件。
521
515
  */
522
- export async function constraintsRetire(id?: string, options: ConstraintsRetireOptions = {}): Promise<void> {
516
+ export async function constraintsRetire(
517
+ id?: string,
518
+ options: ConstraintsRetireOptions = {},
519
+ io: CommandIO = processIO,
520
+ ): Promise<CommandResult> {
523
521
  const projectRoot = options.projectPath || process.cwd();
524
522
 
525
523
  if (id) {
526
524
  // 非交互直达:执行层人确认对所有入口成立(含程序化调用方)
527
525
  if (!options.yes) {
528
- console.error(
526
+ logError(io,
529
527
  chalk.red(`❌ 直达退役需要显式人确认(ADR-0001 决策 2:执行层保留一次人确认),未做任何变更\n`) +
530
528
  ` 带 --yes 显式确认直达:harness constraints retire ${id} --yes` +
531
529
  `${options.reason ? ` --reason "${options.reason}"` : ''}\n` +
532
530
  ` 或去掉 id 走交互确认:harness constraints retire`
533
531
  );
534
- process.exitCode = 1;
535
- return;
532
+ return { kind: 'usage-error', reason: `直达退役 ${id} 缺少显式 --yes 人确认,未做任何变更` };
533
+ }
534
+
535
+ // 落盘的 retire stats 只含合法记录:依据不完整要在执行前告知(harness#100,
536
+ // 直达路径不经过 runRetireInteractive,故两处各自告知)
537
+ const { skippedLines } = readProjectTracesReport(projectRoot);
538
+ if (skippedLines > 0) {
539
+ logError(io, chalk.yellow(`⚠️ trace 文件有 ${skippedLines} 行损坏已跳过,落盘的退役统计只基于其余合法记录`));
536
540
  }
537
541
 
538
542
  const result = retireConstraint(projectRoot, id, { reason: options.reason });
539
543
  if (result.status === 'retired' && result.isIronLaw) {
540
- console.log(chalk.yellow(`⚠️ ${id} 是一条 Iron Law,已通过 --yes 直达退役(交互模式会要求二次确认)`));
544
+ log(io, chalk.yellow(`⚠️ ${id} 是一条 Iron Law,已通过 --yes 直达退役(交互模式会要求二次确认)`));
541
545
  }
542
- printRetireResult(result);
543
- return;
546
+ return printRetireResult(result, io);
544
547
  }
545
548
 
546
- await runRetireInteractive(projectRoot);
549
+ return runRetireInteractive(projectRoot);
547
550
  }
@@ -5,10 +5,10 @@
5
5
  * 供 Studio 等消费者获取约束的 hash、计数、文本大小等元数据。
6
6
  */
7
7
 
8
- import { readFileSync, existsSync } from 'fs';
9
- import { join } from 'path';
10
8
  import { createHash } from 'crypto';
11
9
  import { getAllConstraints } from '../../core/constraints/definitions';
10
+ import { getHarnessPackageVersion } from '../../utils/package-version';
11
+ import { log, processIO, type CommandIO, type CommandResult } from '../command-contract';
12
12
 
13
13
  export interface ConstraintsMeta {
14
14
  version: string;
@@ -17,24 +17,6 @@ export interface ConstraintsMeta {
17
17
  textSize: { total: number; perConstraint: number };
18
18
  }
19
19
 
20
- function getPackageVersion(): string {
21
- // Try several common locations
22
- const candidates = [
23
- join(__dirname, '..', '..', '..', 'package.json'),
24
- join(process.cwd(), 'package.json'),
25
- ];
26
- for (const p of candidates) {
27
- if (existsSync(p)) {
28
- try {
29
- return JSON.parse(readFileSync(p, 'utf-8')).version;
30
- } catch {
31
- // continue to next candidate
32
- }
33
- }
34
- }
35
- return 'unknown';
36
- }
37
-
38
20
  export function getConstraintsMeta(): ConstraintsMeta {
39
21
  const constraints = getAllConstraints();
40
22
 
@@ -56,7 +38,7 @@ export function getConstraintsMeta(): ConstraintsMeta {
56
38
  const hash = createHash('sha256').update(hashInput).digest('hex');
57
39
 
58
40
  return {
59
- version: getPackageVersion(),
41
+ version: getHarnessPackageVersion(),
60
42
  hash,
61
43
  counts: {
62
44
  ironLaws: ironLaws.length,
@@ -73,14 +55,15 @@ export function getConstraintsMeta(): ConstraintsMeta {
73
55
  /**
74
56
  * CLI handler: output --json
75
57
  */
76
- export async function constraints(options: { json?: boolean }): Promise<void> {
58
+ export async function constraints(options: { json?: boolean }, io: CommandIO = processIO): Promise<CommandResult> {
77
59
  const meta = getConstraintsMeta();
78
60
  if (options.json) {
79
- console.log(JSON.stringify(meta, null, 2));
61
+ log(io, JSON.stringify(meta, null, 2));
80
62
  } else {
81
- console.log(`version: ${meta.version}`);
82
- console.log(`hash: ${meta.hash}`);
83
- console.log(`ironLaws: ${meta.counts.ironLaws}, guidelines: ${meta.counts.guidelines}, prompts: ${meta.counts.prompts}`);
84
- console.log(`textSize: ${meta.textSize.total} chars total, ~${meta.textSize.perConstraint} chars/constraint`);
63
+ log(io, `version: ${meta.version}`);
64
+ log(io, `hash: ${meta.hash}`);
65
+ log(io, `ironLaws: ${meta.counts.ironLaws}, guidelines: ${meta.counts.guidelines}, prompts: ${meta.counts.prompts}`);
66
+ log(io, `textSize: ${meta.textSize.total} chars total, ~${meta.textSize.perConstraint} chars/constraint`);
85
67
  }
68
+ return { kind: 'ok' };
86
69
  }