@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
@@ -0,0 +1,221 @@
1
+ /**
2
+ * tools 能力目录 registry 完整性测试(架构评审 2026-09-02 候选12 / harness#91)
3
+ *
4
+ * `src/tools/definitions/` 是随包发布的数据(package.json files 含 src),studio 经包根
5
+ * 公开导出消费(`getToolsDir` / `getRegistryPath`)——此前代码半边有 public-exports 钉住导出面,
6
+ * 数据半边零测试:幽灵项 / 漏登项 / path 悬空 / name 漂移都没人守。
7
+ * 这是 ADR-0002「注册型能力一律定义即注册 + 构建期闭环」唯一没覆盖的一类注册表。
8
+ *
9
+ * 裁决(#91 triage):
10
+ * - **yml 目录是正本**,registry.json 是派生索引 → 对照方向 = 双向集合一致
11
+ * - **不做 yml schema 校验**(那等于替 studio 定能力契约,破坏面外溢到 studio)
12
+ * - `rollback` 重名 ×2(std/deploy 与 std/governance)是既有事实,进豁免名单;
13
+ * 重名本身是否合法另票裁决 → 名单外的任何新重名一律红
14
+ *
15
+ * 判定形状参照 ADR-0009 的 reconcile:读声明 → 对照文件系统 → 产出双向差异清单,
16
+ * 失败信息一次报全(不逐条断言到第一个错就停)。
17
+ */
18
+
19
+ import * as fs from 'fs';
20
+ import * as path from 'path';
21
+ import * as yaml from 'js-yaml';
22
+ import { getRegistryPath, getToolsDir } from '../paths';
23
+ import { walkFiles } from '../../utils/file-walk';
24
+ import { createProjectFixture } from '../../test-setup/project-fixture';
25
+
26
+ /** registry.json 条目(只取本测试关心的两字段,schema 校验是 out of scope) */
27
+ interface RegistryEntry {
28
+ name: string;
29
+ path: string;
30
+ }
31
+
32
+ /** 一个 tools 定义目录的读取结果:派生索引声明 + 正本文件清单 + 正本内 name */
33
+ interface DefinitionsSnapshot {
34
+ /** registry.json 声明的条目 */
35
+ entries: RegistryEntry[];
36
+ /** definitions/ 下实际存在的 yml(相对定义根,`/` 分隔) */
37
+ ymlPaths: string[];
38
+ /** yml 内 name(相对路径 → name;读不到/非字符串 → null) */
39
+ ymlNames: Record<string, string | null>;
40
+ }
41
+
42
+ /** 双向漂移清单(ADR-0009 形状:一次产出全部判定,消费方按需取用) */
43
+ interface RegistryDrift {
44
+ /** 幽灵项:registry 登记了磁盘上不存在的 yml(含 path 悬空) */
45
+ ghostPaths: string[];
46
+ /** 漏登项:磁盘上的 yml 未被 registry 任何条目引用 */
47
+ unregisteredPaths: string[];
48
+ /** name 漂移:registry.name 与 yml 内 name 不一致(含 yml 缺 name) */
49
+ nameMismatches: string[];
50
+ /** 重名:同 name 被多条条目使用(排序) */
51
+ duplicateNames: string[];
52
+ }
53
+
54
+ /** 既有事实豁免:#91 裁决保留的重名,名单外新增即红 */
55
+ const KNOWN_DUPLICATE_NAMES = ['rollback'];
56
+
57
+ const EMPTY_DRIFT: RegistryDrift = {
58
+ ghostPaths: [],
59
+ unregisteredPaths: [],
60
+ nameMismatches: [],
61
+ duplicateNames: [],
62
+ };
63
+
64
+ /** 读一个定义目录:registry.json 声明 + yml 正本清单与其中的 name(相对路径统一 `/` 分隔) */
65
+ function readDefinitions(definitionsDir: string): DefinitionsSnapshot {
66
+ const registry = JSON.parse(fs.readFileSync(path.join(definitionsDir, 'registry.json'), 'utf-8')) as {
67
+ tools: RegistryEntry[];
68
+ };
69
+ const ymlPaths = walkFiles(definitionsDir, { filter: name => name.endsWith('.yml') })
70
+ .map(full => path.relative(definitionsDir, full).split(path.sep).join('/'))
71
+ .sort();
72
+ const ymlNames: Record<string, string | null> = {};
73
+ for (const rel of ymlPaths) {
74
+ const doc = yaml.load(fs.readFileSync(path.join(definitionsDir, rel), 'utf-8')) as
75
+ | { name?: unknown }
76
+ | undefined;
77
+ ymlNames[rel] = typeof doc?.name === 'string' ? doc.name : null;
78
+ }
79
+ return { entries: registry.tools, ymlPaths, ymlNames };
80
+ }
81
+
82
+ /** 双向对照(纯判定,不做 IO——ADR-0009 同口径) */
83
+ function reconcileRegistry(snapshot: DefinitionsSnapshot): RegistryDrift {
84
+ const ymlSet = new Set(snapshot.ymlPaths);
85
+ const declaredPaths = new Set<string>();
86
+ const seenNames = new Set<string>();
87
+ const duplicateNames = new Set<string>();
88
+ const ghostPaths: string[] = [];
89
+ const nameMismatches: string[] = [];
90
+
91
+ for (const entry of snapshot.entries) {
92
+ declaredPaths.add(entry.path);
93
+ if (!ymlSet.has(entry.path)) {
94
+ ghostPaths.push(entry.path);
95
+ continue; // 正本不存在,name 无从对照(幽灵项已含该条)
96
+ }
97
+ const ymlName = snapshot.ymlNames[entry.path];
98
+ if (ymlName !== entry.name) {
99
+ nameMismatches.push(
100
+ `${entry.path}: registry="${entry.name}" ${ymlName === null ? 'yml=<缺失>' : `yml="${ymlName}"`}`
101
+ );
102
+ }
103
+ if (seenNames.has(entry.name)) duplicateNames.add(entry.name);
104
+ seenNames.add(entry.name);
105
+ }
106
+
107
+ return {
108
+ ghostPaths: ghostPaths.sort(),
109
+ unregisteredPaths: snapshot.ymlPaths.filter(p => !declaredPaths.has(p)).sort(),
110
+ nameMismatches: nameMismatches.sort(),
111
+ duplicateNames: [...duplicateNames].sort(),
112
+ };
113
+ }
114
+
115
+ /** 临时定义根夹具(经 project-fixture 建根,afterAll 由 mkdtemp-cleanup 回收) */
116
+ function makeDefinitionsFixture(
117
+ label: string,
118
+ tools: RegistryEntry[],
119
+ ymls: Record<string, string>
120
+ ): string {
121
+ const files: Record<string, string> = {
122
+ 'definitions/registry.json': JSON.stringify({ tools }, null, 2),
123
+ };
124
+ for (const [rel, content] of Object.entries(ymls)) files[`definitions/${rel}`] = content;
125
+ return path.join(createProjectFixture({ name: `tools-registry-${label}`, files }), 'definitions');
126
+ }
127
+
128
+ const CLEAN_TOOLS: RegistryEntry[] = [
129
+ { name: 'code/parse', path: 'core/code/parse.yml' },
130
+ { name: 'deploy/rollback', path: 'std/deploy/rollback.yml' },
131
+ ];
132
+ const CLEAN_YMLS = {
133
+ 'core/code/parse.yml': 'name: code/parse\ndescription: parse\n',
134
+ 'std/deploy/rollback.yml': 'name: deploy/rollback\ndescription: rollback\n',
135
+ };
136
+
137
+ describe('tools registry 完整性 — 判定形状(ADR-0009 reconcile)', () => {
138
+ it('干净夹具:双向零漂移(不误报)', () => {
139
+ const snapshot = readDefinitions(makeDefinitionsFixture('clean', CLEAN_TOOLS, CLEAN_YMLS));
140
+ expect(reconcileRegistry(snapshot)).toEqual(EMPTY_DRIFT);
141
+ });
142
+
143
+ it('幽灵项 / path 悬空:registry 登记磁盘不存在的 yml → 红(闭环失败方向 1)', () => {
144
+ const snapshot = readDefinitions(
145
+ makeDefinitionsFixture('ghost', [...CLEAN_TOOLS, { name: 'ghost/item', path: 'std/ghost/item.yml' }], CLEAN_YMLS)
146
+ );
147
+ expect(reconcileRegistry(snapshot).ghostPaths).toEqual(['std/ghost/item.yml']);
148
+ });
149
+
150
+ it('漏登项:yml 存在但 registry 未登记 → 红(闭环失败方向 2)', () => {
151
+ const snapshot = readDefinitions(
152
+ makeDefinitionsFixture(
153
+ 'missing',
154
+ CLEAN_TOOLS,
155
+ { ...CLEAN_YMLS, 'std/unlisted/new-tool.yml': 'name: unlisted/new-tool\n' }
156
+ )
157
+ );
158
+ expect(reconcileRegistry(snapshot).unregisteredPaths).toEqual(['std/unlisted/new-tool.yml']);
159
+ });
160
+
161
+ it('name 漂移:registry.name 与 yml 内 name 不一致 → 红', () => {
162
+ const snapshot = readDefinitions(
163
+ makeDefinitionsFixture('name-drift', CLEAN_TOOLS, {
164
+ ...CLEAN_YMLS,
165
+ 'core/code/parse.yml': 'name: code/renamed\ndescription: parse\n',
166
+ })
167
+ );
168
+ expect(reconcileRegistry(snapshot).nameMismatches).toEqual([
169
+ 'core/code/parse.yml: registry="code/parse" yml="code/renamed"',
170
+ ]);
171
+ });
172
+
173
+ it('yml 缺 name 字段 → 计入 name 漂移(不做 schema 校验,但 name 是对照锚点)', () => {
174
+ const snapshot = readDefinitions(
175
+ makeDefinitionsFixture('name-absent', CLEAN_TOOLS, {
176
+ ...CLEAN_YMLS,
177
+ 'core/code/parse.yml': 'description: parse\n',
178
+ })
179
+ );
180
+ expect(reconcileRegistry(snapshot).nameMismatches).toEqual([
181
+ 'core/code/parse.yml: registry="code/parse" yml=<缺失>',
182
+ ]);
183
+ });
184
+
185
+ it('豁免名单外的新重名 → 红(rollback 重名合法性另票裁决,#91)', () => {
186
+ const snapshot = readDefinitions(
187
+ makeDefinitionsFixture(
188
+ 'dup',
189
+ [...CLEAN_TOOLS, { name: 'deploy/rollback', path: 'std/governance/rollback.yml' }],
190
+ { ...CLEAN_YMLS, 'std/governance/rollback.yml': 'name: deploy/rollback\n' }
191
+ )
192
+ );
193
+ expect(reconcileRegistry(snapshot).duplicateNames).toEqual(['deploy/rollback']);
194
+ });
195
+ });
196
+
197
+ describe('tools registry 完整性 — 真实数据(definitions/ 随包发布)', () => {
198
+ const snapshot = readDefinitions(getToolsDir());
199
+
200
+ it('yml 正本与 registry 派生索引双向一致:无幽灵、无漏登、path 全部指向存在的 yml', () => {
201
+ const drift = reconcileRegistry(snapshot);
202
+ expect({ ...drift, duplicateNames: drift.duplicateNames.filter(n => !KNOWN_DUPLICATE_NAMES.includes(n)) })
203
+ .toEqual(EMPTY_DRIFT);
204
+ });
205
+
206
+ it('重名豁免名单钉死:当前唯一重名 = rollback ×2,名单变化即红', () => {
207
+ expect(reconcileRegistry(snapshot).duplicateNames).toEqual(KNOWN_DUPLICATE_NAMES);
208
+ });
209
+
210
+ it('src/tools/CONTEXT.md 声明的 yml 计数与实际一致', () => {
211
+ const contextSource = fs.readFileSync(path.join(getToolsDir(), '..', 'CONTEXT.md'), 'utf-8');
212
+ const declared = contextSource.match(/(\d+) 个 yml/);
213
+ expect(declared).not.toBeNull();
214
+ expect(Number(declared?.[1])).toBe(snapshot.ymlPaths.length);
215
+ });
216
+
217
+ it('包根公开导出的两个路径入口指向存在的 definitions/ 与 registry.json(studio 消费面)', () => {
218
+ expect(fs.existsSync(getToolsDir())).toBe(true);
219
+ expect(fs.existsSync(getRegistryPath())).toBe(true);
220
+ });
221
+ });
@@ -1,14 +1,13 @@
1
1
  /**
2
- * src/types 模块导出覆盖测试
2
+ * src/types 类型层行为测试
3
3
  */
