@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
@@ -1,16 +1,28 @@
1
1
  /**
2
2
  * capability_sync:CAPABILITIES.md 与代码同步检查(工单 21)
3
3
  *
4
- * 两步验证:
5
- * 1. Git diff 增量 — 当前变更的非测试文件是否在文档中有记录
6
- * 2. 全量扫描 源码根下所有源文件是否都在 CAPABILITIES.md 中(T-058)
4
+ * 两步验证,归因不同(harness#119):
5
+ * 1. Git diff 增量 — 本次变更的非测试文件是否在文档中有记录 → **判违规**(有因果)
6
+ * 2. 全量扫描(T-058)— 源码根下所有源文件是否都在 CAPABILITIES.md 中 → **只出提示**
7
7
  *
8
- * 覆盖判定两步统一(isCoveredBy):目录条目(以 / 结尾)前缀匹配,
9
- * 文件条目精确匹配或路径边界后缀匹配。module 模式下目录条目参与覆盖,
10
- * 未覆盖文件按源码根下第一级子目录聚合。
8
+ * Step 2 不判违规的理由:它是仓库级完备性不变量,与正在被评估的那次变更无因果——
9
+ * 一处历史漏登会让此后每次 module_* 评估恒红(studio 实测 22/22 fail,且因 fail
10
+ * 零证据而无人能定位)。本处保留缺口输出是为了让人看得见、能自己修,而不是为了拦。
11
+ *
12
+ * 已知缺口(ADR-0016「影响」①):降级后仓库级漏登**没有自动拦截点**。`sync-docs --check`
13
+ * 单独跑会点名并 exit 1,但 CI/ship 都按「先 sync-docs 写入、后 --check」的顺序跑
14
+ * (2026-08-08 CI 4 连红固化的顺序),而 file 模式的写入正给缺登记自动补行
15
+ * (`capabilities-syncer` 的 `mode !== 'module'` 分支)、那份写入又不回提——于是流水线里
16
+ * 永远查不到漏登。补这个牙齿归流水线侧(sync 后 git diff 非空即失败),不在本约束里做。
17
+ *
18
+ * 例外:「有表格但零条目」= 文档被清空,判违规(历史门,此前靠 Step 2 兜,现显式化)。
19
+ *
20
+ * 覆盖/漏登判定不在本文件实现,统一走 capabilities-reconcile(ADR-0009):
21
+ * 目录条目(以 / 结尾)前缀匹配,文件条目精确匹配或路径边界后缀匹配;
22
+ * module 模式下目录条目参与覆盖,未覆盖文件按源码根下第一级子目录聚合。
11
23
  *
12
24
  * 清单格式(计数行,mode=listing 或嗅探命中)无文件表可核对,直接放行;
13
- * 检查异常时 fail-open 但输出 console.warn 保留可观测性。
25
+ * 无表格的散文文档保持历史放行;检查异常时 fail-open 但输出 console.warn 保留可观测性。
14
26
  *
15
27
  * ADR-0001 存在性探测:项目根无 CAPABILITIES.md(未采用该约定)时返回 'skip',
16
28
  * 不计 pass/fail,避免在未采用约定的项目上全量误报。
@@ -18,32 +30,11 @@
18
30
 
19
31
  import { existsSync, readFileSync } from 'fs';
20
32
  import { join } from 'path';
21
- import {
22
- aggregateToSourceSubdir,
23
- isCapabilityListingFormat,
24
- parseCapabilitiesEntries,
25
- } from '../capabilities-parser';
33
+ import { isCapabilityListingFormat } from '../capabilities-parser';
26
34
  import { getCapabilitiesMode } from '../../project-config-loader';
27
35
  import { detectSourceRoots } from '../../../utils/detect-source-roots';
28
- import type { ConstraintCheck } from './types';
29
-
30
- /** markdown 表格分隔行(|------|------|):区分「无表格的散文文档」与「有表格但零条目」 */
31
- const TABLE_SEPARATOR_REGEX = /^\s*\|[\s:|-]+\|\s*$/m;
32
-
33
- /**
34
- * 变更文件是否被条目覆盖
35
- *
36
- * 目录条目(以 / 结尾)前缀匹配;文件条目精确匹配或路径边界后缀匹配
37
- * (兼容 basename 条目,但拒绝 xfoo.ts 命中 foo.ts、docs/src/foo.tsx 命中 src/foo.ts
38
- * 这类 endsWith/includes 模糊匹配造成的碰撞)
39
- */
40
- function isCoveredBy(entries: string[], changed: string): boolean {
41
- return entries.some((listed) =>
42
- listed.endsWith('/')
43
- ? changed.startsWith(listed)
44
- : changed === listed || changed.endsWith('/' + listed)
45
- );
46
- }
36
+ import { reconcileCapabilities, significantCodeChanges } from '../capabilities-reconcile';
37
+ import { formatEvidence, type ConstraintCheck } from './types';
47
38
 
