@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
@@ -6,6 +6,7 @@
6
6
 
7
7
  import * as fs from 'fs';
8
8
  import * as path from 'path';
9
+ import { readJsonl, appendJsonl } from '../utils/jsonl';
9
10
  import type { FailureRecord } from './types';
10
11
 
11
12
  /**
@@ -25,7 +26,7 @@ export interface FailureRecorderConfig {
25
26
  *
26
27
  * 用法:
27
28
  * ```typescript
28
- * const recorder = new FailureRecorder({ logFile: '.harness/logs/failures.log' });
29
+ * const recorder = new FailureRecorder({ logFile: DEFAULT_FAILURE_LOG_FILE });
29
30
  * await recorder.record({
30
31
  * type: ErrorType.TEST_FAILED,
31
32
  * level: FailureLevel.L1,
@@ -58,9 +59,8 @@ export class FailureRecorder {
58
59
  // 检查文件大小,必要时滚动
59
60
  await this.rotateIfNeeded();
60
61
 
61
- // 追加写入(单行 JSON)
62
- const line = JSON.stringify(record) + '\n';
63
- fs.appendFileSync(this.logFile, line, 'utf-8');
62
+ // 追加写入单行 JSON(写链收口:ensureDir + append,harness#82
63
+ appendJsonl(this.logFile, record);
64
64
  }
65
65
 
66
66
  /**
@@ -74,17 +74,19 @@ export class FailureRecorder {
74
74
 
75
75
  /**
76
76
  * 获取历史记录
77
+ *
78
+ * 坏行策略:显式 skip(harness#96 定稿:跳过 + stderr 一次性计数警告,
79
+ * stdout 含 --json 逐字节不变、退出码 0——#82 只把本调用点改走 jsonl
80
+ * 读链正本并吸收 #96 的局部跳过实现,对外行为不变)。
81
+ * 计数去向:透传(harness#100 四消费点之一)——skippedLines 进下面那句 console.error,
82
+ * #96 定稿的文案与退出码本票逐字不动。
77
83
  */
