@dommaker/harness 1.6.0 → 1.8.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 (560) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/bin/harness.js +17 -1
  3. package/dist/agents/index.d.ts +6 -2
  4. package/dist/agents/index.d.ts.map +1 -1
  5. package/dist/agents/index.js +6 -16
  6. package/dist/agents/index.js.map +1 -1
  7. package/dist/cli/commands/__tests__/gate-decision.d.ts +18 -0
  8. package/dist/cli/commands/__tests__/gate-decision.d.ts.map +1 -0
  9. package/dist/cli/commands/__tests__/gate-decision.js +26 -0
  10. package/dist/cli/commands/__tests__/gate-decision.js.map +1 -0
  11. package/dist/cli/commands/acceptance.d.ts.map +1 -1
  12. package/dist/cli/commands/acceptance.js +17 -28
  13. package/dist/cli/commands/acceptance.js.map +1 -1
  14. package/dist/cli/commands/check.d.ts +11 -3
  15. package/dist/cli/commands/check.d.ts.map +1 -1
  16. package/dist/cli/commands/check.js +26 -11
  17. package/dist/cli/commands/check.js.map +1 -1
  18. package/dist/cli/commands/command.d.ts +0 -1
  19. package/dist/cli/commands/command.d.ts.map +1 -1
  20. package/dist/cli/commands/command.js +8 -6
  21. package/dist/cli/commands/command.js.map +1 -1
  22. package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
  23. package/dist/cli/commands/constraints-retire.js +36 -57
  24. package/dist/cli/commands/constraints-retire.js.map +1 -1
  25. package/dist/cli/commands/contract.d.ts.map +1 -1
  26. package/dist/cli/commands/contract.js +36 -42
  27. package/dist/cli/commands/contract.js.map +1 -1
  28. package/dist/cli/commands/definitions.d.ts +2 -0
  29. package/dist/cli/commands/definitions.d.ts.map +1 -1
  30. package/dist/cli/commands/definitions.js +11 -6
  31. package/dist/cli/commands/definitions.js.map +1 -1
  32. package/dist/cli/commands/failure.d.ts.map +1 -1
  33. package/dist/cli/commands/failure.js +16 -2
  34. package/dist/cli/commands/failure.js.map +1 -1
  35. package/dist/cli/commands/init.d.ts +14 -8
  36. package/dist/cli/commands/init.d.ts.map +1 -1
  37. package/dist/cli/commands/init.js +172 -364
  38. package/dist/cli/commands/init.js.map +1 -1
  39. package/dist/cli/commands/knowledge-view.d.ts +90 -0
  40. package/dist/cli/commands/knowledge-view.d.ts.map +1 -0
  41. package/dist/cli/commands/knowledge-view.js +198 -0
  42. package/dist/cli/commands/knowledge-view.js.map +1 -0
  43. package/dist/cli/commands/knowledge.d.ts +137 -52
  44. package/dist/cli/commands/knowledge.d.ts.map +1 -1
  45. package/dist/cli/commands/knowledge.js +534 -400
  46. package/dist/cli/commands/knowledge.js.map +1 -1
  47. package/dist/cli/commands/passes-gate.d.ts +4 -1
  48. package/dist/cli/commands/passes-gate.d.ts.map +1 -1
  49. package/dist/cli/commands/passes-gate.js +11 -2
  50. package/dist/cli/commands/passes-gate.js.map +1 -1
  51. package/dist/cli/commands/performance.d.ts.map +1 -1
  52. package/dist/cli/commands/performance.js +19 -31
  53. package/dist/cli/commands/performance.js.map +1 -1
  54. package/dist/cli/commands/report.d.ts.map +1 -1
  55. package/dist/cli/commands/report.js +2 -1
  56. package/dist/cli/commands/report.js.map +1 -1
  57. package/dist/cli/commands/review.d.ts.map +1 -1
  58. package/dist/cli/commands/review.js +13 -25
  59. package/dist/cli/commands/review.js.map +1 -1
  60. package/dist/cli/commands/scaffold-templates.d.ts +73 -0
  61. package/dist/cli/commands/scaffold-templates.d.ts.map +1 -0
  62. package/dist/cli/commands/scaffold-templates.js +424 -0
  63. package/dist/cli/commands/scaffold-templates.js.map +1 -0
  64. package/dist/cli/commands/scaffold.d.ts +105 -0
  65. package/dist/cli/commands/scaffold.d.ts.map +1 -0
  66. package/dist/cli/commands/scaffold.js +263 -0
  67. package/dist/cli/commands/scaffold.js.map +1 -0
  68. package/dist/cli/commands/security.d.ts.map +1 -1
  69. package/dist/cli/commands/security.js +22 -24
  70. package/dist/cli/commands/security.js.map +1 -1
  71. package/dist/cli/commands/spec-baseline-check.d.ts +43 -0
  72. package/dist/cli/commands/spec-baseline-check.d.ts.map +1 -1
  73. package/dist/cli/commands/spec-baseline-check.js +109 -34
  74. package/dist/cli/commands/spec-baseline-check.js.map +1 -1
  75. package/dist/cli/commands/sync-docs/context-syncer.d.ts +21 -2
  76. package/dist/cli/commands/sync-docs/context-syncer.d.ts.map +1 -1
  77. package/dist/cli/commands/sync-docs/context-syncer.js +112 -28
  78. package/dist/cli/commands/sync-docs/context-syncer.js.map +1 -1
  79. package/dist/cli/commands/sync-docs/index.d.ts +1 -1
  80. package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
  81. package/dist/cli/commands/sync-docs/index.js +86 -27
  82. package/dist/cli/commands/sync-docs/index.js.map +1 -1
  83. package/dist/cli/commands/sync-docs/project-reader.d.ts +24 -1
  84. package/dist/cli/commands/sync-docs/project-reader.d.ts.map +1 -1
  85. package/dist/cli/commands/sync-docs/project-reader.js +33 -7
  86. package/dist/cli/commands/sync-docs/project-reader.js.map +1 -1
  87. package/dist/cli/commands/validate.d.ts +0 -16
  88. package/dist/cli/commands/validate.d.ts.map +1 -1
  89. package/dist/cli/commands/validate.js +2 -97
  90. package/dist/cli/commands/validate.js.map +1 -1
  91. package/dist/cli/gate-command.d.ts +37 -0
  92. package/dist/cli/gate-command.d.ts.map +1 -0
  93. package/dist/cli/gate-command.js +64 -0
  94. package/dist/cli/gate-command.js.map +1 -0
  95. package/dist/completion-checkers/index.d.ts +1 -1
  96. package/dist/completion-checkers/index.d.ts.map +1 -1
  97. package/dist/completion-checkers/index.js +0 -15
  98. package/dist/completion-checkers/index.js.map +1 -1
  99. package/dist/context/knowledge-injector.d.ts +4 -2
  100. package/dist/context/knowledge-injector.d.ts.map +1 -1
  101. package/dist/context/knowledge-injector.js +13 -6
  102. package/dist/context/knowledge-injector.js.map +1 -1
  103. package/dist/core/constraints/agent-prompt-renderer.js +2 -2
  104. package/dist/core/constraints/agent-prompt-renderer.js.map +1 -1
  105. package/dist/core/constraints/capabilities-reconcile.d.ts +20 -0
  106. package/dist/core/constraints/capabilities-reconcile.d.ts.map +1 -1
  107. package/dist/core/constraints/capabilities-reconcile.js +26 -4
  108. package/dist/core/constraints/capabilities-reconcile.js.map +1 -1
  109. package/dist/core/constraints/check-cache.d.ts +1 -1
  110. package/dist/core/constraints/check-cache.js +1 -1
  111. package/dist/core/constraints/checker.d.ts +8 -3
  112. package/dist/core/constraints/checker.d.ts.map +1 -1
  113. package/dist/core/constraints/checker.js +30 -19
  114. package/dist/core/constraints/checker.js.map +1 -1
  115. package/dist/core/constraints/checkers/capability-sync.d.ts +3 -1
  116. package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
  117. package/dist/core/constraints/checkers/capability-sync.js +17 -24
  118. package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
  119. package/dist/core/constraints/checkers/context-doc-sync.d.ts +1 -1
  120. package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
  121. package/dist/core/constraints/checkers/context-doc-sync.js +9 -6
  122. package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
  123. package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
  124. package/dist/core/constraints/checkers/docs-freshness.js +24 -30
  125. package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
  126. package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
  127. package/dist/core/constraints/checkers/governance-presence.js +10 -4
  128. package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
  129. package/dist/core/constraints/checkers/types.d.ts +10 -6
  130. package/dist/core/constraints/checkers/types.d.ts.map +1 -1
  131. package/dist/core/constraints/checkers/types.js +7 -3
  132. package/dist/core/constraints/checkers/types.js.map +1 -1
  133. package/dist/core/constraints/context-builder.d.ts +11 -10
  134. package/dist/core/constraints/context-builder.d.ts.map +1 -1
  135. package/dist/core/constraints/context-builder.js +31 -27
  136. package/dist/core/constraints/context-builder.js.map +1 -1
  137. package/dist/core/constraints/context-reconcile.d.ts +75 -0
  138. package/dist/core/constraints/context-reconcile.d.ts.map +1 -0
  139. package/dist/core/constraints/context-reconcile.js +161 -0
  140. package/dist/core/constraints/context-reconcile.js.map +1 -0
  141. package/dist/core/constraints/doc-freshness/runner.d.ts +7 -0
  142. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  143. package/dist/core/constraints/doc-freshness/runner.js +8 -3
  144. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  145. package/dist/core/constraints/git-evidence.js +1 -1
  146. package/dist/core/constraints/git-evidence.js.map +1 -1
  147. package/dist/core/constraints/injection-drift.d.ts +7 -2
  148. package/dist/core/constraints/injection-drift.d.ts.map +1 -1
  149. package/dist/core/constraints/injection-drift.js +13 -7
  150. package/dist/core/constraints/injection-drift.js.map +1 -1
  151. package/dist/core/constraints/run-env.d.ts +92 -0
  152. package/dist/core/constraints/run-env.d.ts.map +1 -0
  153. package/dist/core/constraints/run-env.js +175 -0
  154. package/dist/core/constraints/run-env.js.map +1 -0
  155. package/dist/core/constraints/triggers.d.ts +22 -0
  156. package/dist/core/constraints/triggers.d.ts.map +1 -0
  157. package/dist/core/constraints/triggers.js +31 -0
  158. package/dist/core/constraints/triggers.js.map +1 -0
  159. package/dist/core/effective-constraints.d.ts +13 -4
  160. package/dist/core/effective-constraints.d.ts.map +1 -1
  161. package/dist/core/effective-constraints.js +16 -6
  162. package/dist/core/effective-constraints.js.map +1 -1
  163. package/dist/core/effective-set.d.ts +8 -15
  164. package/dist/core/effective-set.d.ts.map +1 -1
  165. package/dist/core/effective-set.js +7 -12
  166. package/dist/core/effective-set.js.map +1 -1
  167. package/dist/core/index.d.ts +2 -1
  168. package/dist/core/index.d.ts.map +1 -1
  169. package/dist/core/index.js.map +1 -1
  170. package/dist/core/project-config-loader.d.ts +39 -17
  171. package/dist/core/project-config-loader.d.ts.map +1 -1
  172. package/dist/core/project-config-loader.js +51 -122
  173. package/dist/core/project-config-loader.js.map +1 -1
  174. package/dist/core/validators/check-handlers/http.d.ts +2 -2
  175. package/dist/core/validators/check-handlers/http.d.ts.map +1 -1
  176. package/dist/core/validators/check-handlers/http.js +12 -7
  177. package/dist/core/validators/check-handlers/http.js.map +1 -1
  178. package/dist/core/validators/index.d.ts +1 -1
  179. package/dist/core/validators/index.d.ts.map +1 -1
  180. package/dist/core/validators/passes-gate.d.ts.map +1 -1
  181. package/dist/core/validators/passes-gate.js +2 -2
  182. package/dist/core/validators/passes-gate.js.map +1 -1
  183. package/dist/failure/classifier.d.ts +1 -1
  184. package/dist/failure/classifier.d.ts.map +1 -1
  185. package/dist/failure/classifier.js +8 -8
  186. package/dist/failure/classifier.js.map +1 -1
  187. package/dist/failure/index.d.ts +2 -1
  188. package/dist/failure/index.d.ts.map +1 -1
  189. package/dist/failure/index.js +9 -17
  190. package/dist/failure/index.js.map +1 -1
  191. package/dist/failure/recorder.d.ts +1 -1
  192. package/dist/failure/recorder.d.ts.map +1 -1
  193. package/dist/gates/checker-gate.d.ts +2 -1
  194. package/dist/gates/checker-gate.d.ts.map +1 -1
  195. package/dist/gates/checker-gate.js +3 -2
  196. package/dist/gates/checker-gate.js.map +1 -1
  197. package/dist/gates/command.d.ts +10 -4
  198. package/dist/gates/command.d.ts.map +1 -1
  199. package/dist/gates/command.js +36 -62
  200. package/dist/gates/command.js.map +1 -1
  201. package/dist/gates/contract.d.ts.map +1 -1
  202. package/dist/gates/contract.js +0 -4
  203. package/dist/gates/contract.js.map +1 -1
  204. package/dist/gates/definitions.d.ts +2 -1
  205. package/dist/gates/definitions.d.ts.map +1 -1
  206. package/dist/gates/definitions.js +6 -5
  207. package/dist/gates/definitions.js.map +1 -1
  208. package/dist/gates/index.d.ts +0 -4
  209. package/dist/gates/index.d.ts.map +1 -1
  210. package/dist/gates/index.js +1 -6
  211. package/dist/gates/index.js.map +1 -1
  212. package/dist/gates/performance.d.ts.map +1 -1
  213. package/dist/gates/performance.js +0 -4
  214. package/dist/gates/performance.js.map +1 -1
  215. package/dist/gates/runner.d.ts +1 -1
  216. package/dist/gates/runner.js +1 -1
  217. package/dist/gates/security.d.ts +1 -1
  218. package/dist/gates/security.d.ts.map +1 -1
  219. package/dist/gates/security.js +5 -6
  220. package/dist/gates/security.js.map +1 -1
  221. package/dist/gates/types.d.ts +14 -17
  222. package/dist/gates/types.d.ts.map +1 -1
  223. package/dist/gates/types.js.map +1 -1
  224. package/dist/hooks/bootstrap.d.ts +6 -4
  225. package/dist/hooks/bootstrap.d.ts.map +1 -1
  226. package/dist/hooks/bootstrap.js +25 -13
  227. package/dist/hooks/bootstrap.js.map +1 -1
  228. package/dist/hooks/config.d.ts +6 -2
  229. package/dist/hooks/config.d.ts.map +1 -1
  230. package/dist/hooks/config.js +5 -1
  231. package/dist/hooks/config.js.map +1 -1
  232. package/dist/hooks/index.d.ts +1 -1
  233. package/dist/hooks/index.d.ts.map +1 -1
  234. package/dist/hooks/index.js +0 -15
  235. package/dist/hooks/index.js.map +1 -1
  236. package/dist/hooks/pipeline.d.ts.map +1 -1
  237. package/dist/hooks/pipeline.js +2 -4
  238. package/dist/hooks/pipeline.js.map +1 -1
  239. package/dist/hooks/registry.d.ts +11 -8
  240. package/dist/hooks/registry.d.ts.map +1 -1
  241. package/dist/hooks/registry.js +22 -7
  242. package/dist/hooks/registry.js.map +1 -1
  243. package/dist/hooks/types.d.ts +23 -7
  244. package/dist/hooks/types.d.ts.map +1 -1
  245. package/dist/index.d.ts +4 -4
  246. package/dist/index.d.ts.map +1 -1
  247. package/dist/index.js +3 -4
  248. package/dist/index.js.map +1 -1
  249. package/dist/knowledge/audit-scoring.d.ts +87 -0
  250. package/dist/knowledge/audit-scoring.d.ts.map +1 -0
  251. package/dist/knowledge/audit-scoring.js +470 -0
  252. package/dist/knowledge/audit-scoring.js.map +1 -0
  253. package/dist/knowledge/audit.d.ts +11 -59
  254. package/dist/knowledge/audit.d.ts.map +1 -1
  255. package/dist/knowledge/audit.js +47 -522
  256. package/dist/knowledge/audit.js.map +1 -1
  257. package/dist/knowledge/import.d.ts.map +1 -1
  258. package/dist/knowledge/import.js +2 -0
  259. package/dist/knowledge/import.js.map +1 -1
  260. package/dist/knowledge/index-generator.d.ts.map +1 -1
  261. package/dist/knowledge/index-generator.js +4 -5
  262. package/dist/knowledge/index-generator.js.map +1 -1
  263. package/dist/knowledge/index.d.ts +3 -2
  264. package/dist/knowledge/index.d.ts.map +1 -1
  265. package/dist/knowledge/index.js +3 -16
  266. package/dist/knowledge/index.js.map +1 -1
  267. package/dist/knowledge/ingest.js +1 -1
  268. package/dist/knowledge/ingest.js.map +1 -1
  269. package/dist/knowledge/lifecycle.d.ts.map +1 -1
  270. package/dist/knowledge/lifecycle.js +4 -2
  271. package/dist/knowledge/lifecycle.js.map +1 -1
  272. package/dist/knowledge/migration.d.ts +3 -0
  273. package/dist/knowledge/migration.d.ts.map +1 -1
  274. package/dist/knowledge/migration.js +8 -2
  275. package/dist/knowledge/migration.js.map +1 -1
  276. package/dist/knowledge/query.d.ts +6 -0
  277. package/dist/knowledge/query.d.ts.map +1 -1
  278. package/dist/knowledge/query.js +8 -2
  279. package/dist/knowledge/query.js.map +1 -1
  280. package/dist/knowledge/reference-tracker.d.ts.map +1 -1
  281. package/dist/knowledge/reference-tracker.js +4 -6
  282. package/dist/knowledge/reference-tracker.js.map +1 -1
  283. package/dist/knowledge/store.d.ts +22 -16
  284. package/dist/knowledge/store.d.ts.map +1 -1
  285. package/dist/knowledge/store.js +53 -12
  286. package/dist/knowledge/store.js.map +1 -1
  287. package/dist/knowledge/tree-walker.d.ts +19 -0
  288. package/dist/knowledge/tree-walker.d.ts.map +1 -0
  289. package/dist/knowledge/tree-walker.js +30 -0
  290. package/dist/knowledge/tree-walker.js.map +1 -0
  291. package/dist/knowledge/types.d.ts +16 -0
  292. package/dist/knowledge/types.d.ts.map +1 -1
  293. package/dist/monitoring/traces.d.ts +5 -3
  294. package/dist/monitoring/traces.d.ts.map +1 -1
  295. package/dist/monitoring/traces.js +19 -5
  296. package/dist/monitoring/traces.js.map +1 -1
  297. package/dist/test-setup/jsonl-fake-fs.d.ts +30 -0
  298. package/dist/test-setup/jsonl-fake-fs.d.ts.map +1 -0
  299. package/dist/test-setup/jsonl-fake-fs.js +49 -0
  300. package/dist/test-setup/jsonl-fake-fs.js.map +1 -0
  301. package/dist/tools/index.d.ts +4 -1
  302. package/dist/tools/index.d.ts.map +1 -1
  303. package/dist/tools/index.js +7 -15
  304. package/dist/tools/index.js.map +1 -1
  305. package/dist/types/checkpoint.d.ts +5 -0
  306. package/dist/types/checkpoint.d.ts.map +1 -1
  307. package/dist/types/passes-gate.d.ts +0 -13
  308. package/dist/types/passes-gate.d.ts.map +1 -1
  309. package/dist/types/project-config.d.ts +18 -0
  310. package/dist/types/project-config.d.ts.map +1 -1
  311. package/dist/types/trace.d.ts +13 -4
  312. package/dist/types/trace.d.ts.map +1 -1
  313. package/dist/types/trace.js +4 -3
  314. package/dist/types/trace.js.map +1 -1
  315. package/dist/utils/exec.d.ts +4 -27
  316. package/dist/utils/exec.d.ts.map +1 -1
  317. package/dist/utils/exec.js +4 -94
  318. package/dist/utils/exec.js.map +1 -1
  319. package/dist/utils/jsonl.d.ts +17 -7
  320. package/dist/utils/jsonl.d.ts.map +1 -1
  321. package/dist/utils/jsonl.js +144 -17
  322. package/dist/utils/jsonl.js.map +1 -1
  323. package/dist/utils/numeric-flag.d.ts +44 -0
  324. package/dist/utils/numeric-flag.d.ts.map +1 -0
  325. package/dist/utils/numeric-flag.js +62 -0
  326. package/dist/utils/numeric-flag.js.map +1 -0
  327. package/package.json +1 -1
  328. package/src/CONTEXT.md +5 -2
  329. package/src/__tests__/acceptance-gate.test.ts +7 -4
  330. package/src/__tests__/checker-extra.test.ts +3 -2
  331. package/src/__tests__/checker.test.ts +4 -3
  332. package/src/__tests__/checkpoint.test.ts +26 -9
  333. package/src/__tests__/context-files-resolution.test.ts +29 -15
  334. package/src/__tests__/contract-gate.test.ts +19 -40
  335. package/src/__tests__/error-classifier.test.ts +1 -1
  336. package/src/__tests__/exec.test.ts +45 -107
  337. package/src/__tests__/failure-recorder.test.ts +7 -5
  338. package/src/__tests__/hooks-pipeline.test.ts +34 -47
  339. package/src/__tests__/layering.test.ts +217 -25
  340. package/src/__tests__/passes-gate.test.ts +9 -4
  341. package/src/__tests__/performance-gate.test.ts +3 -15
  342. package/src/__tests__/project-config-loader-extra.test.ts +31 -57
  343. package/src/__tests__/project-config-loader.test.ts +6 -3
  344. package/src/__tests__/public-exports.test.ts +27 -6
  345. package/src/__tests__/public-type-surface.test.ts +360 -0
  346. package/src/__tests__/public-value-surface.test.ts +182 -0
  347. package/src/__tests__/review-gate.test.ts +4 -6
  348. package/src/__tests__/security-gate.test.ts +5 -20
  349. package/src/__tests__/skip-semantics.test.ts +5 -3
  350. package/src/__tests__/spec-validator.test.ts +3 -2
  351. package/src/__tests__/sub-barrels-explicit.test.ts +87 -0
  352. package/src/__tests__/test-fixture-hygiene.test.ts +53 -0
  353. package/src/__tests__/trace-analyzer-extra.test.ts +5 -3
  354. package/src/__tests__/trace-analyzer.test.ts +5 -3
  355. package/src/__tests__/trace-collector.test.ts +5 -3
  356. package/src/agents/index.ts +13 -2
  357. package/src/cli/__tests__/gate-command.test.ts +162 -0
  358. package/src/cli/commands/CONTEXT.md +29 -5
  359. package/src/cli/commands/__tests__/acceptance.test.ts +24 -25
  360. package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +15 -0
  361. package/src/cli/commands/__tests__/check-drift.test.ts +42 -1
  362. package/src/cli/commands/__tests__/check-read-count.test.ts +167 -0
  363. package/src/cli/commands/__tests__/check-skip-output.test.ts +4 -1
  364. package/src/cli/commands/__tests__/check.test.ts +43 -36
  365. package/src/cli/commands/__tests__/command-gate-dead-options.test.ts +60 -0
  366. package/src/cli/commands/__tests__/command.test.ts +33 -17
  367. package/src/cli/commands/__tests__/constraints-retire.test.ts +52 -0
  368. package/src/cli/commands/__tests__/context-syncer.test.ts +141 -0
  369. package/src/cli/commands/__tests__/contract.test.ts +20 -20
  370. package/src/cli/commands/__tests__/failure.test.ts +5 -4
  371. package/src/cli/commands/__tests__/gate-decision.ts +30 -0
  372. package/src/cli/commands/__tests__/init-dead-options.test.ts +50 -0
  373. package/src/cli/commands/__tests__/init-governance-io.test.ts +139 -0
  374. package/src/cli/commands/__tests__/init-injection.test.ts +3 -3
  375. package/src/cli/commands/__tests__/init-ondisk.test.ts +867 -0
  376. package/src/cli/commands/__tests__/init.test.ts +82 -6
  377. package/src/cli/commands/__tests__/knowledge-audit-threshold.test.ts +146 -0
  378. package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +1 -1
  379. package/src/cli/commands/__tests__/knowledge-tone-mapping.test.ts +244 -0
  380. package/src/cli/commands/__tests__/knowledge-view.test.ts +839 -0
  381. package/src/cli/commands/__tests__/knowledge.test.ts +33 -15
  382. package/src/cli/commands/__tests__/numeric-flag-assembly.test.ts +368 -0
  383. package/src/cli/commands/__tests__/passes-gate-dead-options.test.ts +89 -1
  384. package/src/cli/commands/__tests__/performance.test.ts +19 -21
  385. package/src/cli/commands/__tests__/posteval-plan.test.ts +5 -3
  386. package/src/cli/commands/__tests__/pre-push-hook.test.ts +181 -0
  387. package/src/cli/commands/__tests__/project-path-anchoring.test.ts +95 -2
  388. package/src/cli/commands/__tests__/project-path-convention.test.ts +103 -25
  389. package/src/cli/commands/__tests__/registry.test.ts +2 -0
  390. package/src/cli/commands/__tests__/report.test.ts +23 -1
  391. package/src/cli/commands/__tests__/review.test.ts +10 -17
  392. package/src/cli/commands/__tests__/scaffold.test.ts +505 -0
  393. package/src/cli/commands/__tests__/security-default-scan.test.ts +75 -0
  394. package/src/cli/commands/__tests__/security.test.ts +21 -24
  395. package/src/cli/commands/__tests__/spec-baseline-check.test.ts +3 -2
  396. package/src/cli/commands/__tests__/spec-baseline-index-cap.test.ts +131 -0
  397. package/src/cli/commands/__tests__/spec-baseline-read-count.test.ts +325 -0
  398. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +19 -4
  399. package/src/cli/commands/__tests__/sync-docs-context-drift.test.ts +232 -0
  400. package/src/cli/commands/__tests__/sync-docs-module-order.test.ts +111 -0
  401. package/src/cli/commands/__tests__/sync-docs-read-count.test.ts +192 -0
  402. package/src/cli/commands/__tests__/sync-docs.test.ts +3 -2
  403. package/src/cli/commands/__tests__/validate.test.ts +1 -48
  404. package/src/cli/commands/acceptance.ts +24 -29
  405. package/src/cli/commands/check.ts +40 -17
  406. package/src/cli/commands/command.ts +9 -8
  407. package/src/cli/commands/constraints-retire.ts +57 -70
  408. package/src/cli/commands/contract.ts +44 -47
  409. package/src/cli/commands/definitions.ts +12 -6
  410. package/src/cli/commands/failure.ts +16 -3
  411. package/src/cli/commands/init.ts +242 -389
  412. package/src/cli/commands/knowledge-view.ts +224 -0
  413. package/src/cli/commands/knowledge.ts +643 -448
  414. package/src/cli/commands/passes-gate.ts +12 -3
  415. package/src/cli/commands/performance.ts +31 -38
  416. package/src/cli/commands/report.ts +3 -2
  417. package/src/cli/commands/review.ts +22 -28
  418. package/src/cli/commands/scaffold-templates.ts +406 -0
  419. package/src/cli/commands/scaffold.ts +302 -0
  420. package/src/cli/commands/security.ts +27 -23
  421. package/src/cli/commands/spec-baseline-check.ts +158 -33
  422. package/src/cli/commands/sync-docs/__tests__/context-export-surface.test.ts +102 -0
  423. package/src/cli/commands/sync-docs/__tests__/module-descriptions.test.ts +196 -0
  424. package/src/cli/commands/sync-docs/context-syncer.ts +137 -31
  425. package/src/cli/commands/sync-docs/index.ts +103 -28
  426. package/src/cli/commands/sync-docs/project-reader.ts +51 -7
  427. package/src/cli/commands/validate.ts +1 -104
  428. package/src/cli/gate-command.ts +87 -0
  429. package/src/completion-checkers/index.ts +10 -1
  430. package/src/context/CONTEXT.md +2 -1
  431. package/src/context/__tests__/knowledge-injector.test.ts +27 -0
  432. package/src/context/__tests__/session-manager.test.ts +19 -12
  433. package/src/context/knowledge-injector.ts +14 -7
  434. package/src/core/CONTEXT.md +10 -7
  435. package/src/core/__tests__/effective-set.test.ts +36 -60
  436. package/src/core/constraints/__tests__/context-reconcile.test.ts +246 -0
  437. package/src/core/constraints/__tests__/run-env.test.ts +377 -0
  438. package/src/core/constraints/__tests__/triggers.test.ts +70 -0
  439. package/src/core/constraints/agent-prompt-renderer.ts +1 -1
  440. package/src/core/constraints/capabilities-reconcile.ts +28 -4
  441. package/src/core/constraints/check-cache.ts +1 -1
  442. package/src/core/constraints/checker.ts +30 -14
  443. package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +8 -3
  444. package/src/core/constraints/checkers/__tests__/governance-presence.test.ts +16 -7
  445. package/src/core/constraints/checkers/__tests__/no-hardcoded-credentials.test.ts +2 -1
  446. package/src/core/constraints/checkers/capability-sync.ts +27 -28
  447. package/src/core/constraints/checkers/context-doc-sync.ts +9 -7
  448. package/src/core/constraints/checkers/docs-freshness.ts +28 -33
  449. package/src/core/constraints/checkers/governance-presence.ts +10 -6
  450. package/src/core/constraints/checkers/types.ts +14 -8
  451. package/src/core/constraints/context-builder.ts +35 -26
  452. package/src/core/constraints/context-reconcile.ts +200 -0
  453. package/src/core/constraints/doc-freshness/__tests__/runner.test.ts +25 -0
  454. package/src/core/constraints/doc-freshness/runner.ts +18 -4
  455. package/src/core/constraints/git-evidence.ts +1 -1
  456. package/src/core/constraints/injection-drift.ts +19 -8
  457. package/src/core/constraints/run-env.ts +207 -0
  458. package/src/core/constraints/triggers.ts +33 -0
  459. package/src/core/effective-constraints.ts +17 -6
  460. package/src/core/effective-set.ts +8 -24
  461. package/src/core/index.ts +2 -1
  462. package/src/core/project-config-loader.ts +54 -102
  463. package/src/core/validators/__tests__/http-retry-sleep.test.ts +231 -0
  464. package/src/core/validators/check-handlers/http.ts +18 -7
  465. package/src/core/validators/index.ts +0 -1
  466. package/src/core/validators/passes-gate.ts +2 -3
  467. package/src/failure/CONTEXT.md +2 -1
  468. package/src/failure/classifier.ts +2 -2
  469. package/src/failure/index.ts +14 -2
  470. package/src/failure/recorder.ts +1 -1
  471. package/src/gates/CONTEXT.md +13 -7
  472. package/src/gates/__tests__/checker-gate.test.ts +1 -1
  473. package/src/gates/__tests__/command-single-predicate.test.ts +135 -0
  474. package/src/gates/__tests__/command.test.ts +8 -15
  475. package/src/gates/__tests__/contract.test.ts +5 -5
  476. package/src/gates/__tests__/gate-interface.test.ts +0 -17
  477. package/src/gates/__tests__/performance.test.ts +0 -13
  478. package/src/gates/__tests__/review.test.ts +0 -1
  479. package/src/gates/__tests__/runner.test.ts +1 -1
  480. package/src/gates/__tests__/security-default-scan.test.ts +97 -0
  481. package/src/gates/__tests__/security.test.ts +0 -13
  482. package/src/gates/checker-gate.ts +3 -2
  483. package/src/gates/command.ts +51 -74
  484. package/src/gates/contract.ts +0 -5
  485. package/src/gates/definitions.ts +6 -5
  486. package/src/gates/index.ts +0 -5
  487. package/src/gates/performance.ts +1 -6
  488. package/src/gates/runner.ts +1 -1
  489. package/src/gates/security.ts +9 -11
  490. package/src/gates/types.ts +20 -23
  491. package/src/hooks/CONTEXT.md +10 -9
  492. package/src/hooks/__tests__/bootstrap.test.ts +29 -6
  493. package/src/hooks/__tests__/pipeline.test.ts +28 -17
  494. package/src/hooks/__tests__/registry.test.ts +51 -3
  495. package/src/hooks/bootstrap.ts +35 -15
  496. package/src/hooks/config.ts +6 -2
  497. package/src/hooks/index.ts +10 -1
  498. package/src/hooks/pipeline.ts +4 -6
  499. package/src/hooks/registry.ts +31 -12
  500. package/src/hooks/types.ts +24 -7
  501. package/src/index.ts +1 -3
  502. package/src/knowledge/CONTEXT.md +14 -7
  503. package/src/knowledge/__tests__/audit-labels.test.ts +8 -5
  504. package/src/knowledge/__tests__/audit-scoring.test.ts +230 -0
  505. package/src/knowledge/__tests__/audit-write-count.test.ts +131 -0
  506. package/src/knowledge/__tests__/audit.test.ts +291 -208
  507. package/src/knowledge/__tests__/doctor.test.ts +3 -2
  508. package/src/knowledge/__tests__/ingest.test.ts +3 -2
  509. package/src/knowledge/__tests__/lifecycle.test.ts +57 -2
  510. package/src/knowledge/__tests__/migration.test.ts +35 -6
  511. package/src/knowledge/__tests__/query.test.ts +3 -2
  512. package/src/knowledge/__tests__/reference-tracker.test.ts +23 -2
  513. package/src/knowledge/__tests__/store.test.ts +149 -4
  514. package/src/knowledge/__tests__/tree-walker.test.ts +132 -0
  515. package/src/knowledge/audit-scoring.ts +589 -0
  516. package/src/knowledge/audit.ts +55 -599
  517. package/src/knowledge/import.ts +2 -0
  518. package/src/knowledge/index-generator.ts +4 -6
  519. package/src/knowledge/index.ts +27 -2
  520. package/src/knowledge/ingest.ts +1 -1
  521. package/src/knowledge/lifecycle.ts +5 -2
  522. package/src/knowledge/migration.ts +8 -2
  523. package/src/knowledge/query.ts +8 -1
  524. package/src/knowledge/reference-tracker.ts +5 -7
  525. package/src/knowledge/store.ts +75 -16
  526. package/src/knowledge/tree-walker.ts +29 -0
  527. package/src/knowledge/types.ts +19 -0
  528. package/src/monitoring/CONTEXT.md +5 -2
  529. package/src/monitoring/__tests__/context-tracker.test.ts +16 -8
  530. package/src/monitoring/__tests__/trace-file-anchoring.test.ts +80 -0
  531. package/src/monitoring/traces.ts +19 -5
  532. package/src/release/CONTEXT.md +1 -1
  533. package/src/release/__tests__/integrity.test.ts +21 -12
  534. package/src/test-setup/__tests__/jsonl-fake-fs.test.ts +98 -0
  535. package/src/test-setup/jsonl-fake-fs.ts +59 -0
  536. package/src/tools/index.ts +5 -1
  537. package/src/types/checkpoint.ts +6 -0
  538. package/src/types/passes-gate.ts +0 -17
  539. package/src/types/project-config.ts +21 -0
  540. package/src/types/trace.ts +14 -4
  541. package/src/utils/__tests__/file-walk.test.ts +3 -2
  542. package/src/utils/__tests__/jsonl-bounded-read.test.ts +106 -0
  543. package/src/utils/__tests__/jsonl-skip-disposition.test.ts +10 -6
  544. package/src/utils/__tests__/jsonl.test.ts +61 -14
  545. package/src/utils/__tests__/numeric-flag.test.ts +80 -0
  546. package/src/utils/__tests__/package-version.test.ts +2 -2
  547. package/src/utils/exec.ts +4 -64
  548. package/src/utils/jsonl.ts +159 -20
  549. package/src/utils/numeric-flag.ts +64 -0
  550. package/dist/failure/types.d.ts +0 -9
  551. package/dist/failure/types.d.ts.map +0 -1
  552. package/dist/failure/types.js +0 -25
  553. package/dist/failure/types.js.map +0 -1
  554. package/dist/gates/effective-gates.d.ts +0 -30
  555. package/dist/gates/effective-gates.d.ts.map +0 -1
  556. package/dist/gates/effective-gates.js +0 -81
  557. package/dist/gates/effective-gates.js.map +0 -1
  558. package/src/failure/types.ts +0 -9
  559. package/src/gates/__tests__/effective-gates.test.ts +0 -77
  560. package/src/gates/effective-gates.ts +0 -99
