@dommaker/harness 1.2.3 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (550) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +0 -2
  3. package/bin/harness.js +86 -32
  4. package/dist/cli/command-contract.d.ts +83 -0
  5. package/dist/cli/command-contract.d.ts.map +1 -0
  6. package/dist/cli/command-contract.js +66 -0
  7. package/dist/cli/command-contract.js.map +1 -0
  8. package/dist/cli/commands/acceptance.d.ts +3 -2
  9. package/dist/cli/commands/acceptance.d.ts.map +1 -1
  10. package/dist/cli/commands/acceptance.js +28 -24
  11. package/dist/cli/commands/acceptance.js.map +1 -1
  12. package/dist/cli/commands/analyze-sessions.d.ts +2 -1
  13. package/dist/cli/commands/analyze-sessions.d.ts.map +1 -1
  14. package/dist/cli/commands/analyze-sessions.js +25 -24
  15. package/dist/cli/commands/analyze-sessions.js.map +1 -1
  16. package/dist/cli/commands/check.d.ts +14 -2
  17. package/dist/cli/commands/check.d.ts.map +1 -1
  18. package/dist/cli/commands/check.js +76 -61
  19. package/dist/cli/commands/check.js.map +1 -1
  20. package/dist/cli/commands/command.d.ts +5 -1
  21. package/dist/cli/commands/command.d.ts.map +1 -1
  22. package/dist/cli/commands/command.js +26 -17
  23. package/dist/cli/commands/command.js.map +1 -1
  24. package/dist/cli/commands/constraints-report.d.ts +2 -1
  25. package/dist/cli/commands/constraints-report.d.ts.map +1 -1
  26. package/dist/cli/commands/constraints-report.js +37 -33
  27. package/dist/cli/commands/constraints-report.js.map +1 -1
  28. package/dist/cli/commands/constraints-retire.d.ts +6 -5
  29. package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
  30. package/dist/cli/commands/constraints-retire.js +50 -52
  31. package/dist/cli/commands/constraints-retire.js.map +1 -1
  32. package/dist/cli/commands/constraints.d.ts +2 -1
  33. package/dist/cli/commands/constraints.d.ts.map +1 -1
  34. package/dist/cli/commands/constraints.js +10 -27
  35. package/dist/cli/commands/constraints.js.map +1 -1
  36. package/dist/cli/commands/contract.d.ts +3 -2
  37. package/dist/cli/commands/contract.d.ts.map +1 -1
  38. package/dist/cli/commands/contract.js +45 -42
  39. package/dist/cli/commands/contract.js.map +1 -1
  40. package/dist/cli/commands/definitions.d.ts +11 -11
  41. package/dist/cli/commands/definitions.d.ts.map +1 -1
  42. package/dist/cli/commands/definitions.js +30 -215
  43. package/dist/cli/commands/definitions.js.map +1 -1
  44. package/dist/cli/commands/failure.d.ts +5 -4
  45. package/dist/cli/commands/failure.d.ts.map +1 -1
  46. package/dist/cli/commands/failure.js +33 -26
  47. package/dist/cli/commands/failure.js.map +1 -1
  48. package/dist/cli/commands/init.d.ts +7 -5
  49. package/dist/cli/commands/init.d.ts.map +1 -1
  50. package/dist/cli/commands/init.js +147 -211
  51. package/dist/cli/commands/init.js.map +1 -1
  52. package/dist/cli/commands/knowledge.d.ts +21 -15
  53. package/dist/cli/commands/knowledge.d.ts.map +1 -1
  54. package/dist/cli/commands/knowledge.js +237 -199
  55. package/dist/cli/commands/knowledge.js.map +1 -1
  56. package/dist/cli/commands/passes-gate.d.ts +8 -2
  57. package/dist/cli/commands/passes-gate.d.ts.map +1 -1
  58. package/dist/cli/commands/passes-gate.js +44 -66
  59. package/dist/cli/commands/passes-gate.js.map +1 -1
  60. package/dist/cli/commands/performance.d.ts +2 -1
  61. package/dist/cli/commands/performance.d.ts.map +1 -1
  62. package/dist/cli/commands/performance.js +20 -18
  63. package/dist/cli/commands/performance.js.map +1 -1
  64. package/dist/cli/commands/posteval-plan.d.ts +5 -1
  65. package/dist/cli/commands/posteval-plan.d.ts.map +1 -1
  66. package/dist/cli/commands/posteval-plan.js +26 -15
  67. package/dist/cli/commands/posteval-plan.js.map +1 -1
  68. package/dist/cli/commands/release.d.ts +6 -1
  69. package/dist/cli/commands/release.d.ts.map +1 -1
  70. package/dist/cli/commands/release.js +73 -77
  71. package/dist/cli/commands/release.js.map +1 -1
  72. package/dist/cli/commands/report.d.ts +2 -1
  73. package/dist/cli/commands/report.d.ts.map +1 -1
  74. package/dist/cli/commands/report.js +9 -5
  75. package/dist/cli/commands/report.js.map +1 -1
  76. package/dist/cli/commands/review.d.ts +3 -2
  77. package/dist/cli/commands/review.d.ts.map +1 -1
  78. package/dist/cli/commands/review.js +33 -29
  79. package/dist/cli/commands/review.js.map +1 -1
  80. package/dist/cli/commands/sdd.d.ts +2 -1
  81. package/dist/cli/commands/sdd.d.ts.map +1 -1
  82. package/dist/cli/commands/sdd.js +7 -5
  83. package/dist/cli/commands/sdd.js.map +1 -1
  84. package/dist/cli/commands/security.d.ts +3 -2
  85. package/dist/cli/commands/security.d.ts.map +1 -1
  86. package/dist/cli/commands/security.js +29 -26
  87. package/dist/cli/commands/security.js.map +1 -1
  88. package/dist/cli/commands/spec-baseline-check.d.ts +2 -1
  89. package/dist/cli/commands/spec-baseline-check.d.ts.map +1 -1
  90. package/dist/cli/commands/spec-baseline-check.js +15 -11
  91. package/dist/cli/commands/spec-baseline-check.js.map +1 -1
  92. package/dist/cli/commands/spec.d.ts +7 -2
  93. package/dist/cli/commands/spec.d.ts.map +1 -1
  94. package/dist/cli/commands/spec.js +61 -51
  95. package/dist/cli/commands/spec.js.map +1 -1
  96. package/dist/cli/commands/status.d.ts +2 -1
  97. package/dist/cli/commands/status.d.ts.map +1 -1
  98. package/dist/cli/commands/status.js +57 -53
  99. package/dist/cli/commands/status.js.map +1 -1
  100. package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
  101. package/dist/cli/commands/sync-docs/agents-syncer.js +31 -16
  102. package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
  103. package/dist/cli/commands/sync-docs/context-syncer.d.ts.map +1 -1
  104. package/dist/cli/commands/sync-docs/context-syncer.js +3 -2
  105. package/dist/cli/commands/sync-docs/context-syncer.js.map +1 -1
  106. package/dist/cli/commands/sync-docs/index.d.ts +2 -1
  107. package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
  108. package/dist/cli/commands/sync-docs/index.js +68 -88
  109. package/dist/cli/commands/sync-docs/index.js.map +1 -1
  110. package/dist/cli/commands/sync-docs/project-reader.d.ts +6 -1
  111. package/dist/cli/commands/sync-docs/project-reader.d.ts.map +1 -1
  112. package/dist/cli/commands/sync-docs/project-reader.js +14 -16
  113. package/dist/cli/commands/sync-docs/project-reader.js.map +1 -1
  114. package/dist/cli/commands/update-user-model.d.ts +2 -1
  115. package/dist/cli/commands/update-user-model.d.ts.map +1 -1
  116. package/dist/cli/commands/update-user-model.js +31 -45
  117. package/dist/cli/commands/update-user-model.js.map +1 -1
  118. package/dist/cli/commands/validate.d.ts +4 -3
  119. package/dist/cli/commands/validate.d.ts.map +1 -1
  120. package/dist/cli/commands/validate.js +28 -26
  121. package/dist/cli/commands/validate.js.map +1 -1
  122. package/dist/cli/session-mining/transcript.d.ts +7 -1
  123. package/dist/cli/session-mining/transcript.d.ts.map +1 -1
  124. package/dist/cli/session-mining/transcript.js +21 -14
  125. package/dist/cli/session-mining/transcript.js.map +1 -1
  126. package/dist/completion-checkers/index.d.ts +3 -3
  127. package/dist/completion-checkers/index.js +3 -3
  128. package/dist/completion-checkers/phase-format.d.ts +1 -1
  129. package/dist/completion-checkers/phase-format.js +2 -2
  130. package/dist/completion-checkers/phase-format.js.map +1 -1
  131. package/dist/completion-checkers/tdd-chain.d.ts +1 -1
  132. package/dist/completion-checkers/tdd-chain.js +2 -2
  133. package/dist/completion-checkers/tdd-chain.js.map +1 -1
  134. package/dist/completion-checkers/types.d.ts +5 -5
  135. package/dist/completion-checkers/types.d.ts.map +1 -1
  136. package/dist/completion-checkers/types.js +2 -2
  137. package/dist/context/session-manager.d.ts.map +1 -1
  138. package/dist/context/session-manager.js +19 -32
  139. package/dist/context/session-manager.js.map +1 -1
  140. package/dist/core/constraints/agent-prompt-renderer.d.ts.map +1 -1
  141. package/dist/core/constraints/agent-prompt-renderer.js +1 -4
  142. package/dist/core/constraints/agent-prompt-renderer.js.map +1 -1
  143. package/dist/core/constraints/capabilities-parser.d.ts +24 -7
  144. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  145. package/dist/core/constraints/capabilities-parser.js +32 -28
  146. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  147. package/dist/core/constraints/capabilities-reconcile.d.ts +67 -0
  148. package/dist/core/constraints/capabilities-reconcile.d.ts.map +1 -0
  149. package/dist/core/constraints/capabilities-reconcile.js +105 -0
  150. package/dist/core/constraints/capabilities-reconcile.js.map +1 -0
  151. package/dist/core/constraints/checker.d.ts +21 -43
  152. package/dist/core/constraints/checker.d.ts.map +1 -1
  153. package/dist/core/constraints/checker.js +64 -115
  154. package/dist/core/constraints/checker.js.map +1 -1
  155. package/dist/core/constraints/checkers/capability-sync.d.ts +4 -3
  156. package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
  157. package/dist/core/constraints/checkers/capability-sync.js +29 -69
  158. package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
  159. package/dist/core/constraints/checkers/context-doc-sync.d.ts +4 -2
  160. package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
  161. package/dist/core/constraints/checkers/context-doc-sync.js +13 -22
  162. package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
  163. package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
  164. package/dist/core/constraints/checkers/docs-freshness.js +37 -63
  165. package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
  166. package/dist/core/constraints/checkers/governance-presence.d.ts +2 -2
  167. package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
  168. package/dist/core/constraints/checkers/governance-presence.js +7 -28
  169. package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
  170. package/dist/core/constraints/checkers/no-test-simplification.d.ts.map +1 -1
  171. package/dist/core/constraints/checkers/no-test-simplification.js +4 -3
  172. package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
  173. package/dist/core/constraints/checkers/types.d.ts +2 -2
  174. package/dist/core/constraints/context-builder.d.ts +15 -14
  175. package/dist/core/constraints/context-builder.d.ts.map +1 -1
  176. package/dist/core/constraints/context-builder.js +32 -96
  177. package/dist/core/constraints/context-builder.js.map +1 -1
  178. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  179. package/dist/core/constraints/doc-freshness/runner.js +2 -1
  180. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  181. package/dist/core/constraints/git-evidence.d.ts +60 -0
  182. package/dist/core/constraints/git-evidence.d.ts.map +1 -0
  183. package/dist/core/constraints/git-evidence.js +88 -0
  184. package/dist/core/constraints/git-evidence.js.map +1 -0
  185. package/dist/core/constraints/injection-drift.d.ts +3 -20
  186. package/dist/core/constraints/injection-drift.d.ts.map +1 -1
  187. package/dist/core/constraints/injection-drift.js +10 -57
  188. package/dist/core/constraints/injection-drift.js.map +1 -1
  189. package/dist/core/constraints/injection-writer.d.ts +113 -0
  190. package/dist/core/constraints/injection-writer.d.ts.map +1 -0
  191. package/dist/core/constraints/injection-writer.js +228 -0
  192. package/dist/core/constraints/injection-writer.js.map +1 -0
  193. package/dist/core/constraints/usage-report.d.ts +22 -1
  194. package/dist/core/constraints/usage-report.d.ts.map +1 -1
  195. package/dist/core/constraints/usage-report.js +27 -20
  196. package/dist/core/constraints/usage-report.js.map +1 -1
  197. package/dist/core/index.d.ts +2 -2
  198. package/dist/core/index.d.ts.map +1 -1
  199. package/dist/core/index.js +4 -2
  200. package/dist/core/index.js.map +1 -1
  201. package/dist/core/project-config-loader.d.ts +32 -1
  202. package/dist/core/project-config-loader.d.ts.map +1 -1
  203. package/dist/core/project-config-loader.js +39 -9
  204. package/dist/core/project-config-loader.js.map +1 -1
  205. package/dist/core/validators/passes-gate.d.ts +15 -64
  206. package/dist/core/validators/passes-gate.d.ts.map +1 -1
  207. package/dist/core/validators/passes-gate.js +84 -222
  208. package/dist/core/validators/passes-gate.js.map +1 -1
  209. package/dist/core/validators/test-output.d.ts +58 -0
  210. package/dist/core/validators/test-output.d.ts.map +1 -0
  211. package/dist/core/validators/test-output.js +109 -0
  212. package/dist/core/validators/test-output.js.map +1 -0
  213. package/dist/failure/recorder.d.ts +7 -1
  214. package/dist/failure/recorder.d.ts.map +1 -1
  215. package/dist/failure/recorder.js +13 -9
  216. package/dist/failure/recorder.js.map +1 -1
  217. package/dist/gates/acceptance.d.ts +1 -48
  218. package/dist/gates/acceptance.d.ts.map +1 -1
  219. package/dist/gates/acceptance.js +6 -38
  220. package/dist/gates/acceptance.js.map +1 -1
  221. package/dist/gates/checker-gate.d.ts.map +1 -1
  222. package/dist/gates/checker-gate.js +6 -11
  223. package/dist/gates/checker-gate.js.map +1 -1
  224. package/dist/gates/command.d.ts.map +1 -1
  225. package/dist/gates/command.js +10 -22
  226. package/dist/gates/command.js.map +1 -1
  227. package/dist/gates/contract.d.ts.map +1 -1
  228. package/dist/gates/contract.js +22 -61
  229. package/dist/gates/contract.js.map +1 -1
  230. package/dist/gates/performance.d.ts.map +1 -1
  231. package/dist/gates/performance.js +13 -31
  232. package/dist/gates/performance.js.map +1 -1
  233. package/dist/gates/review.d.ts.map +1 -1
  234. package/dist/gates/review.js +21 -53
  235. package/dist/gates/review.js.map +1 -1
  236. package/dist/gates/security.d.ts.map +1 -1
  237. package/dist/gates/security.js +14 -32
  238. package/dist/gates/security.js.map +1 -1
  239. package/dist/gates/types.d.ts +28 -5
  240. package/dist/gates/types.d.ts.map +1 -1
  241. package/dist/gates/types.js +38 -1
  242. package/dist/gates/types.js.map +1 -1
  243. package/dist/hooks/bootstrap.d.ts +2 -0
  244. package/dist/hooks/bootstrap.d.ts.map +1 -1
  245. package/dist/hooks/bootstrap.js +5 -2
  246. package/dist/hooks/bootstrap.js.map +1 -1
  247. package/dist/index.d.ts +4 -3
  248. package/dist/index.d.ts.map +1 -1
  249. package/dist/index.js +13 -4
  250. package/dist/index.js.map +1 -1
  251. package/dist/knowledge/audit.d.ts +7 -2
  252. package/dist/knowledge/audit.d.ts.map +1 -1
  253. package/dist/knowledge/audit.js +59 -79
  254. package/dist/knowledge/audit.js.map +1 -1
  255. package/dist/knowledge/flywheel-metrics.d.ts +39 -0
  256. package/dist/knowledge/flywheel-metrics.d.ts.map +1 -0
  257. package/dist/knowledge/flywheel-metrics.js +47 -0
  258. package/dist/knowledge/flywheel-metrics.js.map +1 -0
  259. package/dist/knowledge/import.d.ts.map +1 -1
  260. package/dist/knowledge/import.js +4 -14
  261. package/dist/knowledge/import.js.map +1 -1
  262. package/dist/knowledge/index-generator.d.ts.map +1 -1
  263. package/dist/knowledge/index-generator.js +18 -15
  264. package/dist/knowledge/index-generator.js.map +1 -1
  265. package/dist/knowledge/ingest.d.ts.map +1 -1
  266. package/dist/knowledge/ingest.js +9 -4
  267. package/dist/knowledge/ingest.js.map +1 -1
  268. package/dist/knowledge/migration.d.ts.map +1 -1
  269. package/dist/knowledge/migration.js +11 -8
  270. package/dist/knowledge/migration.js.map +1 -1
  271. package/dist/knowledge/reference-tracker.d.ts.map +1 -1
  272. package/dist/knowledge/reference-tracker.js +12 -9
  273. package/dist/knowledge/reference-tracker.js.map +1 -1
  274. package/dist/knowledge/store.d.ts +15 -0
  275. package/dist/knowledge/store.d.ts.map +1 -1
  276. package/dist/knowledge/store.js +59 -10
  277. package/dist/knowledge/store.js.map +1 -1
  278. package/dist/monitoring/context-tracker.d.ts.map +1 -1
  279. package/dist/monitoring/context-tracker.js +11 -20
  280. package/dist/monitoring/context-tracker.js.map +1 -1
  281. package/dist/monitoring/index.d.ts +1 -1
  282. package/dist/monitoring/index.d.ts.map +1 -1
  283. package/dist/monitoring/index.js +1 -2
  284. package/dist/monitoring/index.js.map +1 -1
  285. package/dist/monitoring/trace-analyzer.d.ts +15 -0
  286. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  287. package/dist/monitoring/trace-analyzer.js +19 -2
  288. package/dist/monitoring/trace-analyzer.js.map +1 -1
  289. package/dist/monitoring/traces.d.ts +21 -2
  290. package/dist/monitoring/traces.d.ts.map +1 -1
  291. package/dist/monitoring/traces.js +38 -29
  292. package/dist/monitoring/traces.js.map +1 -1
  293. package/dist/release/index.d.ts +3 -0
  294. package/dist/release/index.d.ts.map +1 -0
  295. package/dist/release/index.js +7 -0
  296. package/dist/release/index.js.map +1 -0
  297. package/dist/release/integrity.d.ts +65 -0
  298. package/dist/release/integrity.d.ts.map +1 -0
  299. package/dist/release/integrity.js +151 -0
  300. package/dist/release/integrity.js.map +1 -0
  301. package/dist/sdd/index-generator.js +10 -11
  302. package/dist/sdd/index-generator.js.map +1 -1
  303. package/dist/test-setup/project-fixture.d.ts +48 -0
  304. package/dist/test-setup/project-fixture.d.ts.map +1 -0
  305. package/dist/test-setup/project-fixture.js +106 -0
  306. package/dist/test-setup/project-fixture.js.map +1 -0
  307. package/dist/types/constraint.d.ts +0 -18
  308. package/dist/types/constraint.d.ts.map +1 -1
  309. package/dist/types/constraint.js.map +1 -1
  310. package/dist/types/failure.d.ts +7 -0
  311. package/dist/types/failure.d.ts.map +1 -1
  312. package/dist/types/failure.js +8 -1
  313. package/dist/types/failure.js.map +1 -1
  314. package/dist/types/passes-gate.d.ts +10 -25
  315. package/dist/types/passes-gate.d.ts.map +1 -1
  316. package/dist/types/project-config.d.ts +1 -1
  317. package/dist/types/session.d.ts +6 -5
  318. package/dist/types/session.d.ts.map +1 -1
  319. package/dist/utils/exec.d.ts +10 -0
  320. package/dist/utils/exec.d.ts.map +1 -1
  321. package/dist/utils/exec.js +12 -0
  322. package/dist/utils/exec.js.map +1 -1
  323. package/dist/utils/file-walk.d.ts +9 -1
  324. package/dist/utils/file-walk.d.ts.map +1 -1
  325. package/dist/utils/file-walk.js +10 -1
  326. package/dist/utils/file-walk.js.map +1 -1
  327. package/dist/utils/frontmatter.d.ts +73 -0
  328. package/dist/utils/frontmatter.d.ts.map +1 -0
  329. package/dist/utils/frontmatter.js +141 -0
  330. package/dist/utils/frontmatter.js.map +1 -0
  331. package/dist/utils/jsonl.d.ts +92 -0
  332. package/dist/utils/jsonl.d.ts.map +1 -0
  333. package/dist/utils/jsonl.js +166 -0
  334. package/dist/utils/jsonl.js.map +1 -0
  335. package/dist/utils/package-version.d.ts +16 -0
  336. package/dist/utils/package-version.d.ts.map +1 -0
  337. package/dist/utils/package-version.js +63 -0
  338. package/dist/utils/package-version.js.map +1 -0
  339. package/package.json +1 -1
  340. package/src/CONTEXT.md +7 -2
  341. package/src/__tests__/acceptance-gate.test.ts +3 -1
  342. package/src/__tests__/checker-extra.test.ts +7 -1218
  343. package/src/__tests__/context-files-resolution.test.ts +116 -0
  344. package/src/__tests__/effective-constraints.test.ts +10 -13
  345. package/src/__tests__/exec.test.ts +35 -1
  346. package/src/__tests__/failure-recorder.test.ts +9 -0
  347. package/src/__tests__/layering.test.ts +152 -0
  348. package/src/__tests__/passes-gate-extra.test.ts +0 -145
  349. package/src/__tests__/passes-gate.test.ts +142 -106
  350. package/src/__tests__/public-exports.test.ts +3 -0
  351. package/src/__tests__/skip-semantics.test.ts +5 -9
  352. package/src/cli/__tests__/command-contract.test.ts +170 -0
  353. package/src/cli/command-contract.ts +108 -0
  354. package/src/cli/commands/CONTEXT.md +22 -7
  355. package/src/cli/commands/__tests__/acceptance.test.ts +40 -33
  356. package/src/cli/commands/__tests__/analyze-sessions.test.ts +35 -29
  357. package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +101 -0
  358. package/src/cli/commands/__tests__/check-drift.test.ts +27 -34
  359. package/src/cli/commands/__tests__/check-skip-output.test.ts +21 -24
  360. package/src/cli/commands/__tests__/check.test.ts +378 -382
  361. package/src/cli/commands/__tests__/command.test.ts +85 -70
  362. package/src/cli/commands/__tests__/constraints-report.test.ts +100 -71
  363. package/src/cli/commands/__tests__/constraints-retire.test.ts +152 -95
  364. package/src/cli/commands/__tests__/contract.test.ts +27 -34
  365. package/src/cli/commands/__tests__/failure.test.ts +31 -34
  366. package/src/cli/commands/__tests__/init-injection.test.ts +58 -28
  367. package/src/cli/commands/__tests__/init.test.ts +77 -48
  368. package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +108 -0
  369. package/src/cli/commands/__tests__/knowledge.test.ts +96 -38
  370. package/src/cli/commands/__tests__/passes-gate.test.ts +43 -45
  371. package/src/cli/commands/__tests__/performance.test.ts +22 -23
  372. package/src/cli/commands/__tests__/posteval-plan.test.ts +39 -38
  373. package/src/cli/commands/__tests__/project-path-anchoring.test.ts +157 -0
  374. package/src/cli/commands/__tests__/project-path-convention.test.ts +169 -0
  375. package/src/cli/commands/__tests__/registry.test.ts +69 -35
  376. package/src/cli/commands/__tests__/release.test.ts +224 -74
  377. package/src/cli/commands/__tests__/report-extra.test.ts +16 -16
  378. package/src/cli/commands/__tests__/report.test.ts +25 -25
  379. package/src/cli/commands/__tests__/review.test.ts +33 -31
  380. package/src/cli/commands/__tests__/security.test.ts +23 -27
  381. package/src/cli/commands/__tests__/spec-baseline-check.test.ts +20 -20
  382. package/src/cli/commands/__tests__/spec.test.ts +41 -29
  383. package/src/cli/commands/__tests__/status-extra.test.ts +18 -18
  384. package/src/cli/commands/__tests__/status-skipped-lines.test.ts +72 -0
  385. package/src/cli/commands/__tests__/status.test.ts +34 -34
  386. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +108 -86
  387. package/src/cli/commands/__tests__/sync-docs.test.ts +72 -74
  388. package/src/cli/commands/__tests__/update-user-model.test.ts +62 -33
  389. package/src/cli/commands/__tests__/validate.test.ts +27 -29
  390. package/src/cli/commands/acceptance.ts +35 -24
  391. package/src/cli/commands/analyze-sessions.ts +25 -24
  392. package/src/cli/commands/check.ts +88 -65
  393. package/src/cli/commands/command.ts +30 -17
  394. package/src/cli/commands/constraints-report.ts +37 -34
  395. package/src/cli/commands/constraints-retire.ts +58 -55
  396. package/src/cli/commands/constraints.ts +10 -27
  397. package/src/cli/commands/contract.ts +51 -42
  398. package/src/cli/commands/definitions.ts +37 -223
  399. package/src/cli/commands/failure.ts +35 -27
  400. package/src/cli/commands/init.ts +166 -220
  401. package/src/cli/commands/knowledge.ts +250 -203
  402. package/src/cli/commands/passes-gate.ts +58 -68
  403. package/src/cli/commands/performance.ts +23 -18
  404. package/src/cli/commands/posteval-plan.ts +29 -15
  405. package/src/cli/commands/release.ts +75 -78
  406. package/src/cli/commands/report.ts +9 -5
  407. package/src/cli/commands/review.ts +39 -29
  408. package/src/cli/commands/sdd.ts +7 -5
  409. package/src/cli/commands/security.ts +35 -26
  410. package/src/cli/commands/spec-baseline-check.ts +16 -11
  411. package/src/cli/commands/spec.ts +66 -53
  412. package/src/cli/commands/status.ts +59 -53
  413. package/src/cli/commands/sync-docs/agents-syncer.ts +31 -15
  414. package/src/cli/commands/sync-docs/context-syncer.ts +3 -2
  415. package/src/cli/commands/sync-docs/index.ts +73 -100
  416. package/src/cli/commands/sync-docs/project-reader.ts +17 -18
  417. package/src/cli/commands/update-user-model.ts +33 -45
  418. package/src/cli/commands/validate.ts +31 -25
  419. package/src/cli/session-mining/__tests__/transcript.test.ts +118 -0
  420. package/src/cli/session-mining/transcript.ts +26 -8
  421. package/src/completion-checkers/CONTEXT.md +1 -1
  422. package/src/completion-checkers/__tests__/contract-presence.test.ts +1 -1
  423. package/src/completion-checkers/__tests__/phase-format.test.ts +1 -1
  424. package/src/completion-checkers/__tests__/tdd-chain.test.ts +2 -2
  425. package/src/completion-checkers/index.ts +3 -3
  426. package/src/completion-checkers/phase-format.ts +2 -2
  427. package/src/completion-checkers/tdd-chain.ts +2 -2
  428. package/src/completion-checkers/types.ts +5 -5
  429. package/src/context/session-manager.ts +19 -33
  430. package/src/core/CONTEXT.md +9 -6
  431. package/src/core/constraints/__tests__/capabilities-reconcile.test.ts +200 -0
  432. package/src/core/constraints/__tests__/capability-counts.test.ts +94 -0
  433. package/src/core/constraints/__tests__/git-evidence.test.ts +208 -0
  434. package/src/core/constraints/__tests__/injection-drift.test.ts +3 -10
  435. package/src/core/constraints/__tests__/injection-writer.test.ts +231 -0
  436. package/src/core/constraints/__tests__/trace-injection.test.ts +71 -0
  437. package/src/core/constraints/__tests__/usage-report.test.ts +112 -0
  438. package/src/core/constraints/agent-prompt-renderer.ts +2 -6
  439. package/src/core/constraints/capabilities-parser.ts +53 -35
  440. package/src/core/constraints/capabilities-reconcile.ts +166 -0
  441. package/src/core/constraints/checker.ts +75 -130
  442. package/src/core/constraints/checkers/__tests__/capability-sync.test.ts +214 -0
  443. package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +77 -0
  444. package/src/core/constraints/checkers/__tests__/docs-freshness.test.ts +117 -0
  445. package/src/{__tests__ → core/constraints/checkers/__tests__}/governance-presence.test.ts +83 -9
  446. package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +50 -0
  447. package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +76 -0
  448. package/src/{__tests__ → core/constraints/checkers/__tests__}/no-hardcoded-credentials.test.ts +4 -3
  449. package/src/core/constraints/checkers/__tests__/no-test-simplification.test.ts +58 -0
  450. package/src/core/constraints/checkers/__tests__/three-state-consistency.test.ts +145 -0
  451. package/src/core/constraints/checkers/capability-sync.ts +29 -88
  452. package/src/core/constraints/checkers/context-doc-sync.ts +15 -25
  453. package/src/core/constraints/checkers/docs-freshness.ts +40 -69
  454. package/src/core/constraints/checkers/governance-presence.ts +7 -25
  455. package/src/core/constraints/checkers/no-test-simplification.ts +4 -3
  456. package/src/core/constraints/checkers/types.ts +2 -2
  457. package/src/core/constraints/context-builder.ts +39 -91
  458. package/src/core/constraints/doc-freshness/runner.ts +2 -1
  459. package/src/core/constraints/git-evidence.ts +120 -0
  460. package/src/core/constraints/injection-drift.ts +13 -60
  461. package/src/core/constraints/injection-writer.ts +212 -0
  462. package/src/core/constraints/usage-report.ts +36 -17
  463. package/src/core/index.ts +4 -2
  464. package/src/core/project-config-loader.ts +49 -9
  465. package/src/core/validators/__tests__/passes-gate.test.ts +2 -56
  466. package/src/core/validators/__tests__/test-output.test.ts +372 -0
  467. package/src/core/validators/passes-gate.ts +86 -248
  468. package/src/core/validators/test-output.ts +128 -0
  469. package/src/failure/__tests__/recorder.test.ts +168 -0
  470. package/src/failure/recorder.ts +13 -11
  471. package/src/gates/CONTEXT.md +6 -3
  472. package/src/gates/__tests__/acceptance-types-single-source.test.ts +68 -0
  473. package/src/gates/__tests__/acceptance.test.ts +4 -86
  474. package/src/gates/__tests__/effective-gates.test.ts +3 -8
  475. package/src/gates/__tests__/test-verdict-consistency.test.ts +140 -0
  476. package/src/gates/acceptance.ts +19 -84
  477. package/src/gates/checker-gate.ts +8 -7
  478. package/src/gates/command.ts +10 -16
  479. package/src/gates/contract.ts +22 -61
  480. package/src/gates/performance.ts +10 -22
  481. package/src/gates/review.ts +21 -41
  482. package/src/gates/security.ts +10 -22
  483. package/src/gates/types.ts +70 -5
  484. package/src/hooks/CONTEXT.md +1 -1
  485. package/src/hooks/bootstrap.ts +5 -2
  486. package/src/index.ts +11 -6
  487. package/src/knowledge/CONTEXT.md +8 -3
  488. package/src/knowledge/__tests__/audit-labels.test.ts +44 -0
  489. package/src/knowledge/__tests__/flywheel-metrics.test.ts +135 -0
  490. package/src/knowledge/__tests__/import.test.ts +44 -0
  491. package/src/knowledge/__tests__/index-generator.test.ts +55 -2
  492. package/src/knowledge/__tests__/ingest.test.ts +85 -0
  493. package/src/knowledge/__tests__/migration.test.ts +47 -0
  494. package/src/knowledge/__tests__/reference-tracker.test.ts +16 -0
  495. package/src/knowledge/__tests__/store.test.ts +194 -0
  496. package/src/knowledge/audit.ts +67 -71
  497. package/src/knowledge/flywheel-metrics.ts +71 -0
  498. package/src/knowledge/import.ts +4 -18
  499. package/src/knowledge/index-generator.ts +20 -15
  500. package/src/knowledge/ingest.ts +9 -4
  501. package/src/knowledge/migration.ts +11 -8
  502. package/src/knowledge/reference-tracker.ts +12 -8
  503. package/src/knowledge/store.ts +64 -11
  504. package/src/monitoring/CONTEXT.md +8 -3
  505. package/src/monitoring/__tests__/skipped-lines-report.test.ts +178 -0
  506. package/src/monitoring/__tests__/traces.test.ts +57 -0
  507. package/src/monitoring/context-tracker.ts +11 -23
  508. package/src/monitoring/index.ts +1 -1
  509. package/src/monitoring/trace-analyzer.ts +21 -2
  510. package/src/monitoring/traces.ts +39 -36
  511. package/src/release/CONTEXT.md +24 -0
  512. package/src/release/__tests__/barrel.test.ts +60 -0
  513. package/src/release/__tests__/integrity.test.ts +157 -0
  514. package/src/release/index.ts +2 -0
  515. package/src/release/integrity.ts +141 -0
  516. package/src/sdd/__tests__/index-generator.test.ts +71 -0
  517. package/src/sdd/index-generator.ts +9 -9
  518. package/src/test-setup/__tests__/project-fixture.test.ts +178 -0
  519. package/src/test-setup/project-fixture.ts +87 -0
  520. package/src/tools/CONTEXT.md +6 -0
  521. package/src/tools/__tests__/registry.test.ts +221 -0
  522. package/src/types/__tests__/types.test.ts +4 -5
  523. package/src/types/constraint.ts +0 -25
  524. package/src/types/failure.ts +8 -0
  525. package/src/types/passes-gate.ts +10 -30
  526. package/src/types/project-config.ts +1 -1
  527. package/src/types/session.ts +5 -5
  528. package/src/utils/__tests__/frontmatter.test.ts +206 -0
  529. package/src/utils/__tests__/jsonl-skip-disposition.test.ts +175 -0
  530. package/src/utils/__tests__/jsonl.test.ts +187 -0
  531. package/src/utils/__tests__/package-version.test.ts +97 -0
  532. package/src/utils/exec.ts +15 -0
  533. package/src/utils/file-walk.ts +11 -2
  534. package/src/utils/frontmatter.ts +121 -0
  535. package/src/utils/jsonl.ts +167 -0
  536. package/src/utils/package-version.ts +27 -0
  537. package/dist/cli/commands/doc-freshness-check.d.ts +0 -53
  538. package/dist/cli/commands/doc-freshness-check.d.ts.map +0 -1
  539. package/dist/cli/commands/doc-freshness-check.js +0 -352
  540. package/dist/cli/commands/doc-freshness-check.js.map +0 -1
  541. package/dist/types/index.d.ts +0 -14
  542. package/dist/types/index.d.ts.map +0 -1
  543. package/dist/types/index.js +0 -39
  544. package/dist/types/index.js.map +0 -1
  545. package/src/__tests__/long-running.test.ts +0 -17
  546. package/src/__tests__/passes-gate-extension.test.ts +0 -151
  547. package/src/cli/commands/__tests__/doc-freshness-check.test.ts +0 -168
  548. package/src/cli/commands/doc-freshness-check.ts +0 -386
  549. package/src/spec/CONTEXT.md +0 -11
  550. package/src/types/index.ts +0 -47