4
4
 
5
5
  import { describe, it, expect } from '@jest/globals';
6
6
 
7
- // barrel index.ts 导入,覆盖 re-export 行
8
- import { ConstraintViolationError } from '..';
7
+ import { ConstraintViolationError } from '../constraint';
9
8
 
10
- describe('types barrel exports', () => {
11
- it('ConstraintViolationError 应该可从 index 导入', () => {
9
+ describe('ConstraintViolationError 导出', () => {
10
+ it('ConstraintViolationError 应该可导入', () => {
12
11
  expect(ConstraintViolationError).toBeDefined();
13
12
  expect(typeof ConstraintViolationError).toBe('function');
14
13
  });
@@ -108,7 +108,16 @@ export interface ConstraintResult {
108
108
 
109
109
  /** 消息 */
110
110
  message?: string;
111
-
111
+
112
+ /**
113
+ * 证据行(harness#119)
114
+ *
115
+ * checker 经 CheckDetail 返回的具体判定依据(如未登记的文件路径)。
116
+ * 违规时随 message 一并展示;满足但非空时为「提示」(不进 pass/fail 统计)。
117
+ * skip 恒为空——未评估不得留下证据行。
118
+ */
119
+ evidence?: string[];
120
+
112
121
  /** 建议操作 */
113
122
  requiredAction?: string;
114
123
 
@@ -177,39 +186,14 @@ export interface ConstraintContext {
177
186
  /** 是否已进行复用检查 */
178
187
  hasReuseCheck?: boolean;
179
188
 
180
- /** 复用检查结果 */
181
- reuseCheckResult?: {
182
- existingCapabilities: string[];
183
- canReuse: boolean;
184
- reuseRecommendation: string;
185
- };
186
-
187
189
  /** 是否只处理单个任务(用于 incremental_progress) */
188
190
  hasSingleTask?: boolean;
189
191
 
190
- /** 是否已验证外部能力(对应约束已降级为 prompt,字段保留兼容) */
191
- hasExternalCapabilityVerification?: boolean;
192
-
193
- /** 是否已对比需求验证(review 变体已并入主约束,字段保留兼容) */
194
- hasRequirementReview?: boolean;
195
-
196
192
  /** 是否有需求文档(用于 no_implementation_without_requirement) */
197
193
  hasRequirement?: boolean;
198
194
 
199
- /** 是否在 worktree 中执行(对应约束已移出内置,字段保留兼容) */
200
- hasWorktree?: boolean;
201
-
202
195
  /** worktree 路径(用于 worktree 相关检查) */
203
196
  worktreePath?: string;
204
-
205
- /** 完成声明文本(对应检查已并入 prompt 层,字段保留兼容) */
206
- completionClaimText?: string;
207
-
208
- /** 是否完成两阶段审查(对应约束已移出内置,字段保留兼容) */
209
- hasTwoStageReview?: boolean;
210
-
211
- /** 执行上下文 */
212
- executionContext?: any;
213
197
  }
214
198
 
215
199
  /**
@@ -219,7 +203,12 @@ export class ConstraintViolationError extends Error {
219
203
  public readonly result: ConstraintResult;
220
204
 
221
205
  constructor(result: ConstraintResult) {
222
- super(result.message || 'Constraint violation');
206
+ // 铁律违规在 checkConstraints 处直接 throw,CLI 的结构化输出块走不到这里,
207
+ // error.message 是判定证据唯一的外溢面(harness#119)
208
+ const detail = result.evidence?.length
209
+ ? `:\n${result.evidence.map((line) => ` - ${line}`).join('\n')}`
210
+ : '';
211
+ super(`${result.message || 'Constraint violation'}${detail}`);
223
212
  this.name = 'ConstraintViolationError';
224
213
  this.result = result;
225
214
  }
@@ -9,13 +9,20 @@ export interface PassesGateConfig {
9
9
  /** 是否启用 */
10
10
  enabled?: boolean;
11
11
 
12
- /** 测试命令 */
12
+ /**
13
+ * 测试命令。缺省时走 core/validators/passes-gate.ts 的 detectTestCommand 探测:
14
+ * test:ci → test(排除 echo 占位脚本)→ test:e2e → test:coverage → Python 标记 → go.mod;
15
+ * 探不到则 fail-closed(不再兜底 'npm test')
16
+ */
13
17
  testCommand?: string;
14
18
 
15
19
  /** 是否需要证据 */
16
20
  requireEvidence?: boolean;
17
21
 
18
- /** 是否允许部分通过 */
22
+ /**
23
+ * 是否允许部分通过:退出码为 0 时,输出里的失败信号不再否决判定。
24
+ * 不赦免非零退出码(判定依据收在 core/validators/test-output.ts 的 judgeTestRun,ADR-0014)
25
+ */
19
26
  allowPartialPass?: boolean;
20
27
 
21
28
  /** 最大重试次数 */
@@ -50,9 +57,6 @@ export interface TaskTestResult {
50
57
  /** 失败的测试列表 */
51
58
  failures?: string[];
52
59
 
53
- /** 覆盖率 */
54
- coverage?: number;
55
-
56
60
  /** 证据路径 */
57
61
  evidence?: string;
58
62
  }
@@ -94,30 +98,6 @@ export interface DynamicTask {
94
98
  testResult?: TaskTestResult;
95
99
  }
96
100
 
97
- /**
98
- * PassesGate 扩展接口
99
- *
100
- * 用于注册额外的测试类型(如 Puppeteer E2E)
101
- */
102
- export interface PassesGateExtension {
103
- /** 扩展名称 */
104
- name: string;
105
-
106
- /** 扩展描述 */
107
- description?: string;
108
-
109
- /** 运行测试 */
110
- run(workDir: string, task?: DynamicTask): Promise<TaskTestResult>;
111
- }
112
-
113
- /**
114
- * 扩展测试结果(包含类型标识)
115
- */
116
- export interface ExtensionTestResult extends TaskTestResult {
117
- /** 测试类型(扩展名称) */
118
- type?: string;
119
- }
120
-
121
101
  // ========================================
122
102
  // AS-006: 纯约束验证接口
123
103
  // ========================================
@@ -134,7 +114,7 @@ export interface TestResult {
134
114
  /** 测试命令(可选,用于记录) */
135
115
  command?: string;
136
116
 
137
- /** 覆盖率(可选) */
117
+ /** 覆盖率(调用方自备数据,check() 不读它、只随 testResult 原样回显;harness 不取数不执法,ADR-0015) */
138
118
  coverage?: number;
139
119
 
140
120
  /** 证据路径(可选) */
@@ -41,7 +41,7 @@ export interface CustomConstraintDefinition {
41
41
  enabled?: boolean;
42
42
 
43
43
  /**
44
- * 退役元数据(#82 D6 统一落点)
44
+ * 退役元数据(studio#82 D6 统一落点)
45
45
  *
46
46
  * 存在即视为已退役:不进生效集、不注入 CLAUDE.md。恢复 = 删除该段。
47
47
  * 落点在 custom-constraints.yml 条目内(一处真相,不拆 config.yml 第二处)。
@@ -81,9 +81,10 @@ export interface TaskListJson {
81
81
  }
82
82
 
83
83
  /**
84
- * 动态任务定义
84
+ * 动态任务定义(模块内类型:包根同名导出是 types/passes-gate 版,此处仅
85
+ * TaskListJson/SessionInfo 结构引用;原 Extended* 别名导出已随 #85 删除)
85
86
  */
86
- export interface DynamicTask {
87
+ interface DynamicTask {
87
88
  id: string;
88
89
  name?: string;
89
90
  category?: string;
@@ -101,14 +102,13 @@ export interface DynamicTask {
101
102
  }
102
103
 
103
104
  /**
104
- * 测试结果
105
+ * 测试结果(模块内类型,理由同 DynamicTask)
105
106
  */
106
- export interface TaskTestResult {
107
+ interface TaskTestResult {
107
108
  passed: boolean;
108
109
  command: string;
109
110
  output?: string;
110
111
  failures?: string[];
111
- coverage?: number;
112
112
  timestamp: Date;
113
113
  evidence?: string;
114
114
  }
@@ -19,6 +19,8 @@ export const DEFAULT_TRACE_FILE = '.harness/logs/traces.log';
19
19
  * - 只记录核心字段(零 Token)
20
20
  * - 不记录代码片段(需要时从 git diff 获取)
21
21
  * - 不记录决策路径(需要时从 execution logs 获取)
22
+ * - 例外(harness#119):判定证据允许记录——路径级依据不是代码片段,
23
+ * 而没有它,一条恒红的约束在统计侧根本无法回答「红在哪」
22
24
  */
23
25
  export interface ExecutionTrace {
24
26
  // ========================================
@@ -53,6 +55,14 @@ export interface ExecutionTrace {
53
55
  /** 会话 ID(用于追踪同一会话的多次检查) */
54
56
  sessionId?: string;
55
57
 
58
+ /**
59
+ * 判定证据行(harness#119)
60
+ *
61
+ * 只落 checker 给出的路径级依据(如未登记文件清单),由 checker 截断条数;
62
+ * 不落 pass 结果的空数组,避免无信息记录膨胀 JSONL。
63
+ */
64
+ evidence?: string[];
65
+
56
66
  // ========================================
57
67
  // 用户响应(可选,用于诊断)
58
68
  // ========================================
@@ -0,0 +1,206 @@
1
+ /**
2
+ * markdown frontmatter 解析/序列化正本测试(harness#89,架构评审 2026-09-02 候选10)
3
+ *
4
+ * 三种畸形输入(缺 frontmatter / 未闭合 / 空 meta)与 YAML 非法的走法在本文件显式固定,
5
+ * 各消费方(store / migration / knowledge index / sdd index / ingest)只允许复用这套判定。
6
+ */
7
+
8
+ import { splitFrontmatter, joinFrontmatter } from '../frontmatter';
9
+
10
+ describe('splitFrontmatter — 合法 frontmatter 块', () => {
11
+ it('解析 meta + body:与 store/migration 既有写盘格式(---\\n<yaml>---\\n\\n<body>)互认', () => {
12
+ const raw = '---\nid: DEC-001\ntype: decision\n---\n\nBody line\n';
13
+ const result = splitFrontmatter(raw);
14
+ expect(result.state).toBe('ok');
15
+ if (result.state !== 'ok') return;
16
+ expect(result.meta).toEqual({ id: 'DEC-001', type: 'decision' });
17
+ expect(result.body).toBe('Body line\n');
18
+ });
19
+
20
+ it('闭合行之后无空行时正文原样保留', () => {
21
+ const result = splitFrontmatter('---\na: 1\n---\nBody\n');
22
+ expect(result.state).toBe('ok');
23
+ if (result.state !== 'ok') return;
24
+ expect(result.body).toBe('Body\n');
25
+ });
26
+
27
+ it('只吃闭合后的一个空行(与 join 的 `---\\n\\n` 包裹严格对称)', () => {
28
+ const result = splitFrontmatter('---\na: 1\n---\n\n\nBody\n');
29
+ expect(result.state).toBe('ok');
30
+ if (result.state !== 'ok') return;
31
+ expect(result.body).toBe('\nBody\n');
32
+ });
33
+
34
+ it('闭合行即文件末尾(无尾随换行)→ body 为空串,仍是合法块', () => {
35
+ const result = splitFrontmatter('---\na: 1\n---');
36
+ expect(result.state).toBe('ok');
37
+ if (result.state !== 'ok') return;
38
+ expect(result.meta).toEqual({ a: 1 });
39
+ expect(result.body).toBe('');
40
+ });
41
+
42
+ it('正文中的 --- 分隔线不被误吞:闭合取第一个独立 --- 行', () => {
43
+ const result = splitFrontmatter('---\na: 1\n---\n\nx\n\n---\n\ny\n');
44
+ expect(result.state).toBe('ok');
45
+ if (result.state !== 'ok') return;
46
+ expect(result.meta).toEqual({ a: 1 });
47
+ expect(result.body).toBe('x\n\n---\n\ny\n');
48
+ });
49
+
50
+ it('meta 内缩进的 --- 是块标量内容,不算闭合行', () => {
51
+ const result = splitFrontmatter('---\nnote: |\n ---\nb: 2\n---\nBody\n');
52
+ expect(result.state).toBe('ok');
53
+ if (result.state !== 'ok') return;
54
+ expect(result.meta).toEqual({ note: '---\n', b: 2 });
55
+ expect(result.body).toBe('Body\n');
56
+ });
57
+
58
+ it('闭合必须是整行:--- 后带内容的行(含 ---- 长分隔线)不作闭合', () => {
59
+ const result = splitFrontmatter('---\na: 1\n---broken\n----\nBody\n');
60
+ expect(result.state).toBe('malformed');
61
+ if (result.state !== 'malformed') return;
62
+ expect(result.reason).toBe('unterminated');
63
+ });
64
+ });
65
+
66
+ describe('splitFrontmatter — 三种畸形输入 + YAML 非法(一处定义)', () => {
67
+ it('缺 frontmatter:首行不是 --- → absent', () => {
68
+ expect(splitFrontmatter('# Title\n\nBody\n')).toEqual({ state: 'absent' });
69
+ });
70
+
71
+ it('缺 frontmatter:首行 --- 之前还有空白 → absent(起点必须在偏移 0)', () => {
72
+ expect(splitFrontmatter('\n---\na: 1\n---\nBody\n')).toEqual({ state: 'absent' });
73
+ });
74
+
75
+ it('空 meta:零宽元数据块 → absent(不承载信息,与收口前 store 丢条目的走法一致)', () => {
76
+ expect(splitFrontmatter('---\n---\n\nBody\n')).toEqual({ state: 'absent' });
77
+ });
78
+
79
+ it('空 meta:仅空白行 → absent', () => {
80
+ expect(splitFrontmatter('---\n\n \n---\nBody\n')).toEqual({ state: 'absent' });
81
+ });
82
+
83
+ it('未闭合:有开无闭 → malformed/unterminated', () => {
84
+ const result = splitFrontmatter('---\nid: X\ntitle: Y\n');
85
+ expect(result.state).toBe('malformed');
86
+ if (result.state !== 'malformed') return;
87
+ expect(result.reason).toBe('unterminated');
88
+ expect(result.detail).toContain('---');
89
+ });
90
+
91
+ it('YAML 语法错误 → malformed/invalid-yaml,detail 可定位', () => {
92
+ const result = splitFrontmatter('---\na: [1,\n---\nBody\n');
93
+ expect(result.state).toBe('malformed');
94
+ if (result.state !== 'malformed') return;
95
+ expect(result.reason).toBe('invalid-yaml');
96
+ expect(result.detail.length).toBeGreaterThan(0);
97
+ expect(result.detail).not.toContain('\n');
98
+ });
99
+
100
+ it('meta 是标量而非 mapping → malformed/invalid-yaml(消费方禁止再写 meta.id 式钻取)', () => {
101
+ const result = splitFrontmatter('---\njust a string\n---\nBody\n');
102
+ expect(result.state).toBe('malformed');
103
+ if (result.state !== 'malformed') return;
104
+ expect(result.reason).toBe('invalid-yaml');
105
+ });
106
+
107
+ it('meta 是数组 → malformed/invalid-yaml', () => {
108
+ const result = splitFrontmatter('---\n- a\n- b\n---\nBody\n');
109
+ expect(result.state).toBe('malformed');
110
+ if (result.state !== 'malformed') return;
111
+ expect(result.reason).toBe('invalid-yaml');
112
+ });
113
+
114
+ it('meta 是显式 null(`~`)→ malformed/invalid-yaml(与空白 meta 的 absent 区分)', () => {
115
+ const result = splitFrontmatter('---\n~\n---\nBody\n');
116
+ expect(result.state).toBe('malformed');
117
+ if (result.state !== 'malformed') return;
118
+ expect(result.reason).toBe('invalid-yaml');
119
+ });
120
+
121
+ it('CRLF 文件不按 frontmatter 处理 → absent(与收口前各消费方一致,不新增解释)', () => {
122
+ expect(splitFrontmatter('---\r\na: 1\r\n---\r\nBody\r\n')).toEqual({ state: 'absent' });
123
+ });
124
+ });
125
+
126
+ describe('joinFrontmatter', () => {
127
+ it('包裹格式与收口前逐字一致:`---\\n` + yaml.dump + `---\\n\\n` + body', () => {
128
+ expect(joinFrontmatter({ id: 'X' }, 'Body\n')).toBe('---\nid: X\n---\n\nBody\n');
129
+ });
130
+
131
+ it('空 body 也保留闭合后的空行分隔', () => {
132
+ expect(joinFrontmatter({ a: 1 }, '')).toBe('---\na: 1\n---\n\n');
133
+ });
134
+
135
+ it('按传入对象的键序落盘:排序策略留在消费方(裁决:收格式不收字段排序)', () => {
136
+ expect(joinFrontmatter({ b: 1, a: 2 }, '')).toBe('---\nb: 1\na: 2\n---\n\n');
137
+ });
138
+
139
+ it('dump 用 lineWidth 120:100 字符标量不折行(非 js-yaml 缺省 80)', () => {
140
+ const joined = joinFrontmatter({ title: 'a'.repeat(100) }, '');
141
+ expect(joined.split('\n')[1]).toBe(`title: ${'a'.repeat(100)}`);
142
+ });
143
+
144
+ it('dump 用 lineWidth 120:200 字符标量按 120 折行(非不折行)', () => {
145
+ const joined = joinFrontmatter({ title: 'a'.repeat(200) }, '');
146
+ expect(joined).toContain('title: >-\n');
147
+ });
148
+ });
149
+
150
+ describe('join ↔ split 往返闭环(已落盘条目不产生 diff 噪声)', () => {
151
+ const realisticMeta = {
152
+ id: 'PIT-001',
153
+ type: 'pitfall',
154
+ title: '端口不匹配: 3001 -> "API" 的三种走法',
155
+ maturity: 'verified',
156
+ layer: 'system',
157
+ created: '2026-05-28T00:00:00.000Z',
158
+ lastReferenced: '',
159
+ contributors: ['agent'],
160
+ projects: [],
161
+ tags: ['port', 'events'],
162
+ applicablePhases: ['IMPLEMENT'],
163
+ sourceReferences: [{ workflow: 'wf', timestamp: '2026-05-28T00:00:00.000Z' }],
164
+ referencedBy: [],
165
+ executionResults: [],
166
+ consumptionMode: 'reference',
167
+ origin: 'agent',
168
+ };
169
+
170
+ function roundTripIsStable(body: string): void {
171
+ const first = joinFrontmatter(realisticMeta, body);
172
+ const parsed = splitFrontmatter(first);
173
+ expect(parsed.state).toBe('ok');
174
+ if (parsed.state !== 'ok') return;
175
+ expect(parsed.meta).toEqual(realisticMeta);
176
+ expect(parsed.body).toBe(body);
177
+ expect(joinFrontmatter(parsed.meta, parsed.body)).toBe(first);
178
+ }
179
+
180
+ it('真实条目 meta + 正文:join → split → join 字节稳定', () => {
181
+ roundTripIsStable('根因:端口不一致。\n\n## 处置\n显式指定 API_PORT\n');
182
+ });
183
+
184
+ it('空正文稳定', () => {
185
+ roundTripIsStable('');
186
+ });
187
+
188
+ it('正文首行即 --- 分隔线时稳定(不误吞、不重复包裹)', () => {
189
+ roundTripIsStable('---\n\n附录\n');
190
+ });
191
+
192
+ it('正文含超长行时稳定', () => {
193
+ roundTripIsStable(`${'x'.repeat(300)}\n`);
194
+ });
195
+
196
+ it('meta 含被 120 折行的长标量时往返仍字节稳定(load→save 无 diff 噪声)', () => {
197
+ const meta = { ...realisticMeta, title: Array.from({ length: 40 }, (_, i) => `word${i}`).join(' ') };
198
+ const first = joinFrontmatter(meta, 'Body\n');
199
+ expect(first).toContain('title: >-\n');
200
+ const parsed = splitFrontmatter(first);
201
+ expect(parsed.state).toBe('ok');
202
+ if (parsed.state !== 'ok') return;
203
+ expect(parsed.meta).toEqual(meta);
204
+ expect(joinFrontmatter(parsed.meta, parsed.body)).toBe(first);
205
+ });
206
+ });