@@ -4,6 +4,7 @@
4
4
 
5
5
  import { acceptance, listAcceptanceCriteria } from '../acceptance';
6
6
  import { captureIO, type CapturingIO } from '../../command-contract';
7
+ import { decide, fakeGate, throwingGate } from './gate-decision';
7
8
  import * as fs from 'fs/promises';
8
9
  import { SpecAcceptanceGate } from '../../../gates/acceptance';
9
10
 
@@ -14,7 +15,7 @@ jest.mock('fs/promises', () => ({
14
15
 
15
16
  jest.mock('../../../gates/acceptance', () => ({
16
17
  SpecAcceptanceGate: jest.fn().mockImplementation(() => ({
17
- check: jest.fn(),
18
+ evaluate: jest.fn(),
18
19
  })),
19
20
  }));
20
21
 
@@ -46,30 +47,31 @@ describe('acceptance command', () => {
46
47
 
47
48
  describe('acceptance', () => {
48
49
  it('should print success when check passes', async () => {
49
- const mockCheck = jest.fn().mockResolvedValue({
50
- passed: true,
51
- message: 'ok',
52
- details: { checkedCriteria: 5, totalCriteria: 5 },
53
- });
54
- MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
50
+ MockGate.mockImplementation(() =>
51
+ fakeGate(decide('acceptance', true, 'ok', { checkedCriteria: 5, totalCriteria: 5 })) as any
52
+ );
55
53
 
56
54
  const result = await acceptance({}, io);
57
55
 
58
56
  expect(io.outText()).toContain('验收标准检查通过');
57
+ expect(io.outText()).toContain(' 通过项: 5');
58
+ expect(io.outText()).toContain(' 总项数: 5');
59
59
  expect(result.kind).toBe('ok');
60
60
  });
61
61
 
62
62
  it('should print failure and exit 1 when check fails', async () => {
63
- const mockCheck = jest.fn().mockResolvedValue({
64
- passed: false,
65
- message: 'criteria not met',
66
- details: { uncheckedCriteria: ['criteria-1', 'criteria-2'] },
67
- });
68
- MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
63
+ MockGate.mockImplementation(() =>
64
+ fakeGate(
65
+ decide('acceptance', false, 'criteria not met', {
66
+ uncheckedCriteria: ['criteria-1', 'criteria-2'],
67
+ })
68
+ ) as any
69
+ );
69
70
 
70
71
  const result = await acceptance({}, io);
71
72
 
72
73
  expect(io.outText()).toContain('验收标准检查失败');
74
+ expect(io.outText()).toContain(' - criteria-1');
73
75
  expect(result).toEqual({
74
76
  kind: 'fail',
75
77
  reason: 'acceptance gate denied: criteria not met',
@@ -77,8 +79,7 @@ describe('acceptance command', () => {
77
79
  });
78
80
 
79
81
  it('should handle errors and exit 1', async () => {
80
- const mockCheck = jest.fn().mockRejectedValue(new Error('gate error'));
81
- MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
82
+ MockGate.mockImplementation(() => throwingGate(new Error('gate error')) as any);
82
83
 
83
84
  const result = await acceptance({}, io);
84
85
 
@@ -90,30 +91,29 @@ describe('acceptance command', () => {
90
91
  });
91
92
 
92
93
  it('should use cwd as default projectPath', async () => {
93
- const mockCheck = jest.fn().mockResolvedValue({ passed: true, message: 'ok' });
94
- MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
94
+ const gate = fakeGate(decide('acceptance', true, 'ok'));
95
+ MockGate.mockImplementation(() => gate as any);
95
96
 
96
97
  await acceptance({}, io);
97
98
 
98
- expect(mockCheck).toHaveBeenCalledWith(
99
+ expect(gate.evaluate).toHaveBeenCalledWith(
99
100
  expect.objectContaining({ projectPath: process.cwd() }),
100
101
  );
101
102
  });
102
103
 
103
104
  it('should pass taskId to gate', async () => {
104
- const mockCheck = jest.fn().mockResolvedValue({ passed: true, message: 'ok' });
105
- MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
105
+ const gate = fakeGate(decide('acceptance', true, 'ok'));
106
+ MockGate.mockImplementation(() => gate as any);
106
107
 
107
108
  await acceptance({ taskId: 'TASK-001' }, io);
108
109
 
109
- expect(mockCheck).toHaveBeenCalledWith(
110
+ expect(gate.evaluate).toHaveBeenCalledWith(
110
111
  expect.objectContaining({ taskId: 'TASK-001' }),
111
112
  );
112
113
  });
113
114
 
114
115
  it('should set e2eTestCommand when runE2e is true', async () => {
115
- const mockCheck = jest.fn().mockResolvedValue({ passed: true, message: 'ok' });
116
- MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
116
+ MockGate.mockImplementation(() => fakeGate(decide('acceptance', true, 'ok')) as any);
117
117
 
118
118
  await acceptance({ runE2e: true }, io);
119
119
 
@@ -123,8 +123,7 @@ describe('acceptance command', () => {
123
123
  });
124
124
 
125
125
  it('should not set e2eTestCommand when runE2e is false', async () => {
126
- const mockCheck = jest.fn().mockResolvedValue({ passed: true, message: 'ok' });
127
- MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
126
+ MockGate.mockImplementation(() => fakeGate(decide('acceptance', true, 'ok')) as any);
128
127
 
129
128
  await acceptance({}, io);
130
129
 
@@ -10,6 +10,7 @@
10
10
  */
11
11
 
12
12
  import * as fs from 'fs';
13
+ import * as os from 'os';
13
14
  import * as path from 'path';
14
15
  import { spawnSync } from 'child_process';
15
16
 
@@ -104,4 +105,18 @@ smoke('端到端:真实命令经 bin 映射后的对外退出码', () => {
104
105
  expect(r.status).toBe(1);
105
106
  expect(r.stderr).toContain('未知子命令: bogus');
106
107
  });
108
+
109
+ // harness#152:数值旗帜的脏输入此前静默 NaN 穿透(退出码 0、少一道判定),现 fail-loud
110
+ it('audit 脏阈值 → 1(返回 usage-error,stdout 不出报告)', () => {
111
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-k152-bin-'));
112
+ try {
113
+ const r = run(['kb', 'audit', '--dir', dir, '--threshold', 'abc']);
114
+ expect(r.status).toBe(1);
115
+ expect(r.stderr).toContain('阈值');
116
+ expect(r.stdout).not.toContain('阈值 NaN');
117
+ expect(r.stdout).toBe('');
118
+ } finally {
119
+ fs.rmSync(dir, { recursive: true, force: true });
120
+ }
121
+ });
107
122
  });
@@ -22,8 +22,11 @@ jest.mock('../../../core/constraints/checker', () => ({
22
22
  }),
23
23
  }));
24
24
 
25
+ // 命令 per-run 锚根构造收集器(harness#139):构造替身即拦截 trace 落盘
25
26
  jest.mock('../../../monitoring/traces', () => ({
26
- getTraceCollector: jest.fn(() => ({ record: jest.fn() })),
27
+ TraceCollector: jest.fn(function () {
28
+ return { record: jest.fn() };
29
+ }),
27
30
  }));
28
31
 
29
32
  jest.mock('../../../utils/exec', () => ({
@@ -114,4 +117,42 @@ describe('check 命令注入漂移警告', () => {
114
117
  expect(outputText()).not.toContain('注入漂移');
115
118
  expect(result.kind).toBe('ok');
116
119
  });
120
+
121
+ // ── 生效集来源与漂移比对的同一性(ADR-0023 步骤 4.5)──────────────────
122
+ // 改前 CLI 给 -p 塞了缺省值 'standard',「没传」与「传了」不可区分,
123
+ // 于是「--preset 仅在无自定义配置时覆盖」这条规则恒被触发:项目 config.yml 的
124
+ // preset 静默失效,而漂移侧按 config.yml 算 → 两边稳定对不上(该报的漂移不报)。
125
+
126
+ it('config.yml 的 preset 不再被 CLI 缺省顶掉:不带 -p 按 relaxed 执法且零漂移', async () => {
127
+ const root = createProjectFixture({
128
+ name: 'harness-check-drift-relaxed',
129
+ config: 'preset: relaxed\n',
130
+ });
131
+ writeClaudeMd(root, REAL_VERSION); // getEffectiveConstraints(root) 走 config.yml = relaxed
132
+
133
+ const result = await check({ staged: false, projectPath: root }, io);
134
+
135
+ // relaxed 裁掉的内置条目会出现在「已禁用约束」行——改前 CLI 塞了 standard,这行根本不出现
136
+ const output = outputText();
137
+ expect(output).toContain('已禁用约束:');
138
+ expect(output).not.toContain('内容漂移');
139
+ expect(output).toContain('约束检查通过');
140
+ expect(result.kind).toBe('ok');
141
+ });
142
+
143
+ it('显式 -p 时,漂移比对用的就是本 run 实际执法的那份', async () => {
144
+ const root = createProjectFixture({
145
+ name: 'harness-check-drift-override',
146
+ config: 'preset: relaxed\n',
147
+ });
148
+ writeClaudeMd(root, REAL_VERSION); // 段里是 relaxed 的少数条目
149
+
150
+ await check({ preset: 'standard', staged: false, projectPath: root }, io);
151
+
152
+ // standard 不裁条目(无「已禁用约束」行),且期望段 = 本 run 执法的 standard
153
+ // → 段里条目只少不多,报「缺失 N 条 / 多余 0 条」
154
+ const output = outputText();
155
+ expect(output).not.toContain('已禁用约束:');
156
+ expect(output).toMatch(/内容漂移: 缺失 [1-9]\d* 条 \/ 多余 0 条/);
157
+ });
117
158
  });
@@ -0,0 +1,167 @@
1
+ /**
2
+ * 一次 check 运行内「每个项目文件至多读一次」计数闸(ADR-0023 决策 5 ①)
3
+ *
4
+ * 形状照 `check.test.ts:78-106` 的 `recordingEvidence()`:真执行、只在中间记一笔,
5
+ * 不改任何行为。这里数的不是「观察口被请求几次」(memo 在观察面上,请求多次本就应当),
6
+ * 而是**底层文件读取发生了几次** —— 那才是本 ADR 承诺的口径。
7
+ *
8
+ * 夹具必须让两个吃能力表的 checker 都真评估(触发条件 module_modification +
9
+ * 有 CAPABILITIES.md + 有自定义约束文件),否则闸门是空跑的。
10
+ */
11
+
12
+ import * as fs from 'fs';
13
+ import * as os from 'os';
14
+ import * as path from 'path';
15
+ import { execFileSync } from 'child_process';
16
+ import { check } from '../check';
17
+ import { captureIO, type CapturingIO } from '../../command-contract';
18
+
19
+ type ReadCall = { file: string; via: 'readFileSync' | 'open' };
20
+
21
+ // 计数器挂在 globalThis:jest.mock 工厂会被提前调用,闭包外提会撞 TDZ
22
+ jest.mock('fs', () => {
23
+ const actual = jest.requireActual<typeof import('fs')>('fs');
24
+ const g = globalThis as unknown as { __ADR0023_READS__?: ReadCall[] };
25
+ if (!g.__ADR0023_READS__) g.__ADR0023_READS__ = [];
26
+ const bucket = g.__ADR0023_READS__;
27
+ const name = (p: unknown): string =>
28
+ Buffer.isBuffer(p) ? p.toString('utf8') : String(p);
29
+ const passthrough = (fn: string) =>
30
+ (actual as unknown as Record<string, (...args: unknown[]) => unknown>)[fn];
31
+
32
+ return {
33
+ ...actual,
34
+ readFileSync: jest.fn((p: unknown, ...rest: unknown[]) => {
35
+ bucket.push({ file: name(p), via: 'readFileSync' });
36
+ return passthrough('readFileSync')(p, ...rest);
37
+ }),
38
+ // 有界读走 open/fstat/read:只记只读打开(写 trace 的 append 不算读取)
39
+ openSync: jest.fn((p: unknown, flags?: unknown, ...rest: unknown[]) => {
40
+ if (flags === undefined || flags === 'r' || flags === 'rs') {
41
+ bucket.push({ file: name(p), via: 'open' });
42
+ }
43
+ return passthrough('openSync')(p, flags, ...rest);
44
+ }),
45
+ };
46
+ });
47
+
48
+ const reads = () =>
49
+ ((globalThis as unknown as { __ADR0023_READS__?: ReadCall[] }).__ADR0023_READS__ ||= []);
50
+
51
+ jest.mock('chalk', () => ({
52
+ blue: (s: string) => s,
53
+ yellow: (s: string) => s,
54
+ green: (s: string) => s,
55
+ gray: (s: string) => s,
56
+ red: (s: string) => s,
57
+ }));
58
+
59
+ function git(dir: string, ...args: string[]): void {
60
+ execFileSync('git', args, { cwd: dir, stdio: 'pipe' });
61
+ }
62
+
63
+ function write(dir: string, rel: string, content: string): void {
64
+ const target = path.join(dir, rel);
65
+ fs.mkdirSync(path.dirname(target), { recursive: true });
66
+ fs.writeFileSync(target, content, 'utf-8');
67
+ }
68
+
69
+ const TABLE = ['| 能力 | 路径 | 说明 |', '|---|---|---|'];
70
+
71
+ /** 一个「两 checker 都会真评估」的项目:源码根下两个已登记文件 + 自定义约束 + 一条 trace */
72
+ function fixtureRepo(): string {
73
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-read-count-'));
74
+ git(dir, 'init', '-q');
75
+ git(dir, 'config', 'user.email', 'fixture@example.com');
76
+ git(dir, 'config', 'user.name', 'Fixture');
77
+ write(dir, 'README.md', '# fixture\n');
78
+ write(dir, '.harness/config.yml', 'preset: standard\n');
79
+ write(
80
+ dir,
81
+ '.harness/custom-constraints.yml',
82
+ 'custom_constraints:\n ab_custom_rule:\n level: guideline\n rule: 自定义规则\n message: 自定义提示\n trigger: manual\n'
83
+ );
84
+ write(
85
+ dir,
86
+ 'CAPABILITIES.md',
87
+ ['# CAPABILITIES.md', '', ...TABLE, '| 既有 | src/existing.ts | 在 |', '| 嵌套 | src/nested/deep.ts | 在 |'].join('\n') + '\n'
88
+ );
89
+ write(dir, 'AGENTS.md', '<!-- PRESERVE:governance -->\n## Governance Rules\n\n条款\n\n<!-- /PRESERVE:governance -->\n');
90
+ write(dir, 'src/existing.ts', 'export const a = 1;\n');
91
+ write(dir, 'src/nested/deep.ts', 'export const d = 1;\n');
92
+ write(dir, '.harness/logs/traces.log', '{"constraintId":"fixture","result":"pass"}\n');
93
+ git(dir, 'add', '.');
94
+ git(dir, 'commit', '-q', '-m', 'baseline');
95
+ // 改一个已登记的源文件并 stage → 触发条件 module_modification
96
+ write(dir, 'src/existing.ts', 'export const a = 2;\n');
97
+ git(dir, 'add', '--', 'src/existing.ts');
98
+ return dir;
99
+ }
100
+
101
+ describe('一次 check 的文件读取计数闸(ADR-0023 决策 5 ①)', () => {
102
+ let io: CapturingIO;
103
+ let dir: string;
104
+
105
+ /** 相对项目根的文件名 → 读取次数(只读语义的 readFileSync 与只读 open 都算一次) */
106
+ function readCounts(): Map<string, number> {
107
+ const counts = new Map<string, number>();
108
+ for (const call of reads()) {
109
+ const rel = path.relative(dir, call.file);
110
+ if (!rel || rel.startsWith('..')) continue; // 非本项目文件(模块加载等)不计
111
+ counts.set(rel, (counts.get(rel) ?? 0) + 1);
112
+ }
113
+ return counts;
114
+ }
115
+
116
+ beforeEach(() => {
117
+ // 原地清空:mock 工厂在装载时就captured了这个数组引用,换新数组计数会落到旧的去向(假绿)
118
+ reads().length = 0;
119
+ io = captureIO();
120
+ dir = fixtureRepo();
121
+ // trace 自 #139 起锚在 projectPath 上写入(本夹具内),写入不经读取计数器
122
+ });
123
+
124
+ afterEach(() => {
125
+ fs.rmSync(dir, { recursive: true, force: true });
126
+ });
127
+
128
+ it('整张读取表如实冻结:本次运行读了哪些项目文件、各读几次', async () => {
129
+ const result = await check({ staged: true, projectPath: dir }, io);
130
+ expect(result.kind).toBe('ok');
131
+ // 正对照:两个吃能力表的 checker 确实跑了(否则下面的断言是空跑)
132
+ expect(io.outText()).toContain('自定义约束: 1 条');
133
+ expect(io.outText()).toContain('触发条件: module_modification');
134
+
135
+ expect([...readCounts().entries()].sort()).toEqual(
136
+ [
137
+ // —— 一次运行一份:本 ADR 的承诺面 ——
138
+ ['.harness/config.yml', 1],
139
+ ['.harness/custom-constraints.yml', 1],
140
+ ['CAPABILITIES.md', 1],
141
+ ['.harness/logs/traces.log', 2],
142
+ // —— 已知例外(改动需明写理由)——
143
+ // traces.log = 2:head 50(智能提示的阈值判定,决策 3)与 tail 20(有无失败证据,
144
+ // 决策 4)是两个不同窗口的**有界**读;并成一个窗口就等于回到整读。
145
+ // AGENTS.md = 3 / CLAUDE.md = 2:决策表明示本 ADR 不处理治理文档的重复读
146
+ // (1.5–25KB,<1ms)。CLAUDE.md 在本夹具里并不存在,2 = 落点路由与旧落点兜底
147
+ // 各尝试读一次(读失败的尝试同样计入)。
148
+ ['AGENTS.md', 3],
149
+ ['CLAUDE.md', 2],
150
+ ].sort()
151
+ );
152
+ });
153
+
154
+ it('除上表点名例外外的项目文件,一次运行内一律至多读一次', async () => {
155
+ await check({ staged: true, projectPath: dir }, io);
156
+
157
+ const except = new Set([
158
+ path.join('.harness', 'logs', 'traces.log'), // 两个窗口的有界读,见上表理由
159
+ 'AGENTS.md', // 治理文档,ADR-0023 决策表明示不处理
160
+ 'CLAUDE.md',
161
+ ]);
162
+ const offenders = [...readCounts()].filter(
163
+ ([file, n]) => !except.has(file) && n > 1
164
+ );
165
+ expect(offenders).toEqual([]);
166
+ });
167
+ });
@@ -34,8 +34,11 @@ jest.mock('../../../core/constraints/checker', () => ({
34
34
  }),
35
35
  }));
36
36
 
37
+ // 命令 per-run 锚根构造收集器(harness#139):构造替身即拦截 trace 落盘
37
38
  jest.mock('../../../monitoring/traces', () => ({
38
- getTraceCollector: jest.fn(() => ({ record: jest.fn() })),
39
+ TraceCollector: jest.fn(function () {
40
+ return { record: jest.fn() };
41
+ }),
39
42
  }));
40
43
 
41
44
  // Mock effective-constraints(ADR-0001:check 经 getMergedConstraintsConfig 走生效集链路)
@@ -7,8 +7,8 @@
7
7
  * 真 ConstraintChecker,断言 CLI 输出形状与 CommandResult。
8
8
  * 取证次数经 createGitEvidence 的计数 runner 观测——执行仍走真 git,不 mock 子进程。
9
9
  *
10
- * trace 记录器由命令侧组合根接线(harness#88):套件把全局收集器重定向到临时文件,
11
- * 既不断言宿主仓写入,也不污染它。
10
+ * trace 记录器由命令侧组合根接线(harness#88)并锚根构造(#139):落点在本用例的
11
+ * 临时项目里(`<projectPath>/.harness/logs/traces.log`),宿主仓 .harness/ 不受影响。
12
12
  */
13
13
 
14
14
  import * as fs from 'fs';
@@ -18,7 +18,7 @@ import { execFileSync } from 'child_process';
18
18
 
19
19
  import { check, listLaws } from '../check';
20
20
  import { captureIO, type CapturingIO } from '../../command-contract';
21
- import { configureTraceCollector } from '../../../monitoring/traces';
21
+ import { DEFAULT_TRACE_FILE } from '../../../types/trace';
22
22
  import {
23
23
  createGitEvidence,
24
24
  realGitCommandRunner,
@@ -75,6 +75,23 @@ function passTraces(dir: string, lines = 1): void {
75
75
  );
76
76
  }
77
77
 
78
+ /**
79
+ * 本项目的 trace 落点记录(harness#139:命令侧收集器锚根构造,写的就是 `<projectPath>/DEFAULT_TRACE_FILE` 正本)
80
+ *
81
+ * 含 `passTraces()` 预置的种子行,只看命令自己写入的用例按行数切掉前缀。
82
+ */
83
+ function projectTraces(
84
+ dir: string
85
+ ): Array<{ constraintId: string; result: string; projectPath?: string; evidence?: string[] }> {
86
+ const file = path.join(dir, DEFAULT_TRACE_FILE);
87
+ if (!fs.existsSync(file)) return [];
88
+ return fs
89
+ .readFileSync(file, 'utf-8')
90
+ .split('\n')
91
+ .filter(Boolean)
92
+ .map(line => JSON.parse(line));
93
+ }
94
+
78
95
  /** 计数 git 证据:既记录证据方法请求,也记录实际 spawn 的 git 命令(执行走真 adapter) */
79
96
  function recordingEvidence(projectPath: string): {
80
97
  evidence: GitEvidence;
@@ -118,28 +135,9 @@ function stagedTestDeletion(): string {
118
135
 
119
136
  describe('check command(真 git fixture)', () => {
120
137
  let io: CapturingIO;
121
- let traceDir: string;
122
- let traceFile: string;
123
- let traceSeq = 0;
124
-
125
- beforeAll(() => {
126
- traceDir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-check-traces-'));
127
- });
128
-
129
- afterAll(() => {
130
- configureTraceCollector({});
131
- try {
132
- fs.rmSync(traceDir, { recursive: true, force: true });
133
- } catch {
134
- // ignore
135
- }
136
- });
137
138
 
138
139
  beforeEach(() => {
139
140
  io = captureIO();
140
- // 命令侧接线的是全局收集器(默认 cwd 相对写入):重定向到套件临时文件,宿主仓 .harness/ 不受影响
141
- traceFile = path.join(traceDir, `trace-${++traceSeq}.log`);
142
- configureTraceCollector({ traceFile });
143
141
  });
144
142
 
145
143
  describe('判定与输出形状', () => {
@@ -240,6 +238,22 @@ describe('check command(真 git fixture)', () => {
240
238
  expect(io.outText()).not.toContain('变更文件');
241
239
  expect(result).toEqual({ kind: 'ok' });
242
240
  });
241
+
242
+ it('不带 -p 时标注预设来源,带 -p 时打印所传值(ADR-0023 步骤 4.5)', async () => {
243
+ const dir = gitRepo();
244
+ stageChange(dir, 'src/existing.ts', 'export const a = 2;\n');
245
+ passTraces(dir);
246
+
247
+ await check({ staged: true, projectPath: dir, trigger: 'code_implementation' }, io);
248
+ await check(
249
+ { preset: 'standard', staged: true, projectPath: dir, trigger: 'code_implementation' },
250
+ io
251
+ );
252
+
253
+ const out = io.outText();
254
+ expect(out).toContain('预设: (按 config.yml,缺省 standard)');
255
+ expect(out).toContain('预设: standard');
256
+ });
243
257
  });
244
258
 
245
259
  describe('判定证据外显(harness#119)', () => {
@@ -255,14 +269,9 @@ describe('check command(真 git fixture)', () => {
255
269
  return dir;
256
270
  }
257
271
 
258
- /** 从本用例 trace 文件里取某条约束的记录 */
259
- function traceOf(constraintId: string) {
260
- return fs
261
- .readFileSync(traceFile, 'utf-8')
262
- .split('\n')
263
- .filter(Boolean)
264
- .map((line) => JSON.parse(line) as { constraintId: string; result: string; evidence?: string[] })
265
- .find((l) => l.constraintId === constraintId);
272
+ /** 从本用例项目的 trace 正本里取某条约束的记录(#139:落点锚在 projectPath) */
273
+ function traceOf(dir: string, constraintId: string) {
274
+ return projectTraces(dir).find(l => l.constraintId === constraintId);
266
275
  }
267
276
 
268
277
  it('变更文件未登记 → 警告随附证据点名该文件', async () => {
@@ -308,7 +317,7 @@ describe('check command(真 git fixture)', () => {
308
317
  io
309
318
  );
310
319
 
311
- const record = traceOf('capability_sync');
320
+ const record = traceOf(dir, 'capability_sync');
312
321
  expect(record?.result).toBe('pass');
313
322
  expect(record?.evidence?.join('\n')).toContain('src/nested/deep.ts');
314
323
  });
@@ -364,17 +373,15 @@ describe('check command(真 git fixture)', () => {
364
373
  const dir = gitRepo();
365
374
  stageChange(dir, 'src/existing.ts', 'export const a = 2;\n');
366
375
  passTraces(dir);
376
+ const seeded = projectTraces(dir).length;
367
377
 
368
378
  await check(
369
379
  { preset: 'standard', staged: true, projectPath: dir, trigger: 'code_implementation' },
370
380
  io
371
381
  );
372
382
 
373
- const traces = fs
374
- .readFileSync(traceFile, 'utf-8')
375
- .split('\n')
376
- .filter(Boolean)
377
- .map(l => JSON.parse(l) as { constraintId: string; result: string; projectPath: string });
383
+ // #139:落点锚在本 run 的 projectPath 上,切掉夹具预置的证据行
384
+ const traces = projectTraces(dir).slice(seeded);
378
385
 
379
386
  expect(traces.map(t => t.constraintId)).toEqual(
380
387
  expect.arrayContaining(['incremental_progress', 'no_implementation_without_requirement'])
@@ -0,0 +1,60 @@
1
+ /**
2
+ * command 门禁的两个死配置位已删除(#135,架构评审候选 6)
3
+ *
4
+ * `CommandGateConfig.strict` 只在构造时读一次、任何判定路径从不使用——`harness command --strict`
5
+ * 与不带它判定完全一致;`CommandGateConfig.customBlacklist` 全仓零生产注入者,运行时扩展点是
6
+ * `addRule()`。triage 裁决两项都**删**(判据同 ADR-0018/0022:不接进判定、不留「配了不生效」),
7
+ * CLI `--strict` 旗帜随之 removal(属 breaking,见 ADR-0024)。
8
+ *
9
+ * 手法照 `passes-gate-dead-options.test.ts` / `init-dead-options.test.ts`:编译期
10
+ * `@ts-expect-error` 钉字段不存在 + 运行期钉定义表旗帜面与配置形状。
11
+ */
12
+
13
+ import * as fs from 'fs';
14
+ import * as path from 'path';
15
+ import { GATE_DEFINITIONS } from '../../../gates/definitions';
16
+ import { CommandGate } from '../../../gates/command';
17
+ import type { CommandGateConfig } from '../../../gates/types';
18
+ import type { CommandCheckOptions } from '../command';
19
+
20
+ const commandGateCli = GATE_DEFINITIONS.find(gate => gate.id === 'command')!.cli;
21
+
22
+ describe('command 门禁的 strict / customBlacklist 已删除(#135)', () => {
23
+ it('定义表已注册 command 门禁(前提)', () => {
24
+ expect(commandGateCli).toBeDefined();
25
+ });
26
+
27
+ it('command 旗帜面逐字冻结,不含 --strict', () => {
28
+ expect(commandGateCli.options.map(option => option.flags)).toEqual([
29
+ '-l, --level',
30
+ '--list',
31
+ '--json',
32
+ ]);
33
+ });
34
+
35
+ it('CommandGateConfig 与 CommandCheckOptions 的 strict / customBlacklist 已从类型面消失(编译期钉)', () => {
36
+ // @ts-expect-error strict 零消费者(只在构造时读取),随 #135 从 CommandGateConfig 删除
37
+ const removedStrict: CommandGateConfig = { strict: true };
38
+ // @ts-expect-error customBlacklist 零生产注入者,运行时扩展点是 addRule(),随 #135 删除
39
+ const removedCustom: CommandGateConfig = { customBlacklist: [] };
40
+ // @ts-expect-error CLI 同名字段随 --strict 旗帜与装配一并删除
41
+ const removedOption: CommandCheckOptions = { strict: true };
42
+ expect([removedStrict, removedCustom, removedOption]).toHaveLength(3);
43
+ });
44
+
45
+ it('活字段仍可赋值(防止删多)', () => {
46
+ const live: CommandGateConfig = { ignoreCategories: ['database'] };
47
+ const liveCli: CommandCheckOptions = { level: true, list: true, json: false };
48
+ expect(Object.keys(live)).toHaveLength(1);
49
+ expect(Object.keys(liveCli)).toHaveLength(3);
50
+ });
51
+
52
+ it('配置读取面只剩 ignoreCategories(运行期形状钉)', () => {
53
+ expect(Object.keys(new CommandGate().getConfig())).toEqual(['ignoreCategories']);
54
+ });
55
+
56
+ it('CLI 装配点不再引用模块级单例出口(--level 与默认分支同一实例来源)', () => {
57
+ const cliSource = fs.readFileSync(path.join(__dirname, '..', 'command.ts'), 'utf-8');
58
+ expect(cliSource).not.toMatch(/getCommandRiskLevel|getCommandGate\(/);
59
+ });
60
+ });