78
84
  async getHistory(limit?: number): Promise<FailureRecord[]> {
79
- if (!fs.existsSync(this.logFile)) {
80
- return [];
85
+ const { records, skippedLines } = readJsonl<FailureRecord>(this.logFile, 'skip');
86
+ if (skippedLines > 0) {
87
+ console.error(`[harness] ${path.basename(this.logFile)} 跳过 ${skippedLines} 行损坏记录`);
81
88
  }
82
89
 
83
- const content = fs.readFileSync(this.logFile, 'utf-8');
84
- const lines = content.trim().split('\n').filter(Boolean);
85
-
86
- const records = lines.map((line) => JSON.parse(line) as FailureRecord);
87
-
88
90
  if (limit && limit > 0) {
89
91
  return records.slice(-limit);
90
92
  }
@@ -10,8 +10,8 @@
10
10
  - 执行器:`runGates`(`runner.ts`)——按 order 升序;deny 单调(下游 abstain 不得改回 allow)+ ask 枚举预留 fail-closed = deny;决策浅冻结
11
11
  - 生效集:`getEffectiveGates(projectRoot)`(`effective-gates.ts`)——对齐 getEffectiveConstraints 裁剪模式:config.yml `gates.order` 重排 + `gates.<id>.enabled:false` 移除;enabled 段未注册校验与裁剪走 core/effective-set 共享筛选器(throw 模式),order 重排与重复 id 检测留本侧
12
12
  - checker-as-guard 接线点:`createCheckerGate(check)`(`checker-gate.ts`)——ConstraintCheck → Gate(studio #129 随动);false→deny,true/'skip'→abstain;env 经 `buildCheckEnv(..., 'none')` 构造(显式不接证据,语义见 core/constraints/checkers/types.ts 工厂 doc)
13
- - 门禁类:`ReviewGate` / `SecurityGate` / `PerformanceGate` / `ContractGate` / `SpecAcceptanceGate` / `CommandGate`(各自执行细节私有,保留 `check()`/`scan()` 报告方法)
14
- - `types.ts` — GateResult(报告结构,保留)/ GateContext / GateDecision 等公共类型
13
+ - 门禁类:`ReviewGate` / `SecurityGate` / `PerformanceGate` / `ContractGate` / `SpecAcceptanceGate` / `CommandGate`(各自执行细节私有,保留 `check()`/`scan()` 报告方法);SpecAcceptanceGate 的 runner 输出解读与 e2e 判负都从 `core/validators/test-output.ts` 的 `judgeTestRun` 取(解析收口 ADR-0012、判定依据 ADR-0014,本层既不自带正则也不自写判定 `if`)
14
+ - `types.ts` — GateResult(报告结构,保留)/ GateContext / GateDecision 等公共类型 + 报告构造器 `pass` / `fail` / `fromError`(动态 passed 走 `gateResult`):timestamp 与 duration 口径唯一落点,门禁实现禁止手写 GateResult 字面量(duration 是可选字段,漏写无编译期报错)
15
15
  - 便捷工厂函数:createReviewGate / createSecurityGate / createPerformanceGate / createContractGate / createSpecAcceptanceGate / createCommandGate
16
16
 
17
17
  ## 依赖关系
@@ -19,12 +19,15 @@
19
19
  - 依赖 `src/types/` 公共类型
20
20
 
21
21
  ## 约定
22
- - 新门禁必须:① 在 `definitions.ts` 补 GateDefinition(含 CLI 元数据)② 在 `registry.ts` IMPLEMENTATIONS 注册实现(缺一 → 加载期抛错)③ 实现统一 Gate 接口(evaluate 产三态决策,报告走 GateResult)④ 配 CLI 命令(命令实现文件 + CLI 元数据的 module+export 引用,bin 由定义表驱动生成,不再手写块)+ 测试文件
22
+ - 新门禁必须:① 在 `definitions.ts` 补 GateDefinition(含 CLI 元数据)② 在 `registry.ts` IMPLEMENTATIONS 注册实现(缺一 → 加载期抛错)③ 实现统一 Gate 接口(evaluate 产三态决策,报告由 `types.ts` 的 `pass`/`fail`/`fromError`/`gateResult` 构造,不手写字面量)④ 配 CLI 命令(命令实现文件 + CLI 元数据的 module+export 引用,bin 由定义表驱动生成,不再手写块)+ 测试文件
23
23
  - deny 单调是接口契约:决策浅冻结,下游不得改写上游决策
24
+ - `runGates` 是 Gate 链语义的**参考实现**(deny 单调 / ask fail-closed / 全决策浅冻结):仓内暂无生产调用方(harness CLI 文件驱动逐命令执行,studio `runCompletionGuards` 经 checker-gate 复用语义但不 import),消费方只有测试。「deny 后短路跳过剩余门禁」类优化在出现真实消费者前不做——它与「按执行顺序的全部决策」报告契约直接冲突,短路等于改 GateResult 语义(harness#115 裁决,防后续评审重复提议)
24
25
  - ask 枚举预留:暂无实现,runGates fail-closed 按 deny 计
26
+ - **收到的根要传到自己每个 IO/执行点(harness#95)**:门禁只认 `context.projectPath`,内部相对子路径一律锚到它(`SpecAcceptanceGate` 的 tasksPath、`ContractGate` 的 contractPath、e2e/scan/benchmark 的 `cwd`),禁止「根已传入却又取 cwd」、禁止 `xxxPath: './…'` 相对默认值——否则 `-p` 半失效且表现为假绿(acceptance 的「无 tasks.yml 即跳过」= passed:true)。本层唯一保留的 cwd 站点是 `checker-gate` 对 `ctx.projectPath` 的缺省兜底(豁免理由见守护表)。约定正本与两道机器可检的闸见 `src/cli/commands/CONTEXT.md`
25
27
 
26
28
  ## 注意事项
27
29
  - 门禁系统不包含业务逻辑,只提供检查能力
30
+ - `acceptance.ts` 的 13 处 return 是 `AcceptanceGateResult`(无 gate/duration 字段,`evaluate()` 归一化为 GateResult 报告——设计如此,见该方法注释),不是 GateResult 字面量,故不套 `pass`/`fail` 构造器;改成 GateResult 会动公共类型形状
28
31
  - 统一的是决策协议(id/order/三态),执行细节(gh pr view/正则黑名单/OpenAPI diff/benchmark)私有——不要把执行细节塞进 Gate 接口
29
32
  - config.yml gates 段为新增面:引用未注册 id 直接抛错(无历史残留配置需兼容)
30
33
  - CommandGate 为命令黑名单检查(SEC-006)
@@ -0,0 +1,68 @@
1
+ /**
2
+ * AcceptanceGate 类型单一正本(harness#101,架构评审 A1)
3
+ *
4
+ * 钉住三条:
5
+ * 1. 公共导出面(gates/types.ts)的 SpecAcceptanceGateConfig 并入 e2e 字段——
6
+ * 修前窄版会在类型层面拒绝 e2eTestCommand/e2eTestTimeout/projectPath(编译即红)。
7
+ * 2. 测试与公共面走同一 seam:工厂从 gates/index 取,acceptance.ts 侧工厂已删。
8
+ * 3. acceptance.ts 不再持有三类型/工厂的双份定义(源码扫描闸,防双正本回潮)。
9
+ */
10
+
11
+ import * as fs from 'fs';
12
+ import * as path from 'path';
13
+ import { SpecAcceptanceGate, createSpecAcceptanceGate } from '../index';
14
+ import type {
15
+ SpecAcceptanceGateConfig,
16
+ AcceptanceGateContext,
17
+ AcceptanceCriteria,
18
+ } from '../types';
19
+
20
+ describe('AcceptanceGate 类型单一正本(#101)', () => {
21
+ it('公共面 SpecAcceptanceGateConfig 接受 e2eTestCommand/e2eTestTimeout/projectPath', () => {
22
+ const config: SpecAcceptanceGateConfig = {
23
+ tasksPath: 'tasks.yml',
24
+ checkAllTasks: true,
25
+ e2eTestCommand: 'npx playwright test',
26
+ e2eTestTimeout: 60000,
27
+ projectPath: '/tmp/proj',
28
+ };
29
+ expect(config.e2eTestCommand).toBe('npx playwright test');
30
+ expect(config.e2eTestTimeout).toBe(60000);
31
+ expect(config.projectPath).toBe('/tmp/proj');
32
+ });
33
+
34
+ it('公共面 AcceptanceGateContext / AcceptanceCriteria 形状不变', () => {
35
+ const context: AcceptanceGateContext = {
36
+ projectPath: '/tmp/proj',
37
+ taskId: 'TASK-001',
38
+ tasksPath: 'tasks.yml',
39
+ };
40
+ const criteria: AcceptanceCriteria = {
41
+ id: 'AC-1',
42
+ description: 'desc',
43
+ type: 'automated',
44
+ required: true,
45
+ checked: true,
46
+ notes: 'n',
47
+ };
48
+ expect(context.projectPath).toBe('/tmp/proj');
49
+ expect(criteria.type).toBe('automated');
50
+ });
51
+
52
+ it('gates/index 工厂接受并入后的宽版配置(与公共面同一 seam)', () => {
53
+ const gate = createSpecAcceptanceGate({
54
+ e2eTestCommand: 'npx playwright test',
55
+ e2eTestTimeout: 60000,
56
+ projectPath: '/tmp/proj',
57
+ });
58
+ expect(gate).toBeInstanceOf(SpecAcceptanceGate);
59
+ });
60
+
61
+ it('acceptance.ts 不再持有三类型与工厂的双份定义', () => {
62
+ const source = fs.readFileSync(path.join(__dirname, '../acceptance.ts'), 'utf-8');
63
+ expect(source).not.toMatch(
64
+ /export interface (SpecAcceptanceGateConfig|AcceptanceGateContext|AcceptanceCriteria)\b/,
65
+ );
66
+ expect(source).not.toMatch(/export function createSpecAcceptanceGate\b/);
67
+ });
68
+ });
@@ -2,7 +2,9 @@
2
2
  * SpecAcceptanceGate 测试
3
3
  */
4
4
 
5
- import { SpecAcceptanceGate, createSpecAcceptanceGate } from '../acceptance';
5
+ import { SpecAcceptanceGate } from '../acceptance';
6
+ // 工厂与公共面同一 seam:gates/index 是唯一出口(harness#101)
7
+ import { createSpecAcceptanceGate } from '../index';
6
8
  import * as fs from 'fs/promises';
7
9
  import { exec } from 'child_process';
8
10
 
@@ -377,7 +379,7 @@ tasks:
377
379
  });
378
380
  });
379
381
 
380
- describe('parseTestOutput()', () => {
382
+ describe('runE2ETest 判定接线(解析分支矩阵见 core/validators/__tests__/test-output.test.ts)', () => {
381
383
  it('should detect passed Playwright test', async () => {
382
384
  mockFs.readFile.mockResolvedValueOnce(`
383
385
  tasks:
@@ -419,90 +421,6 @@ tasks:
419
421
 
420
422
  expect(result.passed).toBe(false);
421
423
  });
422
-
423
- it('should detect passed Jest test', async () => {
424
- mockFs.readFile.mockResolvedValueOnce(`
425
- tasks:
426
- - id: TASK-001
427
- acceptance:
428
- - description: Test
429
- e2e_test: test.spec.ts
430
- `);
431
-
432
- mockExec.mockImplementationOnce((cmd, opts, callback) => {
433
- callback(null, { stdout: 'Test Suites: 0 failed, 5 passed', stderr: '' });
434
- });
435
-
436
- const result = await gate.check({
437
- projectPath: '/test/project',
438
- taskId: 'TASK-001',
439
- });
440
-
441
- expect(result.passed).toBe(true);
442
- });
443
-
444
- it('should detect failed Jest test', async () => {
445
- mockFs.readFile.mockResolvedValueOnce(`
446
- tasks:
447
- - id: TASK-001
448
- acceptance:
449
- - description: Test
450
- e2e_test: test.spec.ts
451
- `);
452
-
453
- mockExec.mockImplementationOnce((cmd, opts, callback) => {
454
- callback(null, { stdout: 'Test Suites: 2 failed, 3 passed', stderr: '' });
455
- });
456
-
457
- const result = await gate.check({
458
- projectPath: '/test/project',
459
- taskId: 'TASK-001',
460
- });
461
-
462
- expect(result.passed).toBe(false);
463
- });
464
-
465
- it('should detect PASS in generic output', async () => {
466
- mockFs.readFile.mockResolvedValueOnce(`
467
- tasks:
468
- - id: TASK-001
469
- acceptance:
470
- - description: Test
471
- e2e_test: test.spec.ts
472
- `);
473
-
474
- mockExec.mockImplementationOnce((cmd, opts, callback) => {
475
- callback(null, { stdout: 'PASS test.js', stderr: '' });
476
- });
477
-
478
- const result = await gate.check({
479
- projectPath: '/test/project',
480
- taskId: 'TASK-001',
481
- });
482
-
483
- expect(result.passed).toBe(true);
484
- });
485
-
486
- it('should detect FAIL in generic output', async () => {
487
- mockFs.readFile.mockResolvedValueOnce(`
488
- tasks:
489
- - id: TASK-001
490
- acceptance:
491
- - description: Test
492
- e2e_test: test.spec.ts
493
- `);
494
-
495
- mockExec.mockImplementationOnce((cmd, opts, callback) => {
496
- callback(null, { stdout: 'PASS test1.js\nFAIL test2.js', stderr: '' });
497
- });
498
-
499
- const result = await gate.check({
500
- projectPath: '/test/project',
501
- taskId: 'TASK-001',
502
- });
503
-
504
- expect(result.passed).toBe(false);
505
- });
506
424
  });
507
425
 
508
426
  describe('E2E test configuration', () => {
@@ -3,9 +3,8 @@
3
3
  */
4
4
 
5
5
  import * as fs from 'fs';
6
- import * as os from 'os';
7
- import * as path from 'path';
8
6
  import { getEffectiveGates } from '../effective-gates';
7
+ import { createProjectFixture, writeProjectConfig } from '../../test-setup/project-fixture';
9
8
 
10
9
  const DEFAULT_ORDER = ['acceptance', 'command', 'contract', 'performance', 'review', 'security'];
11
10
 
@@ -13,18 +12,14 @@ describe('getEffectiveGates', () => {
13
12
  let dir: string;
14
13
 
15
14
  beforeEach(() => {
16
- dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-gates-'));
15
+ dir = createProjectFixture({ name: 'harness-gates' });
17
16
  });
18
17
 
19
18
  afterEach(() => {
20
19
  fs.rmSync(dir, { recursive: true, force: true });
21
20
  });
22
21
 
23
- function writeConfig(yamlText: string): void {
24
- const harnessDir = path.join(dir, '.harness');
25
- fs.mkdirSync(harnessDir, { recursive: true });
26
- fs.writeFileSync(path.join(harnessDir, 'config.yml'), yamlText);
27
- }
22
+ const writeConfig = (yamlText: string): void => writeProjectConfig(dir, yamlText);
28
23
 
29
24
  it('无 config.yml → 6 门禁按默认 order', () => {
30
25
  expect(getEffectiveGates(dir).map(g => g.id)).toEqual(DEFAULT_ORDER);
@@ -0,0 +1,140 @@
1
+ /**
2
+ * 两门禁判「过没过」的一致性(#93,ADR-0014)
3
+ *
4
+ * PassesGate 的 runTest 与 SpecAcceptanceGate 的 e2e 分支过去判定依据不同:前者成功分支
5
+ * 只看退出码(exit 0 即过,输出里的 ✕ 清单不参与),后者只看 stdout 文本(裸 includes('FAIL')
6
+ * 兜底能被测试名字反转)。同一份 runner 输出可以一处判过、另一处判不过。
7
+ *
8
+ * 裁决后两者从同一判定入口出结果,本文件从两个门禁的**公开面**(PassesGate.runTests() /
9
+ * SpecAcceptanceGate.check())喂同一份样本 + 同一个退出码,断言结论一致且等于裁决值。
10
+ * 判定矩阵本身(各 runner 家族、畸形输入、allowPartialPass)见
11
+ * core/validators/__tests__/test-output.test.ts。
12
+ *
13
+ * 落点在 gates/ 不选 core/:单向分层禁止 src/core/** 值导入 gates/(#88,ADR-0012 落点理由同源)。
14
+ */
15
+
16
+ import { describe, it, expect, beforeEach, jest } from '@jest/globals';
17
+ import { exec } from 'child_process';
18
+ import * as fs from 'fs/promises';
19
+ import { PassesGate } from '../../core/validators/passes-gate';
20
+ import { SpecAcceptanceGate } from '../acceptance';
21
+
22
+ jest.mock('child_process', () => ({ exec: jest.fn() }));
23
+ jest.mock('fs/promises');
24
+
25
+ const mockExec = exec as unknown as jest.Mock;
26
+ const mockFs = fs as unknown as jest.Mocked<typeof fs>;
27
+
28
+ // ========================================
29
+ // runner 输出样本
30
+ // ========================================
31
+
32
+ /** jest verbose 失败清单:✕ 用例名 + 结构化汇总行,全在 stdout 里 */
33
+ const JEST_FAILED_CASES = `FAIL src/app.test.ts
34
+ App
35
+ ✕ renders title (23 ms)
36
+ ✓ renders subtitle (5 ms)
37
+
38
+ Test Suites: 1 failed, 1 total
39
+ Tests: 1 failed, 1 passed, 2 total
40
+ `;
41
+
42
+ /** jest 非 verbose:没有 ✕ 行,只有汇总行说失败了 */
43
+ const JEST_SUMMARY_ONLY_FAILURE = `Test Suites: 2 failed, 2 total
44
+ Tests: 3 failed, 3 total
45
+ `;
46
+
47
+ /**
48
+ * 全绿输出,但通过的用例名里带大写 FAIL 字样。
49
+ * 不带汇总行 / 无 "passed" 字样 —— 这正是旧 acceptance 落到裸 includes('FAIL') 兜底那一支的形状。
50
+ */
51
+ const JEST_GREEN_WITH_FAIL_IN_NAME = `PASS src/a.test.js
52
+ App
53
+ ✓ FAIL 时应该重试 (3 ms)
54
+ `;
55
+
56
+ /** jest 全绿 */
57
+ const JEST_GREEN = `PASS src/a.test.js
58
+ Test Suites: 1 passed, 1 total
59
+ Tests: 3 passed, 3 total
60
+ `;
61
+
62
+ type Sample = { name: string; exitCode: number; output: string; expected: boolean };
63
+
64
+ const SAMPLES: Sample[] = [
65
+ { name: 'exit 0 + jest ✕ 失败清单', exitCode: 0, output: JEST_FAILED_CASES, expected: false },
66
+ { name: 'exit 0 + jest 汇总行只有 failed', exitCode: 0, output: JEST_SUMMARY_ONLY_FAILURE, expected: false },
67
+ { name: 'exit 0 + 全绿但用例名含 FAIL', exitCode: 0, output: JEST_GREEN_WITH_FAIL_IN_NAME, expected: true },
68
+ { name: 'exit 0 + jest 全绿', exitCode: 0, output: JEST_GREEN, expected: true },
69
+ { name: 'exit 1 + 全绿文本(文本不赦免非零退出)', exitCode: 1, output: JEST_GREEN, expected: false },
70
+ ];
71
+
72
+ // ========================================
73
+ // 两个门禁的公开面
74
+ // ========================================
75
+
76
+ /** 退出结果:数字 = 退出码;'killed' = 超时/信号杀掉,reject 的 error 上没有数字退出码 */
77
+ type ExitResult = number | 'killed';
78
+ type VerdictSample = { exitCode: ExitResult; output: string };
79
+
80
+ /** 同一份样本 + 同一退出结果喂给 exec mock:0 → resolve,其余 → reject */
81
+ function stubRunner(exitCode: ExitResult, stdout: string): void {
82
+ mockExec.mockImplementationOnce((...args: unknown[]) => {
83
+ const callback = args[2] as (err: unknown, res?: unknown) => void;
84
+ if (exitCode === 0) {
85
+ callback(null, { stdout, stderr: '' });
86
+ return;
87
+ }
88
+ const error = Object.assign(
89
+ new Error(exitCode === 'killed' ? 'test timed out after 120000ms' : 'Command failed: npm test'),
90
+ { stdout, stderr: '', ...(exitCode === 'killed' ? {} : { code: exitCode }) },
91
+ );
92
+ callback(error);
93
+ });
94
+ }
95
+
96
+ async function verdictFromPassesGate(sample: VerdictSample): Promise<boolean> {
97
+ stubRunner(sample.exitCode, sample.output);
98
+ const gate = new PassesGate({ testCommand: 'npm test', requireEvidence: false });
99
+ return (await gate.runTests('/test/project')).passed;
100
+ }
101
+
102
+ async function verdictFromAcceptance(sample: VerdictSample): Promise<boolean> {
103
+ mockFs.readFile.mockResolvedValueOnce(`
104
+ tasks:
105
+ - id: TASK-001
106
+ acceptance:
107
+ - description: 验收条件
108
+ e2e_test: tests/example.spec.ts
109
+ `);
110
+ stubRunner(sample.exitCode, sample.output);
111
+ const gate = new SpecAcceptanceGate();
112
+ const result = await gate.check({ projectPath: '/test/project', taskId: 'TASK-001' });
113
+ return result.passed;
114
+ }
115
+
116
+ describe('两门禁对同一份 runner 输出给出同一结论', () => {
117
+ beforeEach(() => {
118
+ jest.clearAllMocks();
119
+ });
120
+
121
+ for (const sample of SAMPLES) {
122
+ it(`${sample.name} → 同判「${sample.expected ? '过' : '不过'}」`, async () => {
123
+ const fromPassesGate = await verdictFromPassesGate(sample);
124
+ const fromAcceptance = await verdictFromAcceptance(sample);
125
+
126
+ expect({ fromPassesGate, fromAcceptance }).toEqual({
127
+ fromPassesGate: sample.expected,
128
+ fromAcceptance: sample.expected,
129
+ });
130
+ expect(fromAcceptance).toBe(fromPassesGate);
131
+ });
132
+ }
133
+
134
+ it('runner 被超时杀掉(无数字退出码)→ 同判「不过」', async () => {
135
+ const sample: VerdictSample = { exitCode: 'killed', output: JEST_GREEN };
136
+
137
+ expect(await verdictFromPassesGate(sample)).toBe(false);
138
+ expect(await verdictFromAcceptance(sample)).toBe(false);
139
+ });
140
+ });
@@ -18,56 +18,24 @@
18
18
  */
19
19
 
20
20
  import { execAsync } from '../utils/exec';
21
+ import { judgeTestRun } from '../core/validators/test-output';
21
22
  import * as fs from 'fs/promises';
22
23
  import * as path from 'path';
23
24
  import * as yaml from 'js-yaml';
24
- import type { Gate, GateContext, GateDecision } from './types';
25
+ import type {
26
+ Gate,
27
+ GateContext,
28
+ GateDecision,
29
+ SpecAcceptanceGateConfig,
30
+ AcceptanceGateContext,
31
+ AcceptanceCriteria,
32
+ } from './types';
25
33
  import { decisionFromResult } from './decision';
26
34
 
27
35
 
28
36
  // ==================== 类型定义 ====================
29
-
30
- /**
31
- * 验收标准门禁配置
32
- */
33
- export interface SpecAcceptanceGateConfig {
34
- /** tasks.yml 路径 */
35
- tasksPath?: string;
36
- /** 是否检查所有任务 */
37
- checkAllTasks?: boolean;
38
- /** 自定义验收条件 */
39
- customAcceptanceCriteria?: Record<string, (task: any) => Promise<boolean>>;
40
- /** E2E 测试命令模板 */
41
- e2eTestCommand?: string;
42
- /** E2E 测试超时时间(毫秒) */
43
- e2eTestTimeout?: number;
44
- /** 项目路径 */
45
- projectPath?: string;
46
- }
47
-
48
- /**
49
- * 验收标准门禁上下文
50
- */
51
- export interface AcceptanceGateContext {
52
- /** 项目路径 */
53
- projectPath: string;
54
- /** 任务 ID */
55
- taskId?: string;
56
- /** tasks.yml 路径 */
57
- tasksPath?: string;
58
- }
59
-
60
- /**
61
- * 验收标准(旧格式,向后兼容)
62
- */
63
- export interface AcceptanceCriteria {
64
- id: string;
65
- description: string;
66
- type: 'manual' | 'automated' | 'test';
67
- required: boolean;
68
- checked?: boolean;
69
- notes?: string;
70
- }
37
+ // SpecAcceptanceGateConfig / AcceptanceGateContext / AcceptanceCriteria 正本在 ./types
38
+ // (harness#101:本文件不再持有双份定义与工厂,测试与公共面同走 gates/index seam)
71
39
 
72
40
  /**
73
41
  * 验收条件(新格式,支持 E2E 测试关联)
@@ -144,7 +112,6 @@ export class SpecAcceptanceGate implements Gate {
144
112
 
145
113
  constructor(config?: Partial<SpecAcceptanceGateConfig>) {
146
114
  this.config = {
147
- tasksPath: './tasks.yml',
148
115
  checkAllTasks: false,
149
116
  e2eTestCommand: 'npx playwright test',
150
117
  e2eTestTimeout: 120000,
@@ -176,10 +143,11 @@ export class SpecAcceptanceGate implements Gate {
176
143
  */
177
144
  async check(context: AcceptanceGateContext): Promise<AcceptanceGateResult> {
178
145
  try {
179
- // 确定 tasks.yml 路径
180
- const tasksPath = context.tasksPath ??
181
- this.config.tasksPath ??
182
- path.join(context.projectPath, 'tasks.yml');
146
+ // 确定 tasks.yml 路径:相对值一律锚在 projectPath(此处不取 cwd)
147
+ const tasksPath = path.resolve(
148
+ context.projectPath,
149
+ context.tasksPath ?? this.config.tasksPath ?? 'tasks.yml',
150
+ );
183
151
 
184
152
  // 加载 tasks 文件
185
153
  const tasks = await this.loadTasks(tasksPath);
@@ -458,7 +426,8 @@ export class SpecAcceptanceGate implements Gate {
458
426
  });
459
427
 
460
428
  const output = stdout + stderr;
461
- const passed = this.parseTestOutput(output);
429
+ // 退出码为 0 的分支:文本只能否决,不能反向加分(与 passes-gate 同一判定入口,ADR-0014)
430
+ const { passed } = judgeTestRun({ exitCode: 0, output });
462
431
 
463
432
  return {
464
433
  criteria: testFile,
@@ -468,7 +437,7 @@ export class SpecAcceptanceGate implements Gate {
468
437
  output: output.substring(0, 2000),
469
438
  };
470
439
  } catch (error: any) {
471
- // 超时或执行失败
440
+ // 超时或非零退出的分支:判负,文本不参与(文本救不回非零退出,ADR-0014)
472
441
  return {
473
442
  criteria: testFile,
474
443
  testFile,
@@ -479,38 +448,4 @@ export class SpecAcceptanceGate implements Gate {
479
448
  };
480
449
  }
481
450
  }
482
-
483
- /**
484
- * 解析测试输出判断是否通过
485
- */
486
- private parseTestOutput(output: string): boolean {
487
- // Playwright 格式
488
- if (output.includes('passed')) {
489
- // 检查是否有失败
490
- const failedMatch = output.match(/(\d+)\s+failed/);
491
- if (failedMatch && parseInt(failedMatch[1]) > 0) {
492
- return false;
493
- }
494
- return true;
495
- }
496
-
497
- // Jest 格式
498
- if (output.includes('Test Suites:')) {
499
- const match = output.match(/Test Suites:\s+(\d+)\s+failed/);
500
- if (match && parseInt(match[1]) > 0) {
501
- return false;
502
- }
503
- return output.includes('passed');
504
- }
505
-
506
- // 通用格式
507
- return output.includes('PASS') && !output.includes('FAIL');
508
- }
509
- }
510
-
511
- /**
512
- * 创建验收标准门禁(便捷函数)
513
- */
514
- export function createSpecAcceptanceGate(config?: Partial<SpecAcceptanceGateConfig>): SpecAcceptanceGate {
515
- return new SpecAcceptanceGate(config);
516
451
  }
@@ -10,6 +10,7 @@
10
10
  import type { ConstraintCheck } from '../core/constraints/checkers';
11
11
  import { buildCheckEnv } from '../core/constraints/checkers';
12
12
  import type { Gate, GateContext, GateDecision } from './types';
13
+ import { gateResult } from './types';
13
14
  import { decisionFromResult } from './decision';
14
15
 
15
16
  /**
@@ -29,18 +30,18 @@ export function createCheckerGate(check: ConstraintCheck, order = 0): Gate {
29
30
  const projectPath = ctx.projectPath || process.cwd();
30
31
  const env = buildCheckEnv({ operation: 'manual', projectPath }, 'none');
31
32
  const outcome = await check.evaluate(env);
32
- return decisionFromResult({
33
- gate: check.id,
34
- passed: outcome !== false,
35
- message:
33
+ return decisionFromResult(
34
+ gateResult(
35
+ check.id,
36
+ outcome !== false,
36
37
  outcome === false
37
38
  ? `checker "${check.id}" 判定违规`
38
39
  : outcome === 'skip'
39
40
  ? `checker "${check.id}" 跳过(证据未接线)`
40
41
  : `checker "${check.id}" 通过`,
41
- timestamp: new Date().toISOString(),
42
- duration: Date.now() - startTime,
43
- });
42
+ startTime
43
+ )
44
+ );
44
45
  },
45
46
  };
46
47
  }
@@ -5,6 +5,7 @@
5
5
  * 检查命令是否安全,防止执行危险操作
6
6
  */
7
7
 
8
+ import { pass, gateResult } from './types';
8
9
  import type { GateResult, CommandGateConfig, CommandBlacklistRule, Gate, GateContext, GateDecision } from './types';
9
10
  import { decisionFromResult } from './decision';
10
11
 
@@ -232,32 +233,25 @@ export class CommandGate implements Gate {
232
233
  const startTime = Date.now();
233
234
 
234
235
  if (!this.config.enabled) {
235
- return {
236
- gate: 'command',
237
- passed: true,
238
- message: '命令门禁已禁用',
239
- timestamp: new Date().toISOString(),
240
- duration: Date.now() - startTime,
241
- };
236
+ return pass('command', '命令门禁已禁用', startTime);
242
237
  }
243
238
 
244
239
  const result = this.checkBlacklist(command);
245
240
 
246
- return {
247
- gate: 'command',
248
- passed: result.allowed,
249
- message: result.allowed
241
+ return gateResult(
242
+ 'command',
243
+ result.allowed,
244
+ result.allowed
250
245
  ? this.formatSuccessMessage(result)
251
246
  : this.formatBlockMessage(result),
252
- details: {
247
+ startTime,
248
+ {
253
249
  blocked: result.blocked,
254
250
  warnings: result.warnings,
255
251
  audits: result.audits,
256
252
  command,
257
- },
258
- timestamp: new Date().toISOString(),
259
- duration: Date.now() - startTime,
260
- };
253
+ }
254
+ );
261
255
  }
262
256
 
263
257
  /**