@@ -3,9 +3,12 @@
3
3
  *
4
4
  * 确保 task.passes 字段只能通过测试结果修改
5
5
  * 禁止 Agent 自评通过
6
+ *
7
+ * 「过了没」的判定依据不在本文件:唯一入口是 test-output.ts 的 judgeTestRun(ADR-0014)
6
8
  */
7
9
 
8
10
  import { execAsync, delay } from '../../utils/exec';
11
+ import { judgeTestRun } from './test-output';
9
12
  import * as fs from 'fs/promises';
10
13
  import * as path from 'path';
11
14
  import type {
@@ -13,8 +16,6 @@ import type {
13
16
  PassesGateResult,
14
17
  TaskTestResult,
15
18
  DynamicTask,
16
- PassesGateExtension,
17
- ExtensionTestResult,
18
19
  TestResult,
19
20
  PassesGateCheckResult,
20
21
  PassesGateViolation,
@@ -47,6 +48,51 @@ const PROTECTED_TEST_PATTERNS = [
47
48
  '**/__tests__/**',
48
49
  ];
49
50
 
51
+ /**
52
+ * 检测项目的测试命令(唯一正本,CLI 与库调用共消费,架构评审 A2)
53
+ *
54
+ * 探测顺序:test:ci → test(排除 npm 默认 echo 占位脚本)→ test:e2e → test:coverage
55
+ * → Python(pyproject.toml 或 pytest.ini 任一)→ go.mod。
56
+ * 探不到返回 undefined,兜底策略归调用方(CLI 映射为 skip;PassesGate 内部 fail-closed)。
57
+ */
58
+ export async function detectTestCommand(projectPath: string): Promise<string | undefined> {
59
+ try {
60
+ const content = await fs.readFile(path.join(projectPath, 'package.json'), 'utf-8');
61
+ const pkg = JSON.parse(content);
62
+
63
+ if (pkg.scripts?.['test:ci']) {
64
+ return 'npm run test:ci';
65
+ }
66
+ if (pkg.scripts?.test && pkg.scripts.test !== 'echo "Error: no test specified"') {
67
+ return 'npm test';
68
+ }
69
+ if (pkg.scripts?.['test:e2e']) {
70
+ return 'npm run test:e2e';
71
+ }
72
+ if (pkg.scripts?.['test:coverage']) {
73
+ return 'npm run test:coverage';
74
+ }
75
+ } catch {
76
+ // 没有 package.json,继续探测其他项目类型
77
+ }
78
+
79
+ // Python 项目:pyproject.toml 或 pytest.ini 任一命中
80
+ for (const marker of ['pyproject.toml', 'pytest.ini']) {
81
+ try {
82
+ await fs.access(path.join(projectPath, marker));
83
+ return 'pytest';
84
+ } catch {}
85
+ }
86
+
87
+ // Go 项目
88
+ try {
89
+ await fs.access(path.join(projectPath, 'go.mod'));
90
+ return 'go test ./...';
91
+ } catch {}
92
+
93
+ return undefined;
94
+ }
95
+
50
96
  /**
51
97
  * PassesGate 类
52
98
  */
@@ -219,8 +265,11 @@ export class PassesGate {
219
265
 
220
266
  /**
221
267
  * 运行测试(CLI 使用)
268
+ *
269
+ * `workDir` 必传:执行位置与证据落盘根都从这里来,不在本层取 cwd
270
+ * (projectPath 只在 CLI 入口兜底一次,见 src/cli/commands/CONTEXT.md)
222
271
  */
223
- async runTests(): Promise<{
272
+ async runTests(workDir: string): Promise<{
224
273
  passed: boolean;
225
274
  passedTests: number;
226
275
  failedTests: number;
@@ -230,10 +279,9 @@ export class PassesGate {
230
279
  message?: string;
231
280
  }> {
232
281
  const startTime = Date.now();
233
- const workDir = process.cwd();
234
-
282
+
235
283
  try {
236
- const testCommand = this.config.testCommand || await this.detectTestCommand(workDir);
284
+ const testCommand = this.config.testCommand || await detectTestCommand(workDir);
237
285
 
238
286
  if (!testCommand) {
239
287
  return {
@@ -246,7 +294,7 @@ export class PassesGate {
246
294
  };
247
295
  }
248
296
 
249
- const result = await this.runTest(workDir);
297
+ const result = await this.runTest(workDir, undefined, testCommand);
250
298
  const duration = Date.now() - startTime;
251
299
 
252
300
  // 解析测试数量
@@ -281,155 +329,49 @@ export class PassesGate {
281
329
  /**
282
330
  * 运行测试
283
331
  */
284
- private async runTest(workDir: string, _task?: DynamicTask): Promise<TaskTestResult> {
285
- const testCommand = await this.detectTestCommand(workDir);
332
+ private async runTest(workDir: string, _task?: DynamicTask, command?: string): Promise<TaskTestResult> {
333
+ const testCommand = command || this.config.testCommand || await detectTestCommand(workDir);
286
334
  const timestamp = new Date();
287
335
 
288
- try {
289
- const { stdout } = await execAsync(testCommand, {
290
- cwd: workDir,
291
- maxBuffer: 10 * 1024 * 1024, // 10MB buffer
292
- });
293
-
294
- const passed = true;
295
- const coverage = this.extractCoverage(stdout);
296
- const failures: string[] = [];
297
-
336
+ // 探测失败 fail-closed:与 runTests 的「未检测到测试命令」分支同形状
337
+ if (!testCommand) {
298
338
  return {
299
- passed,
300
- command: testCommand,
301
- output: stdout,
302
- failures,
303
- coverage,
304
- timestamp,
305
- evidence: await this.generateEvidence(workDir, stdout),
306
- };
307
- } catch (error: any) {
308
- const output = error.stdout || '';
309
- const stderrOutput = error.stderr || '';
310
- const combinedOutput = output + '\n' + stderrOutput;
311
-
312
- const failures = this.extractFailures(combinedOutput);
313
- const passed = this.config.allowPartialPass && failures.length === 0;
314
-
315
- return {
316
- passed,
317
- command: testCommand,
318
- output: combinedOutput,
319
- failures,
320
- coverage: this.extractCoverage(output),
339
+ passed: false,
340
+ command: '',
341
+ output: '未检测到测试命令',
321
342
  timestamp,
322
- evidence: await this.generateEvidence(workDir, combinedOutput),
323
343
  };
324
344
  }
325
- }
326
-
327
- /**
328
- * 检测测试命令
329
- */
330
- private async detectTestCommand(workDir: string): Promise<string> {
331
- if (this.config.testCommand) {
332
- return this.config.testCommand;
333
- }
334
-
335
- // 尝试读取 package.json
336
- try {
337
- const packageJsonPath = path.join(workDir, 'package.json');
338
- const content = await fs.readFile(packageJsonPath, 'utf-8');
339
- const packageJson = JSON.parse(content);
340
345
 
341
- // 优先使用 e2e 测试
342
- if (packageJson.scripts?.['test:e2e']) {
343
- return 'npm run test:e2e';
344
- }
345
- if (packageJson.scripts?.['test:coverage']) {
346
- return 'npm run test:coverage';
347
- }
348
- if (packageJson.scripts?.test) {
349
- return 'npm test';
350
- }
351
- } catch {
352
- // package.json 不存在,尝试其他检测
353
- }
354
-
355
- // 尝试 Python 项目
356
- try {
357
- const pyprojectPath = path.join(workDir, 'pyproject.toml');
358
- await fs.access(pyprojectPath);
359
- return 'pytest';
360
- } catch {
361
- // 不是 Python 项目
362
- }
346
+ let exitCode = 0;
347
+ let output = '';
363
348
 
364
- // 尝试 Go 项目
365
349
  try {
366
- const goModPath = path.join(workDir, 'go.mod');
367
- await fs.access(goModPath);
368
- return 'go test ./...';
369
- } catch {
370
- // 不是 Go 项目
371
- }
372
-
373
- // 默认命令
374
- return 'npm test';
375
- }
376
-
377
- /**
378
- * 从输出中提取覆盖率
379
- */
380
- private extractCoverage(output: string): number | undefined {
381
- // Jest 格式: All files | 80.5 | 70.2 | ...
382
- const jestMatch = output.match(/All files[|\s]+(\d+\.?\d*)/);
383
- if (jestMatch?.[1]) {
384
- return parseFloat(jestMatch[1]);
385
- }
386
-
387
- // Istanbul/nyc 格式: Statements : 80.5% ( 100/124 )
388
- const istanbulMatch = output.match(/Statements\s*:\s*(\d+\.?\d*)%/);
389
- if (istanbulMatch?.[1]) {
390
- return parseFloat(istanbulMatch[1]);
391
- }
392
-
393
- // pytest-cov 格式: TOTAL 1234 80%
394
- const pytestMatch = output.match(/TOTAL\s+\d+\s+(\d+)%/);
395
- if (pytestMatch?.[1]) {
396
- return parseInt(pytestMatch[1], 10);
397
- }
398
-
399
- return undefined;
400
- }
401
-
402
- /**
403
- * 从输出中提取失败信息
404
- */
405
- private extractFailures(output: string): string[] {
406
- const failures: string[] = [];
407
-
408
- // Jest 格式
409
- const jestMatches = output.matchAll(/✕\s+(.+?)\s+\(/g);
410
- for (const match of jestMatches) {
411
- if (match[1]) failures.push(match[1]);
412
- }
413
-
414
- // Mocha 格式
415
- const mochaMatches = output.matchAll(/\d+\)\s+(.+?):/g);
416
- for (const match of mochaMatches) {
417
- if (match[1]) failures.push(match[1]);
418
- }
419
-
420
- // pytest 格式
421
- const pytestMatches = output.matchAll(/FAILED\s+(.+?)::/g);
422
- for (const match of pytestMatches) {
423
- if (match[1]) failures.push(match[1]);
350
+ const result = await execAsync(testCommand, {
351
+ cwd: workDir,
352
+ maxBuffer: 10 * 1024 * 1024, // 10MB buffer
353
+ });
354
+ output = result.stdout + result.stderr;
355
+ } catch (error: any) {
356
+ // 非零退出(超时 / buffer 溢出等执行失败也落这里):判定依据就是退出码,文本不参与
357
+ exitCode = typeof error.code === 'number' ? error.code : 1;
358
+ output = (error.stdout || '') + '\n' + (error.stderr || '');
424
359
  }
425
360
 
426
- // Go test 格式
427
- const goMatches = output.matchAll(/--- FAIL:\s+(.+?)\s+\(/g);
428
- for (const match of goMatches) {
429
- if (match[1]) failures.push(match[1]);
430
- }
361
+ const { passed, failures } = judgeTestRun({
362
+ exitCode,
363
+ output,
364
+ allowPartialPass: this.config.allowPartialPass,
365
+ });
431
366
 
432
- return failures;
367
+ return {
368
+ passed,
369
+ command: testCommand,
370
+ output,
371
+ failures,
372
+ timestamp,
373
+ evidence: await this.generateEvidence(workDir, output),
374
+ };
433
375
  }
434
376
 
435
377
  /**
@@ -499,110 +441,6 @@ export class PassesGate {
499
441
  return this.testResults.get(taskId);
500
442
  }
501
443
 
502
- // ========================================
503
- // 扩展点支持(Long-Running Agents)
504
- // ========================================
505
-
506
- /**
507
- * 注册的扩展列表
508
- */
509
- private extensions: Map<string, PassesGateExtension> = new Map();
510
-
511
- /**
512
- * 注册扩展
513
- *
514
- * 用于注册额外的测试类型(如 Puppeteer E2E)
515
- *
516
- * @param name 扩展名称
517
- * @param extension 扩展实现
518
- *
519
- * @example
520
- * ```typescript
521
- * const puppeteerExtension = {
522
- * name: 'puppeteer',
523
- * run: async (workDir, task) => {
524
- * // 运行 Puppeteer 测试
525
- * return { passed: true, command: 'puppeteer', ... };
526
- * }
527
- * };
528
- *
529
- * passesGate.registerExtension('puppeteer', puppeteerExtension);
530
- * ```
531
- */
532
- registerExtension(name: string, extension: PassesGateExtension): void {
533
- this.extensions.set(name, extension);
534
- }
535
-
536
- /**
537
- * 注销扩展
538
- */
539
- unregisterExtension(name: string): boolean {
540
- return this.extensions.delete(name);
541
- }
542
-
543
- /**
544
- * 获取所有已注册的扩展名称
545
- */
546
- getExtensionNames(): string[] {
547
- return Array.from(this.extensions.keys());
548
- }
549
-
550
- /**
551
- * 运行所有测试(包括扩展测试)
552
- *
553
- * @param workDir 工作目录
554
- * @param task 可选的任务信息
555
- * @returns 所有测试结果
556
- */
557
- async runAllTests(workDir: string, task?: DynamicTask): Promise<ExtensionTestResult[]> {
558
- const results: ExtensionTestResult[] = [];
559
-
560
- // 1. 运行单元测试(原有逻辑)
561
- const unitTestResult = await this.runTest(workDir, task);
562
- results.push(unitTestResult);
563
-
564
- // 2. 运行所有扩展测试
565
- for (const [name, extension] of this.extensions) {
566
- try {
567
- const extensionResult = await extension.run(workDir, task);
568
- results.push({
569
- ...extensionResult,
570
- type: name,
571
- });
572
- } catch (error: any) {
573
- results.push({
574
- passed: false,
575
- command: `${name}-extension`,
576
- error: error.message,
577
- type: name,
578
- timestamp: new Date(),
579
- });
580
- }
581
- }
582
-
583
- return results;
584
- }
585
-
586
- /**
587
- * 检查所有测试是否通过
588
- */
589
- async checkAllPasses(workDir: string, task?: DynamicTask): Promise<{
590
- passed: boolean;
591
- results: ExtensionTestResult[];
592
- failedTypes: string[];
593
- }> {
594
- const results = await this.runAllTests(workDir, task);
595
- const failedTypes = results
596
- .filter(r => !r.passed)
597
- .map(r => r.type || 'unit');
598
-
599
- return {
600
- passed: failedTypes.length === 0,
601
- results,
602
- failedTypes,
603
- };
604
- }
605
-
606
444
  }
607
445
 
608
446
  /**
@@ -0,0 +1,128 @@
1
+ /**
2
+ * 测试产物解读——从 runner 的 stdout 读出「过了没 / 哪些失败」(ADR-0012,判定口径 ADR-0014)
3
+ *
4
+ * 纯函数,字符串进、值出,不做 IO、不跑进程。此前解析器分别 private 关在
5
+ * core/validators/passes-gate.ts(extractCoverage / extractFailures)与
6
+ * gates/acceptance.ts(parseTestOutput)体内:两处只有 jest 一个家族共同识别,用的信号
7
+ * 还不同(`✕` 行 vs `Test Suites:` 汇总行),同一份输出能在一处看出失败、在另一处看不出。
8
+ *
9
+ * 本模块是包内模块,不进 src/index.ts 导出面(对齐 ADR-0009 capabilities-reconcile /
10
+ * ADR-0011 injection-writer 先例)。
11
+ *
12
+ * #93 裁决(ADR-0014):判定依据统一为「退出码为主 + 文本交叉否决」,收在 judgeTestRun
13
+ * 一处,两个门禁都只从它取结论——原 parseTestOutput(纯文本判定)删除。
14
+ *
15
+ * #94 裁决 B(ADR-0015):本模块不读覆盖率。extractCoverage 与其唯一产出
16
+ * (TaskTestResult.coverage)已删除——harness 核心判定不管覆盖率执法,判覆盖率的
17
+ * 两处(`harness passes-gate --coverage`、performance 门禁)各自读 json-summary。
18
+ *
19
+ * extractFailures 的正则、分支顺序、返回形状逐字迁移自 ADR-0012;仍保持原样的已知粗糙处:
20
+ * - 一次跑完收集所有失败(首个失败即定的编排)→ #93 范围外,另票
21
+ *
22
+ * PR #117 实发修正(2026-09-08):四个 runner 正则由「可跨行」收紧为行首锚定/行内匹配。
23
+ * 原 mocha 正则 /\d+\)\s+(.+?):/g 的 \s+ 跨行——绿色运行的 console 噪音里一行以
24
+ * 数字+")"结尾(stack 帧 checker.ts:213:23)、init 日志 (v1.3.0))、下一行含 ":",
25
+ * 就被误捕为失败用例,governance 门禁对全绿 npm test 输出误判 24 条失败(exit 0 遭
26
+ * judgeTestRun 文本否决)。jest ✕ / pytest FAILED / go --- FAIL: 的 \s+ 同理收紧。
27
+ */
28
+
29
+ /**
30
+ * 从输出中提取失败信息
31
+ */
32
+ export function extractFailures(output: string): string[] {
33
+ const failures: string[] = [];
34
+
35
+ // Jest 格式(✕ 与用例名同行;\s 不跨行,防把下一行日志/堆栈捕进来)
36
+ const jestMatches = output.matchAll(/✕[ \t]+([^\n]+?)[ \t]+\(/g);
37
+ for (const match of jestMatches) {
38
+ if (match[1]) failures.push(match[1]);
39
+ }
40
+
41
+ // Mocha 格式(行首锚定:「N) 标题:」必须是一行的开头形状;
42
+ // 旧写法不锚行首且 \s+ 跨行,绿输出的 console 噪音会被误判——见文件头 PR #117 段)
43
+ const mochaMatches = output.matchAll(/^[ \t]*\d+\)[ \t]+([^\n]+?):/gm);
44
+ for (const match of mochaMatches) {
45
+ if (match[1]) failures.push(match[1]);
46
+ }
47
+
48
+ // pytest 格式
49
+ const pytestMatches = output.matchAll(/FAILED[ \t]+([^\n]+?)::/g);
50
+ for (const match of pytestMatches) {
51
+ if (match[1]) failures.push(match[1]);
52
+ }
53
+
54
+ // Go test 格式
55
+ const goMatches = output.matchAll(/--- FAIL:[ \t]+([^\n]+?)[ \t]+\(/g);
56
+ for (const match of goMatches) {
57
+ if (match[1]) failures.push(match[1]);
58
+ }
59
+
60
+ return failures;
61
+ }
62
+
63
+ /**
64
+ * 判定结果:过了没,以及文本里读出的失败用例名
65
+ */
66
+ export interface TestRunVerdict {
67
+ passed: boolean;
68
+ /** 用例级失败名(判负时的可定位线索;退出码非零时也照常带出) */
69
+ failures: string[];
70
+ }
71
+
72
+ /**
73
+ * 行首锚定的结构化失败信号(ADR-0014)
74
+ *
75
+ * 只认 runner 自己打的汇总/套件行,不认子串:旧 `output.includes('FAIL')` 兜底会被通过的
76
+ * 用例名(`✓ FAIL 时应该重试`)或含 FAIL 的目录名反转成判负。
77
+ * - 套件行 `FAIL src/a.test.ts`(pytest 的 `FAILED x::`、go 的 `--- FAIL:` 行首不是 FAIL,
78
+ * 由 extractFailures 认)
79
+ * - 计数汇总行:jest `Test Suites: 2 failed, ...` / `Tests: 2 failed, ...`,
80
+ * playwright / mocha / pytest 的 ` 1 failed` / ` 1 failing` / `2 failed, 5 passed in 0.88s`。
81
+ * 计数为 0 是全过(`Test Suites: 0 failed, 2 total`),不构成信号
82
+ */
83
+ const SUITE_FAILURE_LINE = /^FAIL\b.*$/m;
84
+ const SUMMARY_FAILURE_COUNTS: RegExp[] = [
85
+ /^(?:Test Suites|Suites|Tests):\s+(\d+)\s+failed\b/m,
86
+ /^\s*(\d+)\s+(?:failed|failing)\b/m,
87
+ ];
88
+
89
+ function hasStructuredFailure(output: string): boolean {
90
+ if (SUITE_FAILURE_LINE.test(output)) return true;
91
+
92
+ return SUMMARY_FAILURE_COUNTS.some(pattern => {
93
+ const match = pattern.exec(output);
94
+ return match !== null && Number(match[1]) > 0;
95
+ });
96
+ }
97
+
98
+ /**
99
+ * 判一次测试跑动的结论:退出码为主 + 文本交叉否决(#93,ADR-0014)
100
+ *
101
+ * - 退出码非零 → 判负,文本救不回来
102
+ * - 退出码为零但文本有结构化失败信号 → 判负(runner 退出码撒谎的兜底)
103
+ * - 退出码为零且无失败信号 → 判过;「没跑出测试」不在此判定的否决范围内(另票)
104
+ *
105
+ * `allowPartialPass` 的落点是**文本否决**这一维(部分通过 = 允许个别用例失败但命令本身过了);
106
+ * 它不赦免非零退出码。
107
+ */
108
+ export function judgeTestRun(input: {
109
+ /** 测试命令的退出码(0 = runner 自己说过了) */
110
+ exitCode: number;
111
+ /** runner 的 stdout + stderr 合并文本 */
112
+ output: string;
113
+ /** 允许部分通过:文本失败信号不否决零退出码,缺省 false */
114
+ allowPartialPass?: boolean;
115
+ }): TestRunVerdict {
116
+ const failures = extractFailures(input.output);
117
+
118
+ if (input.exitCode !== 0) {
119
+ return { passed: false, failures };
120
+ }
121
+ if (input.allowPartialPass) {
122
+ return { passed: true, failures };
123
+ }
124
+
125
+ const vetoed = failures.length > 0 || hasStructuredFailure(input.output);
126
+
127
+ return { passed: !vetoed, failures };
128
+ }
@@ -0,0 +1,168 @@
1
+ /**
2
+ * FailureRecorder 坏行容错测试(harness#96)
3
+ *
4
+ * fixtures 走真实临时文件(mkdtempSync,统一清理见 src/test-setup):
5
+ * failures.log 夹一行坏 JSON 时,failure list|stats 不崩栈、
6
+ * stdout(含 --json)逐字节不变、stderr 出现一次含坏行条数的警告。
7
+ */
8
+
9
+ import * as fs from 'fs';
10
+ import { captureIO, type CapturingIO } from '../../cli/command-contract';
11
+ import * as os from 'os';
12
+ import * as path from 'path';
13
+ import { FailureRecorder } from '../recorder';
14
+ import { failureList, failureStats } from '../../cli/commands/failure';
15
+ import { ErrorType, FailureLevel, DEFAULT_FAILURE_LOG_FILE } from '../../types/failure';
16
+
17
+ const CORRUPT_LINE = '{"type":"TEST_FAILED","level":"L1","mess'; // 半写入截断
18
+
19
+ function makeRecorder(logFile: string): FailureRecorder {
20
+ return new FailureRecorder({ logFile });
21
+ }
22
+
23
+ function writeLogFile(dir: string, lines: string[]): string {
24
+ // CLI 侧经 getRecorder 读 <projectPath>/<DEFAULT_FAILURE_LOG_FILE>,fixture 必须落在同一真实路径
25
+ const logFile = path.join(dir, DEFAULT_FAILURE_LOG_FILE);
26
+ fs.mkdirSync(path.dirname(logFile), { recursive: true });
27
+ fs.writeFileSync(logFile, lines.join('\n') + '\n', 'utf-8');
28
+ return logFile;
29
+ }
30
+
31
+ function validRecord(message: string): string {
32
+ return JSON.stringify({
33
+ type: ErrorType.TEST_FAILED,
34
+ level: FailureLevel.L1,
35
+ message,
36
+ timestamp: 1700000000000,
37
+ });
38
+ }
39
+
40
+ function captureConsole(): { stdout: string[]; stderr: string[] } {
41
+ const captured = { stdout: [] as string[], stderr: [] as string[] };
42
+ jest.spyOn(console, 'log').mockImplementation((...args: unknown[]) => {
43
+ captured.stdout.push(args.map(String).join(' '));
44
+ });
45
+ jest.spyOn(console, 'error').mockImplementation((...args: unknown[]) => {
46
+ captured.stderr.push(args.map(String).join(' '));
47
+ });
48
+ return captured;
49
+ }
50
+
51
+ let io: CapturingIO;
52
+ beforeEach(() => {
53
+ io = captureIO();
54
+ });
55
+
56
+ describe('FailureRecorder 坏行容错(harness#96)', () => {
57
+ let dir: string;
58
+
59
+ beforeEach(() => {
60
+ dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-recorder-'));
61
+ });
62
+
63
+ afterEach(() => {
64
+ jest.restoreAllMocks();
65
+ fs.rmSync(dir, { recursive: true, force: true });
66
+ });
67
+
68
+ describe('getHistory()', () => {
69
+ it('坏行不抛:返回全部合法记录', async () => {
70
+ const logFile = writeLogFile(dir, [validRecord('a'), CORRUPT_LINE, validRecord('b')]);
71
+ const records = await makeRecorder(logFile).getHistory();
72
+ expect(records.map(r => r.message)).toEqual(['a', 'b']);
73
+ });
74
+
75
+ it('跳过坏行时 stderr 出现一次含条数的警告', async () => {
76
+ const logFile = writeLogFile(dir, [
77
+ validRecord('a'),
78
+ CORRUPT_LINE,
79
+ CORRUPT_LINE,
80
+ validRecord('b'),
81
+ ]);
82
+ const captured = captureConsole();
83
+ await makeRecorder(logFile).getHistory();
84
+ expect(captured.stderr).toHaveLength(1);
85
+ expect(captured.stderr[0]).toContain('2');
86
+ expect(captured.stdout).toEqual([]);
87
+ });
88
+
89
+ it('文件不存在落到空态,无警告', async () => {
90
+ const captured = captureConsole();
91
+ const records = await makeRecorder(path.join(dir, 'missing.log')).getHistory();
92
+ expect(records).toEqual([]);
93
+ expect(captured.stderr).toEqual([]);
94
+ });
95
+
96
+ it('全部行损坏落到空态', async () => {
97
+ const logFile = writeLogFile(dir, [CORRUPT_LINE, 'not-json']);
98
+ const captured = captureConsole();
99
+ const records = await makeRecorder(logFile).getHistory();
100
+ expect(records).toEqual([]);
101
+ expect(captured.stderr).toHaveLength(1);
102
+ });
103
+
104
+ it('limit 只取最近 N 条合法记录', async () => {
105
+ const logFile = writeLogFile(dir, [validRecord('a'), CORRUPT_LINE, validRecord('b'), validRecord('c')]);
106
+ const records = await makeRecorder(logFile).getHistory(2);
107
+ expect(records.map(r => r.message)).toEqual(['b', 'c']);
108
+ });
109
+ });
110
+
111
+ describe('getStats()', () => {
112
+ it('统计口径只计合法记录', async () => {
113
+ const logFile = writeLogFile(dir, [validRecord('a'), CORRUPT_LINE, validRecord('b')]);
114
+ const stats = await makeRecorder(logFile).getStats();
115
+ expect(stats.total).toBe(2);
116
+ });
117
+ });
118
+
119
+ describe('failure list 命令', () => {
120
+ it('坏行不崩、stdout 与无坏行时逐字节一致、stderr 警告一次', async () => {
121
+ writeLogFile(path.join(dir, 'clean'), [validRecord('a'), validRecord('b')]);
122
+ writeLogFile(path.join(dir, 'dirty'), [validRecord('a'), CORRUPT_LINE, validRecord('b')]);
123
+
124
+ // 列表正文走注入 io;损坏行警告由 recorder 自身 console.error 打印
125
+ const cleanIo = captureIO();
126
+ await failureList({ projectPath: path.join(dir, 'clean') }, cleanIo);
127
+
128
+ const dirtyCapture = captureConsole();
129
+ const dirtyIo = captureIO();
130
+ const dirtyResult = await failureList({ projectPath: path.join(dir, 'dirty') }, dirtyIo);
131
+ expect(dirtyResult).toEqual({ kind: 'ok' });
132
+
133
+ expect(dirtyIo.outText()).toBe(cleanIo.outText());
134
+ expect(dirtyIo.outText()).not.toBe('');
135
+ expect(dirtyCapture.stderr).toHaveLength(1);
136
+ expect(dirtyCapture.stderr[0]).toContain('1');
137
+ });
138
+
139
+ it('--json 输出不受 stderr 警告污染且不含坏行', async () => {
140
+ writeLogFile(dir, [validRecord('a'), CORRUPT_LINE]);
141
+ const captured = captureConsole();
142
+ await failureList({ projectPath: dir, json: true }, io);
143
+
144
+ expect(captured.stderr).toHaveLength(1);
145
+ const parsed = JSON.parse(io.outText());
146
+ expect(parsed.total).toBe(1);
147
+ expect(parsed.records[0].message).toBe('a');
148
+ });
149
+ });
150
+
151
+ describe('failure stats 命令', () => {
152
+ it('坏行不崩、stdout 与无坏行时逐字节一致', async () => {
153
+ writeLogFile(path.join(dir, 'clean'), [validRecord('a'), validRecord('b')]);
154
+ writeLogFile(path.join(dir, 'dirty'), [CORRUPT_LINE, validRecord('a'), validRecord('b')]);
155
+
156
+ const cleanIo = captureIO();
157
+ await failureStats({ projectPath: path.join(dir, 'clean') }, cleanIo);
158
+
159
+ const dirtyCapture = captureConsole();
160
+ const dirtyIo = captureIO();
161
+ await failureStats({ projectPath: path.join(dir, 'dirty') }, dirtyIo);
162
+
163
+ expect(dirtyIo.outText()).toBe(cleanIo.outText());
164
+ expect(dirtyIo.outText()).not.toBe('');
165
+ expect(dirtyCapture.stderr).toHaveLength(1);
166
+ });
167
+ });
168
+ });