@dommaker/harness 1.3.0 → 1.5.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 (532) hide show
  1. package/CHANGELOG.md +20 -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 +104 -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 +27 -219
  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 +17 -30
  53. package/dist/cli/commands/knowledge.d.ts.map +1 -1
  54. package/dist/cli/commands/knowledge.js +183 -273
  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 +9 -3
  115. package/dist/cli/commands/update-user-model.d.ts.map +1 -1
  116. package/dist/cli/commands/update-user-model.js +56 -61
  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 +68 -117
  143. package/dist/core/constraints/checker.js.map +1 -1
  144. package/dist/core/constraints/checkers/capability-sync.d.ts +20 -8
  145. package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
  146. package/dist/core/constraints/checkers/capability-sync.js +66 -77
  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 +49 -66
  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/index.d.ts +2 -2
  160. package/dist/core/constraints/checkers/index.d.ts.map +1 -1
  161. package/dist/core/constraints/checkers/index.js +2 -1
  162. package/dist/core/constraints/checkers/index.js.map +1 -1
  163. package/dist/core/constraints/checkers/no-test-simplification.d.ts.map +1 -1
  164. package/dist/core/constraints/checkers/no-test-simplification.js +4 -3
  165. package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
  166. package/dist/core/constraints/checkers/types.d.ts +43 -4
  167. package/dist/core/constraints/checkers/types.d.ts.map +1 -1
  168. package/dist/core/constraints/checkers/types.js +33 -0
  169. package/dist/core/constraints/checkers/types.js.map +1 -1
  170. package/dist/core/constraints/context-builder.d.ts +15 -14
  171. package/dist/core/constraints/context-builder.d.ts.map +1 -1
  172. package/dist/core/constraints/context-builder.js +32 -96
  173. package/dist/core/constraints/context-builder.js.map +1 -1
  174. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  175. package/dist/core/constraints/doc-freshness/runner.js +2 -1
  176. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  177. package/dist/core/constraints/git-evidence.d.ts +60 -0
  178. package/dist/core/constraints/git-evidence.d.ts.map +1 -0
  179. package/dist/core/constraints/git-evidence.js +88 -0
  180. package/dist/core/constraints/git-evidence.js.map +1 -0
  181. package/dist/core/constraints/injection-drift.d.ts +3 -20
  182. package/dist/core/constraints/injection-drift.d.ts.map +1 -1
  183. package/dist/core/constraints/injection-drift.js +10 -57
  184. package/dist/core/constraints/injection-drift.js.map +1 -1
  185. package/dist/core/constraints/injection-writer.d.ts +113 -0
  186. package/dist/core/constraints/injection-writer.d.ts.map +1 -0
  187. package/dist/core/constraints/injection-writer.js +228 -0
  188. package/dist/core/constraints/injection-writer.js.map +1 -0
  189. package/dist/core/constraints/usage-report.d.ts +22 -1
  190. package/dist/core/constraints/usage-report.d.ts.map +1 -1
  191. package/dist/core/constraints/usage-report.js +27 -20
  192. package/dist/core/constraints/usage-report.js.map +1 -1
  193. package/dist/core/index.d.ts +2 -2
  194. package/dist/core/index.d.ts.map +1 -1
  195. package/dist/core/index.js +4 -2
  196. package/dist/core/index.js.map +1 -1
  197. package/dist/core/project-config-loader.d.ts +32 -1
  198. package/dist/core/project-config-loader.d.ts.map +1 -1
  199. package/dist/core/project-config-loader.js +39 -9
  200. package/dist/core/project-config-loader.js.map +1 -1
  201. package/dist/core/validators/passes-gate.d.ts +15 -64
  202. package/dist/core/validators/passes-gate.d.ts.map +1 -1
  203. package/dist/core/validators/passes-gate.js +84 -222
  204. package/dist/core/validators/passes-gate.js.map +1 -1
  205. package/dist/core/validators/test-output.d.ts +58 -0
  206. package/dist/core/validators/test-output.d.ts.map +1 -0
  207. package/dist/core/validators/test-output.js +109 -0
  208. package/dist/core/validators/test-output.js.map +1 -0
  209. package/dist/failure/recorder.d.ts +6 -0
  210. package/dist/failure/recorder.d.ts.map +1 -1
  211. package/dist/failure/recorder.js +12 -8
  212. package/dist/failure/recorder.js.map +1 -1
  213. package/dist/gates/acceptance.d.ts +1 -48
  214. package/dist/gates/acceptance.d.ts.map +1 -1
  215. package/dist/gates/acceptance.js +6 -38
  216. package/dist/gates/acceptance.js.map +1 -1
  217. package/dist/gates/checker-gate.d.ts +2 -1
  218. package/dist/gates/checker-gate.d.ts.map +1 -1
  219. package/dist/gates/checker-gate.js +11 -13
  220. package/dist/gates/checker-gate.js.map +1 -1
  221. package/dist/gates/command.d.ts.map +1 -1
  222. package/dist/gates/command.js +10 -22
  223. package/dist/gates/command.js.map +1 -1
  224. package/dist/gates/contract.d.ts.map +1 -1
  225. package/dist/gates/contract.js +22 -61
  226. package/dist/gates/contract.js.map +1 -1
  227. package/dist/gates/performance.d.ts.map +1 -1
  228. package/dist/gates/performance.js +13 -31
  229. package/dist/gates/performance.js.map +1 -1
  230. package/dist/gates/review.d.ts.map +1 -1
  231. package/dist/gates/review.js +21 -53
  232. package/dist/gates/review.js.map +1 -1
  233. package/dist/gates/security.d.ts.map +1 -1
  234. package/dist/gates/security.js +14 -32
  235. package/dist/gates/security.js.map +1 -1
  236. package/dist/gates/types.d.ts +28 -5
  237. package/dist/gates/types.d.ts.map +1 -1
  238. package/dist/gates/types.js +38 -1
  239. package/dist/gates/types.js.map +1 -1
  240. package/dist/hooks/bootstrap.d.ts +2 -0
  241. package/dist/hooks/bootstrap.d.ts.map +1 -1
  242. package/dist/hooks/bootstrap.js +5 -2
  243. package/dist/hooks/bootstrap.js.map +1 -1
  244. package/dist/index.d.ts +1 -2
  245. package/dist/index.d.ts.map +1 -1
  246. package/dist/index.js.map +1 -1
  247. package/dist/knowledge/audit.d.ts +7 -2
  248. package/dist/knowledge/audit.d.ts.map +1 -1
  249. package/dist/knowledge/audit.js +59 -79
  250. package/dist/knowledge/audit.js.map +1 -1
  251. package/dist/knowledge/flywheel-metrics.d.ts +39 -0
  252. package/dist/knowledge/flywheel-metrics.d.ts.map +1 -0
  253. package/dist/knowledge/flywheel-metrics.js +47 -0
  254. package/dist/knowledge/flywheel-metrics.js.map +1 -0
  255. package/dist/knowledge/import.d.ts.map +1 -1
  256. package/dist/knowledge/import.js +4 -14
  257. package/dist/knowledge/import.js.map +1 -1
  258. package/dist/knowledge/index-generator.d.ts.map +1 -1
  259. package/dist/knowledge/index-generator.js +17 -14
  260. package/dist/knowledge/index-generator.js.map +1 -1
  261. package/dist/knowledge/ingest.d.ts.map +1 -1
  262. package/dist/knowledge/ingest.js +9 -4
  263. package/dist/knowledge/ingest.js.map +1 -1
  264. package/dist/knowledge/migration.d.ts.map +1 -1
  265. package/dist/knowledge/migration.js +11 -8
  266. package/dist/knowledge/migration.js.map +1 -1
  267. package/dist/knowledge/reference-tracker.d.ts.map +1 -1
  268. package/dist/knowledge/reference-tracker.js +12 -9
  269. package/dist/knowledge/reference-tracker.js.map +1 -1
  270. package/dist/knowledge/store.d.ts +15 -0
  271. package/dist/knowledge/store.d.ts.map +1 -1
  272. package/dist/knowledge/store.js +59 -10
  273. package/dist/knowledge/store.js.map +1 -1
  274. package/dist/monitoring/context-tracker.d.ts.map +1 -1
  275. package/dist/monitoring/context-tracker.js +11 -20
  276. package/dist/monitoring/context-tracker.js.map +1 -1
  277. package/dist/monitoring/index.d.ts +1 -1
  278. package/dist/monitoring/index.d.ts.map +1 -1
  279. package/dist/monitoring/index.js +1 -2
  280. package/dist/monitoring/index.js.map +1 -1
  281. package/dist/monitoring/trace-analyzer.d.ts +15 -0
  282. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  283. package/dist/monitoring/trace-analyzer.js +19 -2
  284. package/dist/monitoring/trace-analyzer.js.map +1 -1
  285. package/dist/monitoring/traces.d.ts +21 -2
  286. package/dist/monitoring/traces.d.ts.map +1 -1
  287. package/dist/monitoring/traces.js +38 -29
  288. package/dist/monitoring/traces.js.map +1 -1
  289. package/dist/release/index.d.ts +2 -2
  290. package/dist/release/index.d.ts.map +1 -1
  291. package/dist/release/index.js +1 -5
  292. package/dist/release/index.js.map +1 -1
  293. package/dist/sdd/index-generator.js +10 -11
  294. package/dist/sdd/index-generator.js.map +1 -1
  295. package/dist/test-setup/project-fixture.d.ts +48 -0
  296. package/dist/test-setup/project-fixture.d.ts.map +1 -0
  297. package/dist/test-setup/project-fixture.js +106 -0
  298. package/dist/test-setup/project-fixture.js.map +1 -0
  299. package/dist/types/constraint.d.ts +8 -18
  300. package/dist/types/constraint.d.ts.map +1 -1
  301. package/dist/types/constraint.js +6 -1
  302. package/dist/types/constraint.js.map +1 -1
  303. package/dist/types/passes-gate.d.ts +10 -25
  304. package/dist/types/passes-gate.d.ts.map +1 -1
  305. package/dist/types/project-config.d.ts +1 -1
  306. package/dist/types/session.d.ts +6 -5
  307. package/dist/types/session.d.ts.map +1 -1
  308. package/dist/types/trace.d.ts +9 -0
  309. package/dist/types/trace.d.ts.map +1 -1
  310. package/dist/utils/exec.d.ts +10 -0
  311. package/dist/utils/exec.d.ts.map +1 -1
  312. package/dist/utils/exec.js +12 -0
  313. package/dist/utils/exec.js.map +1 -1
  314. package/dist/utils/file-walk.d.ts +9 -1
  315. package/dist/utils/file-walk.d.ts.map +1 -1
  316. package/dist/utils/file-walk.js +10 -1
  317. package/dist/utils/file-walk.js.map +1 -1
  318. package/dist/utils/frontmatter.d.ts +73 -0
  319. package/dist/utils/frontmatter.d.ts.map +1 -0
  320. package/dist/utils/frontmatter.js +141 -0
  321. package/dist/utils/frontmatter.js.map +1 -0
  322. package/dist/utils/jsonl.d.ts +92 -0
  323. package/dist/utils/jsonl.d.ts.map +1 -0
  324. package/dist/utils/jsonl.js +166 -0
  325. package/dist/utils/jsonl.js.map +1 -0
  326. package/dist/utils/package-version.d.ts +16 -0
  327. package/dist/utils/package-version.d.ts.map +1 -0
  328. package/dist/utils/package-version.js +63 -0
  329. package/dist/utils/package-version.js.map +1 -0
  330. package/package.json +1 -1
  331. package/src/CONTEXT.md +8 -2
  332. package/src/__tests__/acceptance-gate.test.ts +3 -1
  333. package/src/__tests__/checker-extra.test.ts +7 -1218
  334. package/src/__tests__/context-files-resolution.test.ts +116 -0
  335. package/src/__tests__/effective-constraints.test.ts +10 -13
  336. package/src/__tests__/exec.test.ts +35 -1
  337. package/src/__tests__/layering.test.ts +152 -0
  338. package/src/__tests__/passes-gate-extra.test.ts +0 -145
  339. package/src/__tests__/passes-gate.test.ts +130 -101
  340. package/src/__tests__/skip-semantics.test.ts +5 -9
  341. package/src/cli/__tests__/command-contract.test.ts +170 -0
  342. package/src/cli/command-contract.ts +108 -0
  343. package/src/cli/commands/CONTEXT.md +23 -8
  344. package/src/cli/commands/__tests__/acceptance.test.ts +40 -33
  345. package/src/cli/commands/__tests__/analyze-sessions.test.ts +35 -29
  346. package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +107 -0
  347. package/src/cli/commands/__tests__/check-drift.test.ts +27 -34
  348. package/src/cli/commands/__tests__/check-skip-output.test.ts +21 -24
  349. package/src/cli/commands/__tests__/check.test.ts +448 -378
  350. package/src/cli/commands/__tests__/command.test.ts +85 -70
  351. package/src/cli/commands/__tests__/constraints-report.test.ts +100 -71
  352. package/src/cli/commands/__tests__/constraints-retire.test.ts +152 -95
  353. package/src/cli/commands/__tests__/contract.test.ts +27 -34
  354. package/src/cli/commands/__tests__/failure.test.ts +31 -34
  355. package/src/cli/commands/__tests__/init-injection.test.ts +58 -28
  356. package/src/cli/commands/__tests__/init.test.ts +77 -48
  357. package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +108 -0
  358. package/src/cli/commands/__tests__/knowledge.test.ts +69 -43
  359. package/src/cli/commands/__tests__/passes-gate.test.ts +43 -45
  360. package/src/cli/commands/__tests__/performance.test.ts +22 -23
  361. package/src/cli/commands/__tests__/posteval-plan.test.ts +39 -38
  362. package/src/cli/commands/__tests__/project-path-anchoring.test.ts +157 -0
  363. package/src/cli/commands/__tests__/project-path-convention.test.ts +231 -0
  364. package/src/cli/commands/__tests__/registry.test.ts +75 -35
  365. package/src/cli/commands/__tests__/release.test.ts +209 -80
  366. package/src/cli/commands/__tests__/report-extra.test.ts +16 -16
  367. package/src/cli/commands/__tests__/report.test.ts +25 -25
  368. package/src/cli/commands/__tests__/review.test.ts +33 -31
  369. package/src/cli/commands/__tests__/security.test.ts +23 -27
  370. package/src/cli/commands/__tests__/spec-baseline-check.test.ts +20 -20
  371. package/src/cli/commands/__tests__/spec.test.ts +41 -29
  372. package/src/cli/commands/__tests__/status-extra.test.ts +18 -18
  373. package/src/cli/commands/__tests__/status-skipped-lines.test.ts +72 -0
  374. package/src/cli/commands/__tests__/status.test.ts +34 -34
  375. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +77 -78
  376. package/src/cli/commands/__tests__/sync-docs.test.ts +72 -74
  377. package/src/cli/commands/__tests__/update-user-model.test.ts +97 -34
  378. package/src/cli/commands/__tests__/validate.test.ts +27 -29
  379. package/src/cli/commands/acceptance.ts +35 -24
  380. package/src/cli/commands/analyze-sessions.ts +25 -24
  381. package/src/cli/commands/check.ts +127 -66
  382. package/src/cli/commands/command.ts +30 -17
  383. package/src/cli/commands/constraints-report.ts +37 -34
  384. package/src/cli/commands/constraints-retire.ts +58 -55
  385. package/src/cli/commands/constraints.ts +10 -27
  386. package/src/cli/commands/contract.ts +51 -42
  387. package/src/cli/commands/definitions.ts +34 -227
  388. package/src/cli/commands/failure.ts +33 -26
  389. package/src/cli/commands/init.ts +166 -220
  390. package/src/cli/commands/knowledge.ts +192 -289
  391. package/src/cli/commands/passes-gate.ts +58 -68
  392. package/src/cli/commands/performance.ts +23 -18
  393. package/src/cli/commands/posteval-plan.ts +29 -15
  394. package/src/cli/commands/release.ts +70 -59
  395. package/src/cli/commands/report.ts +9 -5
  396. package/src/cli/commands/review.ts +39 -29
  397. package/src/cli/commands/sdd.ts +7 -5
  398. package/src/cli/commands/security.ts +35 -26
  399. package/src/cli/commands/spec-baseline-check.ts +16 -11
  400. package/src/cli/commands/spec.ts +66 -53
  401. package/src/cli/commands/status.ts +59 -53
  402. package/src/cli/commands/sync-docs/agents-syncer.ts +5 -2
  403. package/src/cli/commands/sync-docs/context-syncer.ts +3 -2
  404. package/src/cli/commands/sync-docs/index.ts +73 -100
  405. package/src/cli/commands/sync-docs/project-reader.ts +17 -18
  406. package/src/cli/commands/update-user-model.ts +64 -61
  407. package/src/cli/commands/validate.ts +31 -25
  408. package/src/cli/session-mining/__tests__/transcript.test.ts +118 -0
  409. package/src/cli/session-mining/transcript.ts +26 -8
  410. package/src/context/session-manager.ts +19 -33
  411. package/src/core/CONTEXT.md +10 -6
  412. package/src/core/constraints/__tests__/capabilities-reconcile.test.ts +200 -0
  413. package/src/core/constraints/__tests__/capability-counts.test.ts +94 -0
  414. package/src/core/constraints/__tests__/git-evidence.test.ts +208 -0
  415. package/src/core/constraints/__tests__/injection-drift.test.ts +3 -10
  416. package/src/core/constraints/__tests__/injection-writer.test.ts +231 -0
  417. package/src/core/constraints/__tests__/trace-injection.test.ts +71 -0
  418. package/src/core/constraints/__tests__/usage-report.test.ts +112 -0
  419. package/src/core/constraints/agent-prompt-renderer.ts +2 -6
  420. package/src/core/constraints/capabilities-parser.ts +53 -35
  421. package/src/core/constraints/capabilities-reconcile.ts +166 -0
  422. package/src/core/constraints/checker.ts +82 -133
  423. package/src/core/constraints/checkers/__tests__/capability-sync.test.ts +273 -0
  424. package/src/core/constraints/checkers/__tests__/check-outcome.test.ts +176 -0
  425. package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +77 -0
  426. package/src/core/constraints/checkers/__tests__/docs-freshness.test.ts +126 -0
  427. package/src/{__tests__ → core/constraints/checkers/__tests__}/governance-presence.test.ts +83 -9
  428. package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +50 -0
  429. package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +76 -0
  430. package/src/{__tests__ → core/constraints/checkers/__tests__}/no-hardcoded-credentials.test.ts +4 -3
  431. package/src/core/constraints/checkers/__tests__/no-test-simplification.test.ts +58 -0
  432. package/src/core/constraints/checkers/__tests__/three-state-consistency.test.ts +153 -0
  433. package/src/core/constraints/checkers/capability-sync.ts +75 -96
  434. package/src/core/constraints/checkers/context-doc-sync.ts +15 -25
  435. package/src/core/constraints/checkers/docs-freshness.ts +54 -70
  436. package/src/core/constraints/checkers/governance-presence.ts +7 -25
  437. package/src/core/constraints/checkers/index.ts +9 -2
  438. package/src/core/constraints/checkers/no-test-simplification.ts +4 -3
  439. package/src/core/constraints/checkers/types.ts +60 -4
  440. package/src/core/constraints/context-builder.ts +39 -91
  441. package/src/core/constraints/doc-freshness/runner.ts +2 -1
  442. package/src/core/constraints/git-evidence.ts +120 -0
  443. package/src/core/constraints/injection-drift.ts +13 -60
  444. package/src/core/constraints/injection-writer.ts +212 -0
  445. package/src/core/constraints/usage-report.ts +36 -17
  446. package/src/core/index.ts +4 -2
  447. package/src/core/project-config-loader.ts +49 -9
  448. package/src/core/validators/__tests__/passes-gate.test.ts +2 -56
  449. package/src/core/validators/__tests__/test-output.test.ts +372 -0
  450. package/src/core/validators/passes-gate.ts +86 -248
  451. package/src/core/validators/test-output.ts +128 -0
  452. package/src/failure/__tests__/recorder.test.ts +168 -0
  453. package/src/failure/recorder.ts +12 -10
  454. package/src/gates/CONTEXT.md +7 -4
  455. package/src/gates/__tests__/acceptance-types-single-source.test.ts +68 -0
  456. package/src/gates/__tests__/acceptance.test.ts +4 -86
  457. package/src/gates/__tests__/checker-gate.test.ts +24 -0
  458. package/src/gates/__tests__/effective-gates.test.ts +3 -8
  459. package/src/gates/__tests__/test-verdict-consistency.test.ts +140 -0
  460. package/src/gates/acceptance.ts +19 -84
  461. package/src/gates/checker-gate.ts +12 -15
  462. package/src/gates/command.ts +10 -16
  463. package/src/gates/contract.ts +22 -61
  464. package/src/gates/performance.ts +10 -22
  465. package/src/gates/review.ts +21 -41
  466. package/src/gates/security.ts +10 -22
  467. package/src/gates/types.ts +70 -5
  468. package/src/hooks/CONTEXT.md +1 -1
  469. package/src/hooks/bootstrap.ts +5 -2
  470. package/src/index.ts +0 -4
  471. package/src/knowledge/CONTEXT.md +8 -3
  472. package/src/knowledge/__tests__/audit-labels.test.ts +44 -0
  473. package/src/knowledge/__tests__/flywheel-metrics.test.ts +135 -0
  474. package/src/knowledge/__tests__/import.test.ts +44 -0
  475. package/src/knowledge/__tests__/index-generator.test.ts +53 -0
  476. package/src/knowledge/__tests__/ingest.test.ts +85 -0
  477. package/src/knowledge/__tests__/migration.test.ts +47 -0
  478. package/src/knowledge/__tests__/reference-tracker.test.ts +16 -0
  479. package/src/knowledge/__tests__/store.test.ts +194 -0
  480. package/src/knowledge/audit.ts +67 -71
  481. package/src/knowledge/flywheel-metrics.ts +71 -0
  482. package/src/knowledge/import.ts +4 -18
  483. package/src/knowledge/index-generator.ts +19 -14
  484. package/src/knowledge/ingest.ts +9 -4
  485. package/src/knowledge/migration.ts +11 -8
  486. package/src/knowledge/reference-tracker.ts +12 -8
  487. package/src/knowledge/store.ts +64 -11
  488. package/src/monitoring/CONTEXT.md +8 -3
  489. package/src/monitoring/__tests__/skipped-lines-report.test.ts +178 -0
  490. package/src/monitoring/__tests__/traces.test.ts +57 -0
  491. package/src/monitoring/context-tracker.ts +11 -23
  492. package/src/monitoring/index.ts +1 -1
  493. package/src/monitoring/trace-analyzer.ts +21 -2
  494. package/src/monitoring/traces.ts +39 -36
  495. package/src/release/CONTEXT.md +1 -0
  496. package/src/release/__tests__/barrel.test.ts +60 -0
  497. package/src/release/index.ts +2 -13
  498. package/src/sdd/__tests__/index-generator.test.ts +71 -0
  499. package/src/sdd/index-generator.ts +9 -9
  500. package/src/test-setup/__tests__/project-fixture.test.ts +178 -0
  501. package/src/test-setup/project-fixture.ts +87 -0
  502. package/src/tools/CONTEXT.md +6 -0
  503. package/src/tools/__tests__/registry.test.ts +221 -0
  504. package/src/types/__tests__/types.test.ts +4 -5
  505. package/src/types/constraint.ts +16 -27
  506. package/src/types/passes-gate.ts +10 -30
  507. package/src/types/project-config.ts +1 -1
  508. package/src/types/session.ts +5 -5
  509. package/src/types/trace.ts +10 -0
  510. package/src/utils/__tests__/frontmatter.test.ts +206 -0
  511. package/src/utils/__tests__/jsonl-skip-disposition.test.ts +175 -0
  512. package/src/utils/__tests__/jsonl.test.ts +187 -0
  513. package/src/utils/__tests__/package-version.test.ts +97 -0
  514. package/src/utils/exec.ts +15 -0
  515. package/src/utils/file-walk.ts +11 -2
  516. package/src/utils/frontmatter.ts +121 -0
  517. package/src/utils/jsonl.ts +167 -0
  518. package/src/utils/package-version.ts +27 -0
  519. package/dist/cli/commands/doc-freshness-check.d.ts +0 -53
  520. package/dist/cli/commands/doc-freshness-check.d.ts.map +0 -1
  521. package/dist/cli/commands/doc-freshness-check.js +0 -352
  522. package/dist/cli/commands/doc-freshness-check.js.map +0 -1
  523. package/dist/types/index.d.ts +0 -14
  524. package/dist/types/index.d.ts.map +0 -1
  525. package/dist/types/index.js +0 -39
  526. package/dist/types/index.js.map +0 -1
  527. package/src/__tests__/long-running.test.ts +0 -17
  528. package/src/__tests__/passes-gate-extension.test.ts +0 -151
  529. package/src/cli/commands/__tests__/doc-freshness-check.test.ts +0 -168
  530. package/src/cli/commands/doc-freshness-check.ts +0 -386
  531. package/src/spec/CONTEXT.md +0 -11
  532. package/src/types/index.ts +0 -47