48
39
  export const capabilitySync: ConstraintCheck = {
49
40
  id: 'capability_sync',
@@ -55,15 +46,6 @@ export const capabilitySync: ConstraintCheck = {
55
46
  }
56
47
  try {
57
48
  const capabilitiesPath = join(projectPath, 'CAPABILITIES.md');
58
- if (!existsSync(capabilitiesPath)) {
59
- // 检查是否有代码变更,无变更则跳过
60
- const diff = await env.stagedDiffNames();
61
- const changedCodeFiles = diff.split('\n').filter(
62
- (f: string) => f.endsWith('.ts') || f.endsWith('.tsx') || f.endsWith('.js')
63
- );
64
- return changedCodeFiles.length === 0;
65
- }
66
-
67
49
  const content = readFileSync(capabilitiesPath, 'utf-8');
68
50
  const capabilitiesMode = getCapabilitiesMode(projectPath);
69
51
 
@@ -72,71 +54,68 @@ export const capabilitySync: ConstraintCheck = {
72
54
  return true;
73
55
  }
74
56
 
75
- const listedFiles = parseCapabilitiesEntries(content, { secondColumnOnly: true });
76
- // module 模式下目录条目同样构成覆盖依据,与 step 2 判定对齐
77
- const entries =
78
- capabilitiesMode === 'module'
79
- ? parseCapabilitiesEntries(content, { includeDirs: true })
80
- : listedFiles;
81
- // 有表格但零条目 = 文档什么都没登记,不得直接放行;无表格的散文文档保持历史放行
82
- const hasTable = TABLE_SEPARATOR_REGEX.test(content);
57
+ const sourceRoots = detectSourceRoots(projectPath);
58
+ const population: string[] = [];
59
+ for (const root of sourceRoots) {
60
+ if (existsSync(join(projectPath, root))) {
61
+ population.push(...env.srcScan(root));
62
+ }
63
+ }
83
64
 
84
- // ── Step 1: Git diff 增量检查 ──
85
- const diff = await env.stagedDiffNames();
86
- const changedCodeFiles = diff.split('\n').filter(
87
- (f: string) => f.endsWith('.ts') || f.endsWith('.tsx') || f.endsWith('.js')
88
- );
89
- const significantChanges = changedCodeFiles.filter(
90
- (f: string) => !f.includes('__tests__') && !f.includes('.test.') && !f.includes('.spec.')
91
- );
65
+ const diffNames = (await env.stagedDiffNames()).split('\n').filter(Boolean);
66
+ const verdict = reconcileCapabilities({
67
+ content,
68
+ populationFiles: population,
69
+ changedFiles: significantCodeChanges(diffNames),
70
+ sourceRoots,
71
+ });
92
72
 
93
- // 每个变更文件都必须被覆盖(every;some 会让一个命中掩盖其余漏网)
94
- if (
95
- significantChanges.length > 0 &&
96
- (entries.length > 0 || hasTable) &&
97
- !significantChanges.every((changed: string) => isCoveredBy(entries, changed))
98
- ) {
99
- return false;
73
+ // 无表格的散文文档:历史放行语义
74
+ if (!verdict.hasTable) return true;
75
+
76
+ // 文档退化门:有表格却什么都没登记(限定「源码根下确有文件」——空仓 + 空表没有可登记
77
+ // 对象,历史行为是放行,本票不顺手扩大 fail 面)
78
+ if (verdict.coverageEntries.length === 0 && population.length > 0) {
79
+ return {
80
+ pass: false,
81
+ evidence: ['CAPABILITIES.md 有表格但零条目登记,运行 harness sync-docs 或直接补登记'],
82
+ };
100
83
  }
101
84
 
102
- // ── Step 2: 全量源码根扫描(T-058 修复核心)──
103
- if (capabilitiesMode === 'module') {
104
- // module 模式:目录条目参与覆盖(isCoveredBy 统一判定);
105
- // 未覆盖文件按源码根下第一级子目录聚合,任一组非空即失败
106
- if (entries.length > 0 || hasTable) {
107
- const uncoveredDirs = new Set<string>();
108
- const sourceRoots = detectSourceRoots(projectPath);
109
- for (const root of sourceRoots) {
110
- const srcDir = join(projectPath, root);
111
- if (existsSync(srcDir)) {
112
- for (const file of env.srcScan(root)) {
113
- if (!isCoveredBy(entries, file)) {
114
- uncoveredDirs.add(aggregateToSourceSubdir(root, file));
115
- }
116
- }
117
- }
118
- }
119
- if (uncoveredDirs.size > 0) return false;
120
- }
121
- } else if (listedFiles.length > 0 || hasTable) {
122
- const sourceRoots = detectSourceRoots(projectPath);
123
- for (const root of sourceRoots) {
124
- const srcDir = join(projectPath, root);
125
- if (existsSync(srcDir)) {
126
- for (const file of env.srcScan(root)) {
127
- if (!isCoveredBy(listedFiles, file)) {
128
- return false;
129
- }
130
- }
131
- }
132
- }
85
+ // ── Step 1: Git diff 增量检查(与本次变更有因果,每个变更文件都必须被覆盖)──
86
+ if (verdict.uncoveredChanges.length > 0) {
87
+ return {
88
+ pass: false,
89
+ evidence: formatEvidence(
90
+ `本次变更有 ${verdict.uncoveredChanges.length} 个文件未登记进 CAPABILITIES.md`,
91
+ verdict.uncoveredChanges
92
+ ),
93
+ };
94
+ }
95
+
96
+ // ── Step 2: 全量源码根扫描(T-058)——仓库级漂移,只提示不判违规,见文件头 ──
97
+ const gaps = capabilitiesMode === 'module' ? verdict.uncoveredDirs : verdict.uncoveredFiles;
98
+ if (gaps.length > 0) {
99
+ const kind = capabilitiesMode === 'module' ? '未登记模块目录' : '未登记源文件';
100
+ return {
101
+ pass: true,
102
+ evidence: formatEvidence(
103
+ `CAPABILITIES.md ${kind} ${gaps.length} 项(仓库级漂移,与本次变更无关;修复: harness sync-docs)`,
104
+ gaps
105
+ ),
106
+ };
133
107
  }
134
108
 
135
109
  return true;
136
110
  } catch (err) {
137
- // fail-open 语义保留,但必须可观测:静默吞错会让解析 bug 变成「永远通过」
111
+ // fail-open 语义保留,但异常放行也要落地可见:warn 只进本地 stderr,
112
+ // 进不了 trace——静默吞错会让解析 bug 变成「永远通过」(harness#119 同族问题)
113
+ const reason = err instanceof Error ? err.message : String(err);
138
114
  console.warn('[capability_sync] 检查执行异常,默认放行:', err);
139
- return true;
115
+ return {
116
+ pass: true,
117
+ evidence: [`检查异常,按 fail-open 放行: ${reason}(本次结果不代表文档已同步)`],
118
+ };
140
119
  }
141
120
  },
142
121
  };
@@ -1,43 +1,33 @@
1
1
  /**
2
2
  * context_doc_sync:关键目录必须有 CONTEXT.md(工单 21)
3
3
  *
4
- * ADR-0001 存在性探测:项目未配置 governance.context_files 约定
5
- * (未启用或无 required_dirs)时返回 'skip',不计 pass/fail。
4
+ * ADR-0001 存在性探测 + context_files 三态统一口径(工单 84):
5
+ * 未配置 / enabled 但无目标(required_dirs 缺失或空)→ 一律 'skip'
6
+ * 不计 pass/fail,与 docs_freshness 同构;判定只走 resolveContextFiles
7
+ * 访问器,不做 governance 手写钻取。
6
8
  */
7
9
 
8
10
  import { existsSync } from 'fs';
9
11
  import { join } from 'path';
10
- import { loadRawProjectConfig } from '../../project-config-loader';
12
+ import { resolveContextFiles } from '../../project-config-loader';
11
13
  import type { ConstraintCheck } from './types';
12
14
 
13
15
  export const contextDocSync: ConstraintCheck = {
14
16
  id: 'context_doc_sync',
15
17
  async evaluate(env) {
16
18
  const projectPath = env.projectPath;
17
- try {
18
- const config = loadRawProjectConfig(projectPath) ?? {};
19
- const governance = config.governance as Record<string, unknown> | undefined;
20
- const contextFiles = governance?.context_files as Record<string, unknown> | undefined;
21
-
22
- if (!contextFiles?.enabled || !Array.isArray(contextFiles.required_dirs)) {
23
- return 'skip'; // 未配置 context_files 约定,跳过评估
24
- }
25
-
26
- const requiredDirs = contextFiles.required_dirs as string[];
27
- if (requiredDirs.length === 0) {
28
- return 'skip'; // 无目标目录,约定未生效
29
- }
19
+ const resolution = resolveContextFiles(projectPath);
20
+ if (resolution.state !== 'enabled') {
21
+ return 'skip'; // 未配置或约定已立但无目标,跳过评估
22
+ }
30
23
 
31
- for (const dir of requiredDirs) {
32
- const contextPath = join(projectPath, dir, 'CONTEXT.md');
33
- if (!existsSync(contextPath)) {
34
- return false;
35
- }
24
+ for (const dir of resolution.dirs) {
25
+ const contextPath = join(projectPath, dir, 'CONTEXT.md');
26
+ if (!existsSync(contextPath)) {
27
+ return false;
36
28
  }
37
-
38
- return true;
39
- } catch {
40
- return 'skip'; // 配置不存在或解析失败 = 无法判定约定,跳过
41
29
  }
30
+
31
+ return true;
42
32
  },
43
33
  };
@@ -9,11 +9,13 @@
9
9
 
10
10
  import { existsSync, readFileSync } from 'fs';
11
11
  import { join } from 'path';
12
- import { loadRawProjectConfig } from '../../project-config-loader';
12
+ import { getGovernanceConfig, resolveContextFiles } from '../../project-config-loader';
13
13
  import { FreshnessRunner, type FreshnessCheckResult } from '../doc-freshness/runner';
14
14
  import { detectSourceRoots } from '../../../utils/detect-source-roots';
15
- import type { DocFreshnessConfig, DocFreshnessCheck } from '../../../types/project-config';
16
- import type { ConstraintCheck } from './types';
15
+ import { reconcileCapabilities } from '../capabilities-reconcile';
16
+ import type { DocFreshnessCheck } from '../../../types/project-config';
17
+ import type { ConstraintCheck, CheckEnv } from './types';
18
+ import { formatEvidence } from './types';
17
19
 
18
20
  /**
19
21
  * 内置默认文档新鲜度检查配置
@@ -30,45 +32,31 @@ function getBuiltInDocFreshnessConfig(): DocFreshnessCheck[] {
30
32
  }
31
33
 
32
34
  /**
33
- * CAPABILITIES.md 文件表新鲜度:列出的文件路径必须仍存在。
34
- * 多根查找:支持不同项目的源码根(harness=src/, studio=apps/api/src/ 等)。
35
- * 无表格行(能力清单格式)时跳过。
35
+ * CAPABILITIES.md 文件表新鲜度:登记的条目(文件与目录)必须仍存在(ADR-0009 口径从严)。
36
+ * 判定逻辑统一走 capabilities-reconcile;多根查找支持不同项目的源码根
37
+ * (harness=src/, studio=apps/api/src/ 等)。无表格(能力清单格式)时自然零幽灵。
36
38
  */
37
- function checkCapabilitiesFreshness(projectPath: string): boolean {
39
+ function findDeadCapabilityEntries(projectPath: string, env: CheckEnv): string[] {
38
40
  try {
39
41
  const capabilitiesPath = join(projectPath, 'CAPABILITIES.md');
40
- if (!existsSync(capabilitiesPath)) return true;
42
+ if (!existsSync(capabilitiesPath)) return [];
41
43
  const content = readFileSync(capabilitiesPath, 'utf-8');
42
44
 
43
- const listedFiles: string[] = [];
44
- const tableRowRegex = /^\|[^|]+\|\s*([^|]+?\.(?:ts|tsx|js|jsx))\s*\|/gm;
45
- let match;
46
- while ((match = tableRowRegex.exec(content)) !== null) {
47
- listedFiles.push(match[1].trim());
48
- }
49
-
50
- if (listedFiles.length === 0) return true;
51
-
52
45
  const sourceRoots = detectSourceRoots(projectPath);
53
- const fileExists = (file: string): boolean => {
54
- if (existsSync(join(projectPath, file))) return true;
55
- for (const root of sourceRoots) {
56
- if (existsSync(join(projectPath, root, file))) return true;
57
- }
58
- return false;
59
- };
60
-
61
- const missing = listedFiles.filter(f => !fileExists(f));
62
- if (missing.length > 0) {
63
- // 报出具体文件名:此前只返回 false,CLI 只能打印通用提示,
64
- // 幽灵条目 basename 碰撞时 sync-docs 也不剔除,用户无从定位(2026-08-08 studio CI 4 连红)
65
- console.error(`[docs_freshness] CAPABILITIES.md 列出的文件不存在: ${missing.join(', ')}`);
66
- return false;
46
+ const population: string[] = [];
47
+ for (const root of sourceRoots) {
48
+ if (existsSync(join(projectPath, root))) population.push(...env.srcScan(root));
67
49
  }
68
50
 
69
- return true;
51
+ const verdict = reconcileCapabilities({
52
+ content,
53
+ populationFiles: population,
54
+ sourceRoots,
55
+ fileExists: (rel) => existsSync(join(projectPath, rel)),
56
+ });
57
+ return verdict.deadEntries;
70
58
  } catch {
71
- return true;
59
+ return [];
72
60
  }
73
61
  }
74
62
 
@@ -77,7 +65,7 @@ function checkCapabilitiesFreshness(projectPath: string): boolean {
77
65
  *
78
66
  * 判定信号(任一命中即视为采用了文档新鲜度约定):
79
67
  * - 配置了 governance.doc_freshness.checks
80
- * - 配置了 governance.context_filesenabled + 非空 required_dirs)
68
+ * - 配置了 governance.context_files 且约定有效(enabled + 非空 required_dirs)
81
69
  * - 项目根存在 CAPABILITIES.md
82
70
  * - 项目根存在 CHANGELOG.md / CHANGELOG
83
71
  *
@@ -89,26 +77,12 @@ function hasFreshnessTargets(projectPath: string): boolean {
89
77
  return true;
90
78
  }
91
79
 
92
- try {
93
- const raw = loadRawProjectConfig(projectPath);
94
- const governance = raw?.governance as Record<string, unknown> | undefined;
95
-
96
- const freshnessConfig = governance?.doc_freshness as DocFreshnessConfig | undefined;
97
- if (freshnessConfig?.checks && freshnessConfig.checks.length > 0) return true;
98
-
99
- const contextFiles = governance?.context_files as Record<string, unknown> | undefined;
100
- if (
101
- contextFiles?.enabled &&
102
- Array.isArray(contextFiles.required_dirs) &&
103
- (contextFiles.required_dirs as string[]).length > 0
104
- ) {
105
- return true;
106
- }
107
- } catch {
108
- // 配置不可读视为无配置
80
+ const governance = getGovernanceConfig(projectPath);
81
+ if (governance?.doc_freshness?.checks && governance.doc_freshness.checks.length > 0) {
82
+ return true;
109
83
  }
110
84
 
111
- return false;
85
+ return resolveContextFiles(projectPath).state === 'enabled';
112
86
  }
113
87
 
114
88
  export const docsFreshness: ConstraintCheck = {
@@ -119,26 +93,27 @@ export const docsFreshness: ConstraintCheck = {
119
93
  // ADR-0001 存在性探测:项目无任何 freshness 配置/目标 → skip(不计 pass/fail)
120
94
  if (!hasFreshnessTargets(projectPath)) return 'skip';
121
95
 
122
- // Step 1: 文件表格式 — 检查列出的文件是否还存在
123
- if (!checkCapabilitiesFreshness(projectPath)) return false;
96
+ // Step 1: 文件表格式 — 登记的条目(文件+目录)是否仍存在(ADR-0009)
97
+ const deadEntries = findDeadCapabilityEntries(projectPath, env);
98
+ if (deadEntries.length > 0) {
99
+ // 证据通道点名条目(harness#119):此前是 console.error 侧信道(2026-08-08 studio
100
+ // CI 4 连红时的临时办法),CLI 结构化输出与 trace 两头都拿不到,
101
+ // 铁律拦截时用户只看到一句通用提示,basename 碰撞下无从定位
102
+ return {
103
+ pass: false,
104
+ evidence: formatEvidence('CAPABILITIES.md 登记的条目不存在', deadEntries),
105
+ };
106
+ }
124
107
 
125
108
  // Step 2: 能力清单格式 + CLAUDE.md + CHANGELOG — 通过 FreshnessRunner
126
109
  try {
127
- let freshnessConfig: DocFreshnessConfig | undefined;
128
- let requiredDirs: string[] | undefined;
129
-
130
- try {
131
- const raw = loadRawProjectConfig(projectPath);
132
- const governance = raw?.governance as Record<string, unknown> | undefined;
133
- freshnessConfig = governance?.doc_freshness as DocFreshnessConfig | undefined;
134
-
135
- const contextFiles = governance?.context_files as Record<string, unknown> | undefined;
136
- if (contextFiles?.enabled && Array.isArray(contextFiles.required_dirs)) {
137
- requiredDirs = contextFiles.required_dirs as string[];
138
- }
139
- } catch {
140
- // 配置解析失败,使用默认
141
- }
110
+ const freshnessConfig = getGovernanceConfig(projectPath)?.doc_freshness;
111
+
112
+ // context_files 三态统一口径(工单 84):约定已立但无目标(enabled 但
113
+ // required_dirs 缺失/空)→ skip,与 context_doc_sync 同构;不再静默放行
114
+ const contextFiles = resolveContextFiles(projectPath);
115
+ if (contextFiles.state === 'enabled-empty') return 'skip';
116
+ const requiredDirs = contextFiles.state === 'enabled' ? contextFiles.dirs : undefined;
142
117
 
143
118
  const runner = new FreshnessRunner();
144
119
  let results: FreshnessCheckResult[];
@@ -149,7 +124,16 @@ export const docsFreshness: ConstraintCheck = {
149
124
  results = runner.runAll({ checks: getBuiltInDocFreshnessConfig() }, projectPath, { requiredDirs });
150
125
  }
151
126
 
152
- if (!results.every(r => r.pass)) return false;
127
+ const failed = results.filter(r => !r.pass);
128
+ if (failed.length > 0) {
129
+ return {
130
+ pass: false,
131
+ evidence: formatEvidence(
132
+ '文档新鲜度检查未通过',
133
+ failed.map(r => `${r.label}: ${r.message ?? '未通过'}`)
134
+ ),
135
+ };
136
+ }
153
137
  } catch {
154
138
  // FreshnessRunner 失败不影响整体
155
139
  }
@@ -10,42 +10,24 @@
10
10
  * 不强制迁移;两处都没有才报违规。
11
11
  */
12
12
 
13
- import { readFileSync } from 'fs';
14
13
  import { join } from 'path';
15
14
  import { loadRawProjectConfig } from '../../project-config-loader';
15
+ import { hasGovernanceContract, hasPreserveBlock, readIfExists } from '../injection-writer';
16
16
  import type { ConstraintCheck } from './types';
17
17
 
18
18
  /** 治理契约 PRESERVE 段名(ADR 落点模型约定) */
19
19
  export const GOVERNANCE_PRESERVE_NAME = 'governance';
20
20
 
21
- const GOVERNANCE_BEGIN_RE = /^<!-- PRESERVE:governance -->\s*$/m;
22
-
23
- /** AGENTS.md 是否存在非空 PRESERVE:governance 块(标记须独占一行,与 preserve-block.ts 同语义) */
21
+ /** AGENTS.md 是否存在非空 PRESERVE:governance 块(切片判定收口 injection-writer.hasPreserveBlock,#83) */
24
22
  export function hasGovernancePreserveBlock(agentsMdPath: string): boolean {
25
- let content: string;
26
- try {
27
- content = readFileSync(agentsMdPath, 'utf-8');
28
- } catch {
29
- return false;
30
- }
31
- const beginMatch = GOVERNANCE_BEGIN_RE.exec(content);
32
- if (!beginMatch) return false;
33
- const rest = content.slice(beginMatch.index + beginMatch[0].length);
34
- const endIdx = rest.indexOf(`<!-- /PRESERVE:${GOVERNANCE_PRESERVE_NAME} -->`);
35
- if (endIdx === -1) return false;
36
- // 块体(首尾标记之间)去空白后非空才算「在场」
37
- return rest.slice(0, endIdx).trim().length > 0;
23
+ const content = readIfExists(agentsMdPath);
24
+ return content !== null && hasPreserveBlock(content, GOVERNANCE_PRESERVE_NAME);
38
25
  }
39
26
 
40
- /** CLAUDE.md 是否存在治理块(与 agents-syncer getGovernanceInfo 同判定,旧模型仓豁免) */
27
+ /** CLAUDE.md 是否有治理契约在场(判定收口 injection-writer.hasGovernanceContract,#83;旧模型仓豁免) */
41
28
  export function hasClaudeGovernance(claudeMdPath: string): boolean {
42
- let content: string;
43
- try {
44
- content = readFileSync(claudeMdPath, 'utf-8');
45
- } catch {
46
- return false;
47
- }
48
- return /^##\s+Governance Rules/m.test(content) || content.includes('HARNESS_CONSTRAINTS_START');
29
+ const content = readIfExists(claudeMdPath);
30
+ return content !== null && hasGovernanceContract(content);
49
31
  }
50
32
 
51
33
  export const governancePresence: ConstraintCheck = {
@@ -80,5 +80,12 @@ export function registeredCheckCount(): number {
80
80
  return registry.size;
81
81
  }
82
82
 
83
- export { buildCheckEnv } from './types';
84
- export type { ConstraintCheck, CheckEnv, CheckOutcome, EvidenceProviders } from './types';
83
+ export { buildCheckEnv, normalizeCheckOutcome } from './types';
84
+ export type {
85
+ ConstraintCheck,
86
+ CheckEnv,
87
+ CheckOutcome,
88
+ CheckDetail,
89
+ NormalizedOutcome,
90
+ EvidenceProviders,
91
+ } from './types';
@@ -4,10 +4,11 @@
4
4
 
5
5
  import type { ConstraintCheck } from './types';
6
6
 
7
+ // m 标志必需:真实 diff 首行恒为 `diff --git`,缺 m 则 ^ 永不命中删除行(铁律静默失效)
7
8
  const DELETED_TEST_PATTERNS = [
8
- /^-\s*(test|it|describe)\s*\(/, // 删除 test/it/describe
9
- /^-\s*expect\s*\(/, // 删除 expect
10
- /^-\s*\/\/\s*test/, // 删除注释的 test
9
+ /^-\s*(test|it|describe)\s*\(/m, // 删除 test/it/describe
10
+ /^-\s*expect\s*\(/m, // 删除 expect
11
+ /^-\s*\/\/\s*test/m, // 删除注释的 test
11
12
  ];
12
13
 
13
14
  export const noTestSimplification: ConstraintCheck = {
@@ -29,8 +29,8 @@ export interface CheckEnv {
29
29
  /**
30
30
  * 证据提供者组:git diff / 源码扫描的 memoized I/O
31
31
  *
32
- * 由调用方绑定后传入 buildCheckEnv(如 ConstraintChecker 绑实例级
33
- * run 缓存的方法);工厂本身不做 memoization。
32
+ * 由调用方绑定后传入 buildCheckEnv(#87:git 两项绑 GitEvidence adapter 实例,
33
+ * 一次 run 一份,命令级 memo 在 adapter 层);工厂本身不做 memoization。
34
34
  */
35
35
  export interface EvidenceProviders {
36
36
  /** staged 全量 diff */
@@ -73,8 +73,64 @@ export function buildCheckEnv(
73
73
  * - true = 满足(pass)
74
74
  * - false = 违反(fail)
75
75
  * - 'skip' = 未评估(项目未采用对应约定,或证据 flag 未接线),不计 pass/fail
76
+ * - CheckDetail = 判定 + 证据行(harness#119)
76
77
  */
77
- export type CheckOutcome = boolean | 'skip';
78
+ export type CheckOutcome = boolean | 'skip' | CheckDetail;
79
+
80
+ /**
81
+ * 带证据的检查结果(harness#119)
82
+ *
83
+ * 动机:只回布尔时,fail 的具体原因(哪个文件没登记)在 CLI 与 trace 两头都落脚不了,
84
+ * 一条恒红的约束无法被诊断。证据行由 checker 自行措辞(自描述、可直接给人看),
85
+ * 编排层只负责透传与打印,不参与解释内容。
86
+ *
87
+ * `pass: true` + 非空 evidence = 「满足但有提示」:不进 pass/fail 统计口径,
88
+ * 仅作为可观测输出(如仓库级文档漂移——与本次变更无因果,不该判违规)。
89
+ */
90
+ export interface CheckDetail {
91
+ /** false = 违反;true = 满足 */
92
+ pass: boolean;
93
+ /** 证据行(每条自描述,如「变更文件未登记: src/foo.ts」) */
94
+ evidence?: string[];
95
+ }
96
+
97
+ /** 归一后的检查结果:编排层 / gate 唯一消费形状 */
98
+ export interface NormalizedOutcome {
99
+ satisfied: boolean;
100
+ skipped: boolean;
101
+ evidence: string[];
102
+ }
103
+
104
+ /** 证据条数上限:trace 是 JSONL,缺口上百项时不能整仓落盘 */
105
+ export const MAX_EVIDENCE_ITEMS = 10;
106
+
107
+ /**
108
+ * 组装证据行:一行说明 + 逐条依据(超限截断)
109
+ *
110
+ * 每行自描述且不带缩进——缩进由消费端(CLI / gate 理由 / 铁律异常文案)决定,trace 原样存。
111
+ * 修复入口不写在这里:那是各约束自己的事,写进各自的 summary 行(docs_freshness 的
112
+ * 幽灵条目与 sync-docs 无关,共用一个指针会误导)。
113
+ */
114
+ export function formatEvidence(summary: string, items: string[]): string[] {
115
+ const shown = items.slice(0, MAX_EVIDENCE_ITEMS);
116
+ const hidden = items.length - shown.length;
117
+ return [...(summary ? [summary] : []), ...shown, ...(hidden > 0 ? [`…另 ${hidden} 项`] : [])];
118
+ }
119
+
120
+ /**
121
+ * 归一 CheckOutcome(boolean | 'skip' | CheckDetail)为统一形状
122
+ *
123
+ * skip 恒不携带证据:未评估的约束不得留下证据行(否则统计侧会把「没查」读成「查出问题」)。
124
+ */
125
+ export function normalizeCheckOutcome(outcome: CheckOutcome): NormalizedOutcome {
126
+ if (outcome === 'skip') return { satisfied: true, skipped: true, evidence: [] };
127
+ if (typeof outcome === 'boolean') return { satisfied: outcome, skipped: false, evidence: [] };
128
+ return {
129
+ satisfied: outcome.pass,
130
+ skipped: false,
131
+ evidence: outcome.evidence ?? [],
132
+ };
133
+ }
78
134
 
79
135
  /**
80
136
  * 单条约束检查实现
@@ -82,7 +138,7 @@ export type CheckOutcome = boolean | 'skip';
82
138
  export interface ConstraintCheck {
83
139
  /** 约束 ID(与 definitions 一致) */
84
140
  id: string;
85
- /** 检查主体:true = 满足;false = 违反;'skip' = 未评估 */
141
+ /** 检查主体:true = 满足;false = 违反;'skip' = 未评估;CheckDetail = 判定 + 证据行 */
86
142
  evaluate(env: CheckEnv): Promise<CheckOutcome> | CheckOutcome;
87
143
  }
88
144