@@ -11,7 +11,6 @@
11
11
  */
12
12
 
13
13
  import * as fs from 'fs';
14
- import * as os from 'os';
15
14
  import * as path from 'path';
16
15
  import { detectInjectionDrift, INJECTION_DRIFT_FIX_HINT } from '../injection-drift';
17
16
  import {
@@ -19,12 +18,11 @@ import {
19
18
  CONSTRAINTS_START_MARKER,
20
19
  } from '../injection-renderer';
21
20
  import { getEffectiveConstraints } from '../../effective-constraints';
21
+ import { createProjectFixture, writeProjectConfig } from '../../../test-setup/project-fixture';
22
22
 
23
23
  const TEST_VERSION = '9.9.9-test';
24
24
 
25
- function makeTmpProject(): string {
26
- return fs.mkdtempSync(path.join(os.tmpdir(), 'harness-drift-test-'));
27
- }
25
+ const makeTmpProject = (): string => createProjectFixture({ name: 'harness-drift-test' });
28
26
 
29
27
  /** 写入与当前生效集完全一致的注入段(版本 TEST_VERSION) */
30
28
  function writeSyncedClaudeMd(root: string, extra = ''): string {
@@ -102,12 +100,7 @@ describe('detectInjectionDrift', () => {
102
100
  const root = makeTmpProject();
103
101
  writeSyncedClaudeMd(root);
104
102
  // 注入后禁用一条 prompt,但不重跑 init
105
- fs.mkdirSync(path.join(root, '.harness'), { recursive: true });
106
- fs.writeFileSync(
107
- path.join(root, '.harness', 'config.yml'),
108
- 'constraints:\n no_fuzzy_completion_claim:\n enabled: false\n',
109
- 'utf-8'
110
- );
103
+ writeProjectConfig(root, 'constraints:\n no_fuzzy_completion_claim:\n enabled: false\n');
111
104
 
112
105
  const drift = detectInjectionDrift(root, TEST_VERSION);
113
106
 
@@ -0,0 +1,231 @@
1
+ /**
2
+ * injection-writer 旁测(架构评审候选1,ADR-0011)
3
+ *
4
+ * 测试面 = 纯函数(字符串进、字符串出)+ 落点路由(真实 fs 临时目录)。
5
+ * 幂等/半标记守护/两种尾部形状/读写路由优先级全部在此测一次;
6
+ * 消费方(init/retire/drift)只测各自语义(init-injection.test 行为面)。
7
+ */
8
+
9
+ import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
10
+ import * as fs from 'fs';
11
+ import * as path from 'path';
12
+ import {
13
+ replaceStandaloneRange,
14
+ replaceEnclosedRange,
15
+ cutMarkerBlock,
16
+ resolveInjectionTarget,
17
+ resolveGovernanceLanding,
18
+ GOVERNANCE_HEADING,
19
+ hasGovernanceContract,
20
+ countGovernanceHeadings,
21
+ hasPreserveBlock,
22
+ } from '../injection-writer';
23
+ import {
24
+ CONSTRAINTS_START_MARKER as CS,
25
+ CONSTRAINTS_END_MARKER as CE,
26
+ } from '../injection-renderer';
27
+
28
+ const B = '<!-- B -->';
29
+ const E = '<!-- E -->';
30
+
31
+ describe('replaceStandaloneRange — 独立成节形状', () => {
32
+ it('替换含标记区间;尾文多换行折叠为恰好一个空行', () => {
33
+ const content = `head\n${B}\nold\n${E}\n\n\n\nrest\n`;
34
+ const r = replaceStandaloneRange(content, B, E, `${B}\nnew\n${E}\n`);
35
+ expect(r).toEqual({ kind: 'updated', content: `head\n${B}\nnew\n${E}\n\nrest\n` });
36
+ });
37
+
38
+ it('尾文恰好一个空行分隔(含原本无多余换行时)', () => {
39
+ const content = `${B}\nold\n${E}\nrest\n`;
40
+ const r = replaceStandaloneRange(content, B, E, `${B}\nnew\n${E}\n`);
41
+ expect(r).toEqual({ kind: 'updated', content: `${B}\nnew\n${E}\n\nrest\n` });
42
+ });
43
+
44
+ it('区间在文件末尾:不产生尾空行', () => {
45
+ const content = `head\n${B}\nold\n${E}\n`;
46
+ const r = replaceStandaloneRange(content, B, E, `${B}\nnew\n${E}\n`);
47
+ expect(r).toEqual({ kind: 'updated', content: `head\n${B}\nnew\n${E}\n` });
48
+ });
49
+
50
+ it('幂等:重复替换结果不动点', () => {
51
+ const once = replaceStandaloneRange(`a\n${B}\nx\n${E}\n\n\nb\n`, B, E, `${B}\nnew\n${E}\n`);
52
+ expect(once.kind).toBe('updated');
53
+ const twice = replaceStandaloneRange((once as { content: string }).content, B, E, `${B}\nnew\n${E}\n`);
54
+ expect(twice).toEqual(once);
55
+ });
56
+
57
+ it('两俱无 → absent;单边/乱序 → half(拒写)', () => {
58
+ expect(replaceStandaloneRange('nothing here', B, E, 'x')).toEqual({ kind: 'absent' });
59
+ expect(replaceStandaloneRange(`${B}\norphan`, B, E, 'x')).toEqual({ kind: 'half' });
60
+ expect(replaceStandaloneRange(`orphan ${E}`, B, E, 'x')).toEqual({ kind: 'half' });
61
+ expect(replaceStandaloneRange(`${E}\n...\n${B}`, B, E, 'x')).toEqual({ kind: 'half' });
62
+ });
63
+ });
64
+
65
+ describe('replaceEnclosedRange — 外层收口紧随形状', () => {
66
+ it('恰好剥一个前导换行,手写余文的额外换行原样保留', () => {
67
+ const inner = `\n## H\n${CS}\nold\n${CE}\n\n手写余文\n`;
68
+ const r = replaceEnclosedRange(inner, CS, CE, `${CS}\nnew\n${CE}\n`);
69
+ expect(r).toEqual({ kind: 'updated', content: `\n## H\n${CS}\nnew\n${CE}\n\n手写余文\n` });
70
+ });
71
+
72
+ it('区间后仅有行尾换行(收口标记直接落行)', () => {
73
+ const inner = `\n${CS}\nold\n${CE}\n`;
74
+ const r = replaceEnclosedRange(inner, CS, CE, `${CS}\nnew\n${CE}\n`);
75
+ expect(r).toEqual({ kind: 'updated', content: `\n${CS}\nnew\n${CE}\n` });
76
+ });
77
+
78
+ it('absent → 调用方走追加;half → 拒写', () => {
79
+ expect(replaceEnclosedRange('\n纯手写段\n', CS, CE, 'x')).toEqual({ kind: 'absent' });
80
+ expect(replaceEnclosedRange(`${CS}orphan`, CS, CE, 'x')).toEqual({ kind: 'half' });
81
+ });
82
+ });
83
+
84
+ describe('cutMarkerBlock', () => {
85
+ it('切出 before/inner/after 三段,inner 含首尾标记行间全部文本', () => {
86
+ const content = `before\n${B}\ninner-body\n${E}\nafter\n`;
87
+ expect(cutMarkerBlock(content, B, E)).toEqual({
88
+ before: 'before\n',
89
+ inner: '\ninner-body\n',
90
+ after: '\nafter\n',
91
+ });
92
+ });
93
+
94
+ it('absent / half 与替换函数同一判定', () => {
95
+ expect(cutMarkerBlock('clean', B, E)).toBe('absent');
96
+ expect(cutMarkerBlock(`${B} only`, B, E)).toBe('half');
97
+ });
98
+ });
99
+
100
+ describe('落点路由(读写两侧,studio #307/#302)', () => {
101
+ let dir: string;
102
+
103
+ beforeEach(() => {
104
+ dir = fs.mkdtempSync(path.join(process.cwd(), 'temp-test-inj-writer-'));
105
+ });
106
+ afterEach(() => fs.rmSync(dir, { recursive: true, force: true }));
107
+
108
+ const write = (name: string, content: string) =>
109
+ fs.writeFileSync(path.join(dir, name), content, 'utf-8');
110
+
111
+ it('resolveInjectionTarget:CLAUDE.md 完整标记段优先于 AGENTS.md', () => {
112
+ write('CLAUDE.md', `x\n${CS}\nbody\n${CE}\n`);
113
+ write('AGENTS.md', `<!-- PRESERVE:governance -->\n${CS}\nbody2\n${CE}\n`);
114
+ expect(resolveInjectionTarget(dir)?.file).toBe('CLAUDE.md');
115
+ });
116
+
117
+ it('CLAUDE.md 标记残缺(单边)不算落点 → 落 AGENTS.md', () => {
118
+ write('CLAUDE.md', `x\n${CS}\nbroken\n`);
119
+ write('AGENTS.md', `${CS}\nbody\n${CE}\n`);
120
+ expect(resolveInjectionTarget(dir)?.file).toBe('AGENTS.md');
121
+ });
122
+
123
+ it('两处均无完整标记段 → null(未注入)', () => {
124
+ write('CLAUDE.md', '# 无约束标记\n');
125
+ expect(resolveInjectionTarget(dir)).toBeNull();
126
+ });
127
+
128
+ it('resolveGovernanceLanding:CLAUDE.md 有标记或有旧版 Governance Rules 块 → claude-md', () => {
129
+ write('CLAUDE.md', `${CS}\nbody\n${CE}\n`);
130
+ expect(resolveGovernanceLanding(dir).target).toBe('claude-md');
131
+ write('CLAUDE.md', '# CLAUDE.md\n\n## Governance Rules\n\n手写\n');
132
+ expect(resolveGovernanceLanding(dir).target).toBe('claude-md');
133
+ });
134
+
135
+ it('CLAUDE.md 与治理无关 / 不存在 → agents-md', () => {
136
+ write('CLAUDE.md', '# CLAUDE.md\n\n@AGENTS.md\n');
137
+ expect(resolveGovernanceLanding(dir).target).toBe('agents-md');
138
+ fs.rmSync(path.join(dir, 'CLAUDE.md'));
139
+ expect(resolveGovernanceLanding(dir).target).toBe('agents-md');
140
+ });
141
+
142
+ it('落点路由消费共享宽松谓词:双空格标题变体也路由 claude-md(fail-open)', () => {
143
+ write('CLAUDE.md', '# CLAUDE.md\n\n## Governance Rules\n\n手写\n');
144
+ expect(resolveGovernanceLanding(dir).target).toBe('claude-md');
145
+ });
146
+ });
147
+
148
+ describe('治理契约探测共享符号(#83,ADR-0011 补注:收口范围含探测器)', () => {
149
+ it('GOVERNANCE_HEADING 是标题字面量单一来源', () => {
150
+ expect(GOVERNANCE_HEADING).toBe('## Governance Rules');
151
+ });
152
+
153
+ describe('hasGovernanceContract — 宽松谓词(有无契约,fail-open)', () => {
154
+ it('精确标题 → true', () => {
155
+ expect(hasGovernanceContract('# doc\n\n## Governance Rules\n\n条款\n')).toBe(true);
156
+ });
157
+
158
+ it('双空格标题变体 → true(宁可信其有)', () => {
159
+ expect(hasGovernanceContract('# doc\n\n## Governance Rules\n\n条款\n')).toBe(true);
160
+ });
161
+
162
+ it('HARNESS 约束标记在场 → true(完整注释壳与裸文本皆认)', () => {
163
+ expect(hasGovernanceContract('<!-- HARNESS_CONSTRAINTS_START -->\nx\n')).toBe(true);
164
+ expect(hasGovernanceContract('HARNESS_CONSTRAINTS_START\nx\n')).toBe(true);
165
+ });
166
+
167
+ it('两样皆无 → false', () => {
168
+ expect(hasGovernanceContract('# CLAUDE.md\n\n@AGENTS.md\n')).toBe(false);
169
+ });
170
+ });
171
+
172
+ describe('countGovernanceHeadings — 严格计数(正本标题,行尾锚定)', () => {
173
+ it('精确拼写逐个计数', () => {
174
+ expect(countGovernanceHeadings('## Governance Rules\n')).toBe(1);
175
+ expect(countGovernanceHeadings('## Governance Rules\n甲\n\n## Governance Rules\n乙\n')).toBe(2);
176
+ });
177
+
178
+ it('行尾空白容忍', () => {
179
+ expect(countGovernanceHeadings('## Governance Rules \n')).toBe(1);
180
+ expect(countGovernanceHeadings('## Governance Rules\t\n')).toBe(1);
181
+ });
182
+
183
+ it('宽松变体不计入:双空格 / 三级标题 / 行中嵌入 → 0', () => {
184
+ expect(countGovernanceHeadings('## Governance Rules\n')).toBe(0);
185
+ expect(countGovernanceHeadings('### Governance Rules\n')).toBe(0);
186
+ expect(countGovernanceHeadings('前文 ## Governance Rules 行中\n')).toBe(0);
187
+ });
188
+ });
189
+
190
+ describe('hasPreserveBlock — PRESERVE 块存在性 + 块体非空', () => {
191
+ const begin = '<!-- PRESERVE:governance -->';
192
+ const end = '<!-- /PRESERVE:governance -->';
193
+
194
+ it('块体非空 + 首尾标记独占一行 → true', () => {
195
+ expect(hasPreserveBlock(`# t\n${begin}\n## H\n条款\n${end}\n`, 'governance')).toBe(true);
196
+ });
197
+
198
+ it('块体仅空白 → false(被掏空)', () => {
199
+ expect(hasPreserveBlock(`${begin}\n \n${end}\n`, 'governance')).toBe(false);
200
+ expect(hasPreserveBlock(`${begin}\n\n${end}\n`, 'governance')).toBe(false);
201
+ });
202
+
203
+ it('结束标记嵌在行中 = 畸形块 → false(显式收紧,#83)', () => {
204
+ expect(hasPreserveBlock(`${begin}\n条款 <!-- /PRESERVE:governance --> 尾注\n`, 'governance')).toBe(false);
205
+ });
206
+
207
+ it('结束标记缺失 / 乱序 → false', () => {
208
+ expect(hasPreserveBlock(`${begin}\n条款\n`, 'governance')).toBe(false);
209
+ expect(hasPreserveBlock(`${end}\n${begin}\n`, 'governance')).toBe(false);
210
+ });
211
+
212
+ it('开始标记不在 / 名称不符 → false', () => {
213
+ expect(hasPreserveBlock('没有标记\n', 'governance')).toBe(false);
214
+ expect(hasPreserveBlock(`${begin}\n条款\n${end}\n`, 'other')).toBe(false);
215
+ });
216
+
217
+ it('标记行行尾空白容忍(独占一行语义)', () => {
218
+ expect(hasPreserveBlock(`${begin} \n条款\n${end} \n`, 'governance')).toBe(true);
219
+ });
220
+ });
221
+
222
+ it('一致性 fixture:双空格 → 宽松在场 true 且严格计数 0;精确拼写 → 两者均认', () => {
223
+ const variant = '# CLAUDE.md\n\n## Governance Rules\n\n条款\n';
224
+ expect(hasGovernanceContract(variant)).toBe(true);
225
+ expect(countGovernanceHeadings(variant)).toBe(0);
226
+
227
+ const exact = '# CLAUDE.md\n\n## Governance Rules\n\n条款\n';
228
+ expect(hasGovernanceContract(exact)).toBe(true);
229
+ expect(countGovernanceHeadings(exact)).toBe(1);
230
+ });
231
+ });
@@ -0,0 +1,71 @@
1
+ /**
2
+ * trace 记录器注入(工单 15 decycle 收尾 / harness#88)
3
+ *
4
+ * 原状态:checker 值导入 monitoring 的 getTraceCollector,并在首次记录时惰性接线
5
+ * (ADR-0003),`setTraceRecorder` 退化成只有测试在用的假 seam。
6
+ * 现在:记录器经**构造参数**注入;未注入 = no-op(core 零上行依赖,默认无副作用),
7
+ * 真实收集器由组合根(CLI 命令 / bootstrap)接线。
8
+ */
9
+
10
+ import * as fs from 'fs';
11
+ import * as os from 'os';
12
+ import * as path from 'path';
13
+ import { ConstraintChecker } from '../checker';
14
+ import type { ExecutionTrace } from '../../../types/trace';
15
+
16
+ jest.mock('../../../monitoring/traces', () => ({
17
+ getTraceCollector: jest.fn(() => ({ record: jest.fn() })),
18
+ TraceCollector: jest.fn(),
19
+ configureTraceCollector: jest.fn(),
20
+ }));
21
+
22
+ /** 替身收集器工厂(core 侧连测试也不值导入 monitoring,方向由 #88 规则锁) */
23
+ const getTraceCollector = jest.requireMock('../../../monitoring/traces')
24
+ .getTraceCollector as jest.Mock;
25
+
26
+ describe('trace 记录器构造注入(harness#88)', () => {
27
+ let projectDir: string;
28
+ /** 与 skip-semantics 同族的 fixture:目录不存在 → 存在性探测走 skip,不阻断 */
29
+ const context = () => ({
30
+ operation: 'code_implementation' as const,
31
+ projectPath: path.join(projectDir, 'not-created'),
32
+ });
33
+
34
+ beforeAll(() => {
35
+ projectDir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-trace-injection-'));
36
+ getTraceCollector.mockClear();
37
+ });
38
+
39
+ afterAll(() => {
40
+ try {
41
+ fs.rmSync(projectDir, { recursive: true, force: true });
42
+ } catch {
43
+ // ignore
44
+ }
45
+ });
46
+
47
+ it('构造注入的记录器收到每条约束的 trace', async () => {
48
+ const records: ExecutionTrace[] = [];
49
+ const checker = new ConstraintChecker({ record: (t) => records.push(t) });
50
+
51
+ await checker.checkConstraints(context());
52
+
53
+ expect(records.map(r => r.constraintId)).toEqual(
54
+ expect.arrayContaining(['incremental_progress', 'no_implementation_without_requirement'])
55
+ );
56
+ expect(records.every(r => ['pass', 'fail', 'skip'].includes(r.result))).toBe(true);
57
+ });
58
+
59
+ it('setTraceRecorder 假 seam 已删除', () => {
60
+ const checker = new ConstraintChecker({ record: () => undefined });
61
+ expect((checker as any).setTraceRecorder).toBeUndefined();
62
+ });
63
+
64
+ it('未注入记录器时不记录,也不反向取全局收集器(core 零上行依赖)', async () => {
65
+ const unwired = new ConstraintChecker();
66
+
67
+ await unwired.checkConstraints(context());
68
+
69
+ expect(getTraceCollector).not.toHaveBeenCalled();
70
+ });
71
+ });
@@ -0,0 +1,112 @@
1
+ /**
2
+ * traces.log 坏行 fixture(harness#82 + harness#100)
3
+ *
4
+ * #82:三种现行响应例证之「静默跳过」(原语义不变)——
5
+ * traces.log 单行损坏时 report 数据层不失败,返回全部合法 trace。
6
+ * #100:计数不再断在 module 层——新增 `readProjectTracesReport()` 报告入口,
7
+ * 并把坏行数挂进 `ConstraintsUsageReport`(与 `traceFileExists` 同一降级维度)。
8
+ */
9
+
10
+ import * as fs from 'fs';
11
+ import * as os from 'os';
12
+ import * as path from 'path';
13
+ import { createProjectFixture } from '../../../test-setup/project-fixture';
14
+ import { readJsonl } from '../../../utils/jsonl';
15
+ import {
16
+ buildConstraintsUsageReport,
17
+ readProjectTraces,
18
+ readProjectTracesReport,
19
+ } from '../usage-report';
20
+ import { DEFAULT_TRACE_FILE } from '../../../types/trace';
21
+
22
+ describe('readProjectTraces 坏行容错(harness#82)', () => {
23
+ let root: string;
24
+
25
+ beforeEach(() => {
26
+ root = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-usage-report-'));
27
+ });
28
+
29
+ afterEach(() => {
30
+ fs.rmSync(root, { recursive: true, force: true });
31
+ });
32
+
33
+ function writeTraces(lines: string[]): void {
34
+ const tracePath = path.join(root, DEFAULT_TRACE_FILE);
35
+ fs.mkdirSync(path.dirname(tracePath), { recursive: true });
36
+ fs.writeFileSync(tracePath, lines.join('\n') + '\n', 'utf-8');
37
+ }
38
+
39
+ it('坏行静默跳过,返回全部合法 trace', () => {
40
+ writeTraces([
41
+ '{"constraintId":"a","level":"iron_law","timestamp":1,"result":"pass"}',
42
+ '{"constraintId":"b","broken',
43
+ '{"constraintId":"c","level":"iron_law","timestamp":3,"result":"fail"}',
44
+ ]);
45
+ const traces = readProjectTraces(root);
46
+ expect(traces.map(t => t.constraintId)).toEqual(['a', 'c']);
47
+ });
48
+
49
+ it('缺文件返回空数组', () => {
50
+ expect(readProjectTraces(root)).toEqual([]);
51
+ });
52
+ });
53
+
54
+ describe('坏行计数透传到 report 数据层(harness#100)', () => {
55
+ const VALID_A = '{"constraintId":"a","level":"iron_law","timestamp":1,"result":"pass"}';
56
+ const VALID_B = '{"constraintId":"b","level":"iron_law","timestamp":3,"result":"fail"}';
57
+ const BAD = '{"constraintId":"x","broken';
58
+ const TRACE_REL = path.join('.harness', 'logs', 'traces.log');
59
+
60
+ function projectWith(lines: string[]): string {
61
+ return createProjectFixture({
62
+ name: 'usage-report-skiplines',
63
+ files: { [TRACE_REL]: lines.join('\n') + '\n' },
64
+ });
65
+ }
66
+
67
+ it.each([
68
+ [1, [BAD]],
69
+ [3, [BAD, 'garbage', '{"half"] }']],
70
+ ] as Array<[number, string[]]>)(
71
+ '%i 行坏数据:readProjectTracesReport 计数与 module 计数对账',
72
+ (expectedSkipped, badLines) => {
73
+ const lines = [VALID_A, ...badLines, VALID_B];
74
+ const root = projectWith(lines);
75
+
76
+ const report = readProjectTracesReport(root);
77
+
78
+ expect(report.skippedLines).toBe(expectedSkipped);
79
+ expect(report.skippedLines).toBe(
80
+ readJsonl(path.join(root, TRACE_REL), 'skip').skippedLines
81
+ );
82
+ // 兼容签名不变:旧入口仍只返回合法记录数组
83
+ expect(readProjectTraces(root)).toEqual(report.traces);
84
+ expect(report.traces.map(t => t.constraintId)).toEqual(['a', 'b']);
85
+ }
86
+ );
87
+
88
+ it('buildConstraintsUsageReport 报告体带坏行数(与 traceFileExists 同一降级维度)', () => {
89
+ const root = projectWith([VALID_A, BAD, VALID_B]);
90
+
91
+ const report = buildConstraintsUsageReport(root);
92
+
93
+ expect(report.skippedLines).toBe(1);
94
+ expect(report.traceFileExists).toBe(true);
95
+ });
96
+
97
+ it('无损坏时计数为 0(既有输出零噪声的前提)', () => {
98
+ const report = buildConstraintsUsageReport(projectWith([VALID_A, VALID_B]));
99
+
100
+ expect(report.skippedLines).toBe(0);
101
+ expect(report.traceFileExists).toBe(true);
102
+ });
103
+
104
+ it('缺 trace 文件:计数 0 且 traceFileExists=false', () => {
105
+ const report = buildConstraintsUsageReport(
106
+ createProjectFixture({ name: 'usage-report-notracefile' })
107
+ );
108
+
109
+ expect(report.skippedLines).toBe(0);
110
+ expect(report.traceFileExists).toBe(false);
111
+ });
112
+ });
@@ -11,8 +11,8 @@
11
11
  * (如 docs_freshness)不渲染。
12
12
  */
13
13
 
14
- import type { Constraint, ConstraintLevel, ConstraintTrigger } from '../../types/constraint';
15
- import { normalizeTriggers } from '../../utils/exec';
14
+ import type { ConstraintLevel, ConstraintTrigger } from '../../types/constraint';
15
+ import { matchesTrigger } from '../../utils/exec';
16
16
  import { getEffectiveConstraints } from '../effective-constraints';
17
17
 
18
18
  /**
@@ -65,7 +65,3 @@ export function renderConstraintsByTrigger(
65
65
  }
66
66
  return lines.join('\n');
67
67
  }
68
-
69
- function matchesTrigger(constraint: Constraint, requested: ConstraintTrigger[]): boolean {
70
- return normalizeTriggers<ConstraintTrigger>(constraint.trigger).some(t => requested.includes(t));
71
- }
@@ -5,26 +5,18 @@
5
5
  * 各持一份正则,收敛到此处。H2 起能力清单计数(checkCapabilityCounts /
6
6
  * updateCapabilityCounts)也从 sync-docs/capabilities-syncer 收敛到此,
7
7
  * 使本模块成为 CAPABILITIES.md 解析 + 计数唯一入口。
8
+ *
9
+ * 计数所需的命令/门禁定义由调用方注入(harness#88:core 不值导入上层定义表)。
8
10
  */
9
11
 
10
12
  import * as fs from 'fs';
11
13
  import { IRON_LAWS, GUIDELINES } from './definitions';
12
- import { COMMAND_DEFINITIONS } from '../../cli/commands/definitions';
13
- import { GATE_DEFINITIONS } from '../../gates/definitions';
14
14
  import { FreshnessRunner } from './doc-freshness/runner';
15
15
  import type { DocFreshnessCheck } from '../../types/project-config';
16
16
 
17
- /** CLI 命令数 = 非门禁命令定义 + 门禁命令定义(ADR-0002:定义表是命令形状单一来源) */
18
- function cliCommandCount(): number {
19
- return COMMAND_DEFINITIONS.length + GATE_DEFINITIONS.filter(g => g.cli).length;
20
- }
21
-
22
17
  /** 匹配表格单元格中的源码文件条目(如 `src/foo.ts`、`foo.tsx`) */
23
18
  const FILE_CELL_REGEX = /\|\s*([^|]+?\.(?:ts|tsx|js|jsx))\s*\|/g;
24
19
 
25
- /** 仅匹配表格第二列的文件条目(checker 的历史语义:第一列为模块名,第二列为文件) */
26
- const FILE_SECOND_COLUMN_REGEX = /^\|[^|]+\|\s*([^|]+?\.(?:ts|tsx|js|jsx))\s*\|/gm;
27
-
28
20
  /**
29
21
  * 匹配表格单元格中的目录条目(如 `agents/`)。
30
22
  *
@@ -35,26 +27,25 @@ const FILE_SECOND_COLUMN_REGEX = /^\|[^|]+\|\s*([^|]+?\.(?:ts|tsx|js|jsx))\s*\|/
35
27
  const DIR_CELL_REGEX = /\|\s*([\w][\w@./-]*\/)\s*\|/g;
36
28
 
37
29
  export interface CapabilitiesParseOptions {
38
- /** 是否同时收集目录条目(sync-docs 需要;checker 只做文件覆盖比对,不需要) */
30
+ /** 是否同时收集目录条目,默认 false */
39
31
  includeDirs?: boolean;
40
- /** 只取表格第二列的文件条目(checker 的历史严格语义),默认 false */
41
- secondColumnOnly?: boolean;
42
32
  }
43
33
 
44
34
  /**
45
35
  * 从 CAPABILITIES.md 内容中提取表格列出的条目
46
36
  *
37
+ * 解析口径(ADR-0009):表格所有单元格收集;「只看第二列」的历史读法废弃。
38
+ *
47
39
  * @param content markdown 内容
48
40
  * @returns 文件路径原样 + (可选)目录条目,按出现顺序去重
49
41
  */
50
42
  export function parseCapabilitiesEntries(content: string, options: CapabilitiesParseOptions = {}): string[] {
51
- const { includeDirs = false, secondColumnOnly = false } = options;
43
+ const { includeDirs = false } = options;
52
44
  const entries: string[] = [];
53
45
 
54
- const fileRegex = secondColumnOnly ? FILE_SECOND_COLUMN_REGEX : FILE_CELL_REGEX;
55
46
  let match: RegExpExecArray | null;
56
- fileRegex.lastIndex = 0;
57
- while ((match = fileRegex.exec(content)) !== null) {
47
+ FILE_CELL_REGEX.lastIndex = 0;
48
+ while ((match = FILE_CELL_REGEX.exec(content)) !== null) {
58
49
  const raw = match[1].trim();
59
50
  if (!entries.includes(raw)) entries.push(raw);
60
51
  }
@@ -111,6 +102,20 @@ export function aggregateToSourceSubdir(root: string, file: string): string {
111
102
 
112
103
  // ── 能力清单计数 ────────────────────────────────────────────
113
104
 
105
+ /**
106
+ * 调用方注入的定义源(harness#88)
107
+ *
108
+ * core 只消费计数所需的最小形状,不 require 命令表:
109
+ * - commands:非门禁 CLI 命令定义(仅取长度)
110
+ * - gates:门禁定义(仅取长度与「是否带 cli 元数据」)
111
+ *
112
+ * ADR-0002「定义表是命令形状单一来源」不变,只是表的引用方从 core 移到 cli。
113
+ */
114
+ export interface CapabilityDefinitionSource {
115
+ commands: readonly unknown[];
116
+ gates: readonly { cli?: unknown }[];
117
+ }
118
+
114
119
  /**
115
120
  * 能力清单统计规则单份定义(ADR-0008:check/write 两方向共用,
116
121
  * 此前 label+pattern 在 buildCapabilityChecks 与 updateCapabilityCounts 各写一遍)
@@ -119,31 +124,40 @@ export function aggregateToSourceSubdir(root: string, file: string): string {
119
124
  * write 的替换文本前缀,如 `Quality Gates (6)`)
120
125
  * - pattern:同时供 check(doc_regex_count 字符串 pattern,捕获组为文档计数)
121
126
  * 与 write(编译为替换 regex)
122
- * - actual:定义表实际计数(ADR-0002 单一来源)
127
+ * - actual:实际计数——命令/门禁来自注入源,约束来自 core 自身定义表
123
128
  */
124
129
  interface CapabilityCountRule {
125
130
  label: string;
126
131
  pattern: string;
127
- actual: () => number;
132
+ actual: number;
128
133
  }
129
134
 
130
- const CAPABILITY_COUNT_RULES: CapabilityCountRule[] = [
131
- { label: 'CLI Commands', pattern: 'CLI Commands\\s*\\((\\d+)\\)', actual: cliCommandCount },
132
- { label: 'Quality Gates', pattern: 'Quality Gates?\\s*\\((\\d+)\\)', actual: () => GATE_DEFINITIONS.length },
133
- { label: 'Iron Laws', pattern: 'Iron Laws?\\s*\\((\\d+)\\)', actual: () => Object.keys(IRON_LAWS).length },
134
- { label: 'Guidelines', pattern: 'Guidelines?\\s*\\((\\d+)\\)', actual: () => Object.keys(GUIDELINES).length },
135
- ];
135
+ /**
136
+ * 能力清单统计规则(CLI Commands 计数 = 非门禁命令数 + cli 元数据的门禁数)
137
+ */
138
+ function capabilityCountRules(source: CapabilityDefinitionSource): CapabilityCountRule[] {
139
+ return [
140
+ {
141
+ label: 'CLI Commands',
142
+ pattern: 'CLI Commands\\s*\\((\\d+)\\)',
143
+ actual: source.commands.length + source.gates.filter(g => g.cli).length,
144
+ },
145
+ { label: 'Quality Gates', pattern: 'Quality Gates?\\s*\\((\\d+)\\)', actual: source.gates.length },
146
+ { label: 'Iron Laws', pattern: 'Iron Laws?\\s*\\((\\d+)\\)', actual: Object.keys(IRON_LAWS).length },
147
+ { label: 'Guidelines', pattern: 'Guidelines?\\s*\\((\\d+)\\)', actual: Object.keys(GUIDELINES).length },
148
+ ];
149
+ }
136
150
 
137
151
  /**
138
152
  * 构建 CAPABILITIES.md 能力清单格式的检查配置
139
153
  */
140
- function buildCapabilityChecks(): DocFreshnessCheck[] {
141
- return CAPABILITY_COUNT_RULES.map(rule => ({
154
+ function buildCapabilityChecks(source: CapabilityDefinitionSource): DocFreshnessCheck[] {
155
+ return capabilityCountRules(source).map(rule => ({
142
156
  type: 'doc_regex_count' as const,
143
157
  doc: 'CAPABILITIES.md',
144
158
  label: rule.label,
145
159
  pattern: rule.pattern,
146
- actual: { kind: 'const_count' as const, value: rule.actual() },
160
+ actual: { kind: 'const_count' as const, value: rule.actual },
147
161
  }));
148
162
  }
149
163
 
@@ -151,10 +165,11 @@ function buildCapabilityChecks(): DocFreshnessCheck[] {
151
165
  * 使用 FreshnessRunner 检查能力清单计数是否与代码一致(--check 模式)
152
166
  */
153
167
  export function checkCapabilityCounts(
154
- projectPath: string
168
+ projectPath: string,
169
+ source: CapabilityDefinitionSource
155
170
  ): { match: boolean; mismatches: string[] } {
156
171
  const runner = new FreshnessRunner();
157
- const checks = buildCapabilityChecks();
172
+ const checks = buildCapabilityChecks(source);
158
173
  const results = runner.runAll({ checks }, projectPath);
159
174
 
160
175
  const mismatches: string[] = [];
@@ -170,14 +185,17 @@ export function checkCapabilityCounts(
170
185
  /**
171
186
  * 更新 CAPABILITIES.md 中的能力清单计数(write 模式)
172
187
  *
173
- * 命令/门禁计数取自定义表(ADR-0002 单一来源),regex 替换文档计数行;
174
- * 规则与 check 方向共用 CAPABILITY_COUNT_RULES(ADR-0008)。
188
+ * 命令/门禁计数取自注入的定义源(ADR-0002 单一来源在调用方),regex 替换文档计数行;
189
+ * 规则与 check 方向共用 capabilityCountRules(ADR-0008)。
175
190
  */
176
- export function updateCapabilityCounts(content: string, _projectPath: string): string {
177
- for (const rule of CAPABILITY_COUNT_RULES) {
191
+ export function updateCapabilityCounts(
192
+ content: string,
193
+ source: CapabilityDefinitionSource
194
+ ): string {
195
+ for (const rule of capabilityCountRules(source)) {
178
196
  const regex = new RegExp(rule.pattern);
179
197
  if (regex.test(content)) {
180
- content = content.replace(regex, `${rule.label} (${rule.actual()})`);
198
+ content = content.replace(regex, `${rule.label} (${rule.actual})`);
181
199
  }
182
200
  }
183
201