@dommaker/harness 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (532) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +0 -2
  3. package/bin/harness.js +86 -32
  4. package/dist/cli/command-contract.d.ts +83 -0
  5. package/dist/cli/command-contract.d.ts.map +1 -0
  6. package/dist/cli/command-contract.js +66 -0
  7. package/dist/cli/command-contract.js.map +1 -0
  8. package/dist/cli/commands/acceptance.d.ts +3 -2
  9. package/dist/cli/commands/acceptance.d.ts.map +1 -1
  10. package/dist/cli/commands/acceptance.js +28 -24
  11. package/dist/cli/commands/acceptance.js.map +1 -1
  12. package/dist/cli/commands/analyze-sessions.d.ts +2 -1
  13. package/dist/cli/commands/analyze-sessions.d.ts.map +1 -1
  14. package/dist/cli/commands/analyze-sessions.js +25 -24
  15. package/dist/cli/commands/analyze-sessions.js.map +1 -1
  16. package/dist/cli/commands/check.d.ts +14 -2
  17. package/dist/cli/commands/check.d.ts.map +1 -1
  18. package/dist/cli/commands/check.js +104 -61
  19. package/dist/cli/commands/check.js.map +1 -1
  20. package/dist/cli/commands/command.d.ts +5 -1
  21. package/dist/cli/commands/command.d.ts.map +1 -1
  22. package/dist/cli/commands/command.js +26 -17
  23. package/dist/cli/commands/command.js.map +1 -1
  24. package/dist/cli/commands/constraints-report.d.ts +2 -1
  25. package/dist/cli/commands/constraints-report.d.ts.map +1 -1
  26. package/dist/cli/commands/constraints-report.js +37 -33
  27. package/dist/cli/commands/constraints-report.js.map +1 -1
  28. package/dist/cli/commands/constraints-retire.d.ts +6 -5
  29. package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
  30. package/dist/cli/commands/constraints-retire.js +50 -52
  31. package/dist/cli/commands/constraints-retire.js.map +1 -1
  32. package/dist/cli/commands/constraints.d.ts +2 -1
  33. package/dist/cli/commands/constraints.d.ts.map +1 -1
  34. package/dist/cli/commands/constraints.js +10 -27
  35. package/dist/cli/commands/constraints.js.map +1 -1
  36. package/dist/cli/commands/contract.d.ts +3 -2
  37. package/dist/cli/commands/contract.d.ts.map +1 -1
  38. package/dist/cli/commands/contract.js +45 -42
  39. package/dist/cli/commands/contract.js.map +1 -1
  40. package/dist/cli/commands/definitions.d.ts +11 -11
  41. package/dist/cli/commands/definitions.d.ts.map +1 -1
  42. package/dist/cli/commands/definitions.js +27 -219
  43. package/dist/cli/commands/definitions.js.map +1 -1
  44. package/dist/cli/commands/failure.d.ts +5 -4
  45. package/dist/cli/commands/failure.d.ts.map +1 -1
  46. package/dist/cli/commands/failure.js +31 -25
  47. package/dist/cli/commands/failure.js.map +1 -1
  48. package/dist/cli/commands/init.d.ts +7 -5
  49. package/dist/cli/commands/init.d.ts.map +1 -1
  50. package/dist/cli/commands/init.js +147 -211
  51. package/dist/cli/commands/init.js.map +1 -1
  52. package/dist/cli/commands/knowledge.d.ts +17 -30
  53. package/dist/cli/commands/knowledge.d.ts.map +1 -1
  54. package/dist/cli/commands/knowledge.js +183 -273
  55. package/dist/cli/commands/knowledge.js.map +1 -1
  56. package/dist/cli/commands/passes-gate.d.ts +8 -2
  57. package/dist/cli/commands/passes-gate.d.ts.map +1 -1
  58. package/dist/cli/commands/passes-gate.js +44 -66
  59. package/dist/cli/commands/passes-gate.js.map +1 -1
  60. package/dist/cli/commands/performance.d.ts +2 -1
  61. package/dist/cli/commands/performance.d.ts.map +1 -1
  62. package/dist/cli/commands/performance.js +20 -18
  63. package/dist/cli/commands/performance.js.map +1 -1
  64. package/dist/cli/commands/posteval-plan.d.ts +5 -1
  65. package/dist/cli/commands/posteval-plan.d.ts.map +1 -1
  66. package/dist/cli/commands/posteval-plan.js +26 -15
  67. package/dist/cli/commands/posteval-plan.js.map +1 -1
  68. package/dist/cli/commands/release.d.ts +6 -1
  69. package/dist/cli/commands/release.d.ts.map +1 -1
  70. package/dist/cli/commands/release.js +69 -59
  71. package/dist/cli/commands/release.js.map +1 -1
  72. package/dist/cli/commands/report.d.ts +2 -1
  73. package/dist/cli/commands/report.d.ts.map +1 -1
  74. package/dist/cli/commands/report.js +9 -5
  75. package/dist/cli/commands/report.js.map +1 -1
  76. package/dist/cli/commands/review.d.ts +3 -2
  77. package/dist/cli/commands/review.d.ts.map +1 -1
  78. package/dist/cli/commands/review.js +33 -29
  79. package/dist/cli/commands/review.js.map +1 -1
  80. package/dist/cli/commands/sdd.d.ts +2 -1
  81. package/dist/cli/commands/sdd.d.ts.map +1 -1
  82. package/dist/cli/commands/sdd.js +7 -5
  83. package/dist/cli/commands/sdd.js.map +1 -1
  84. package/dist/cli/commands/security.d.ts +3 -2
  85. package/dist/cli/commands/security.d.ts.map +1 -1
  86. package/dist/cli/commands/security.js +29 -26
  87. package/dist/cli/commands/security.js.map +1 -1
  88. package/dist/cli/commands/spec-baseline-check.d.ts +2 -1
  89. package/dist/cli/commands/spec-baseline-check.d.ts.map +1 -1
  90. package/dist/cli/commands/spec-baseline-check.js +15 -11
  91. package/dist/cli/commands/spec-baseline-check.js.map +1 -1
  92. package/dist/cli/commands/spec.d.ts +7 -2
  93. package/dist/cli/commands/spec.d.ts.map +1 -1
  94. package/dist/cli/commands/spec.js +61 -51
  95. package/dist/cli/commands/spec.js.map +1 -1
  96. package/dist/cli/commands/status.d.ts +2 -1
  97. package/dist/cli/commands/status.d.ts.map +1 -1
  98. package/dist/cli/commands/status.js +57 -53
  99. package/dist/cli/commands/status.js.map +1 -1
  100. package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
  101. package/dist/cli/commands/sync-docs/agents-syncer.js +5 -2
  102. package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
  103. package/dist/cli/commands/sync-docs/context-syncer.d.ts.map +1 -1
  104. package/dist/cli/commands/sync-docs/context-syncer.js +3 -2
  105. package/dist/cli/commands/sync-docs/context-syncer.js.map +1 -1
  106. package/dist/cli/commands/sync-docs/index.d.ts +2 -1
  107. package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
  108. package/dist/cli/commands/sync-docs/index.js +68 -88
  109. package/dist/cli/commands/sync-docs/index.js.map +1 -1
  110. package/dist/cli/commands/sync-docs/project-reader.d.ts +6 -1
  111. package/dist/cli/commands/sync-docs/project-reader.d.ts.map +1 -1
  112. package/dist/cli/commands/sync-docs/project-reader.js +14 -16
  113. package/dist/cli/commands/sync-docs/project-reader.js.map +1 -1
  114. package/dist/cli/commands/update-user-model.d.ts +9 -3
  115. package/dist/cli/commands/update-user-model.d.ts.map +1 -1
  116. package/dist/cli/commands/update-user-model.js +56 -61
  117. package/dist/cli/commands/update-user-model.js.map +1 -1
  118. package/dist/cli/commands/validate.d.ts +4 -3
  119. package/dist/cli/commands/validate.d.ts.map +1 -1
  120. package/dist/cli/commands/validate.js +28 -26
  121. package/dist/cli/commands/validate.js.map +1 -1
  122. package/dist/cli/session-mining/transcript.d.ts +7 -1
  123. package/dist/cli/session-mining/transcript.d.ts.map +1 -1
  124. package/dist/cli/session-mining/transcript.js +21 -14
  125. package/dist/cli/session-mining/transcript.js.map +1 -1
  126. package/dist/context/session-manager.d.ts.map +1 -1
  127. package/dist/context/session-manager.js +19 -32
  128. package/dist/context/session-manager.js.map +1 -1
  129. package/dist/core/constraints/agent-prompt-renderer.d.ts.map +1 -1
  130. package/dist/core/constraints/agent-prompt-renderer.js +1 -4
  131. package/dist/core/constraints/agent-prompt-renderer.js.map +1 -1
  132. package/dist/core/constraints/capabilities-parser.d.ts +24 -7
  133. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  134. package/dist/core/constraints/capabilities-parser.js +32 -28
  135. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  136. package/dist/core/constraints/capabilities-reconcile.d.ts +67 -0
  137. package/dist/core/constraints/capabilities-reconcile.d.ts.map +1 -0
  138. package/dist/core/constraints/capabilities-reconcile.js +105 -0
  139. package/dist/core/constraints/capabilities-reconcile.js.map +1 -0
  140. package/dist/core/constraints/checker.d.ts +21 -43
  141. package/dist/core/constraints/checker.d.ts.map +1 -1
  142. package/dist/core/constraints/checker.js +68 -117
  143. package/dist/core/constraints/checker.js.map +1 -1
  144. package/dist/core/constraints/checkers/capability-sync.d.ts +20 -8
  145. package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
  146. package/dist/core/constraints/checkers/capability-sync.js +66 -77
  147. package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
  148. package/dist/core/constraints/checkers/context-doc-sync.d.ts +4 -2
  149. package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
  150. package/dist/core/constraints/checkers/context-doc-sync.js +13 -22
  151. package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
  152. package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
  153. package/dist/core/constraints/checkers/docs-freshness.js +49 -66
  154. package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
  155. package/dist/core/constraints/checkers/governance-presence.d.ts +2 -2
  156. package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
  157. package/dist/core/constraints/checkers/governance-presence.js +7 -28
  158. package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
  159. package/dist/core/constraints/checkers/index.d.ts +2 -2
  160. package/dist/core/constraints/checkers/index.d.ts.map +1 -1
  161. package/dist/core/constraints/checkers/index.js +2 -1
  162. package/dist/core/constraints/checkers/index.js.map +1 -1
  163. package/dist/core/constraints/checkers/no-test-simplification.d.ts.map +1 -1
  164. package/dist/core/constraints/checkers/no-test-simplification.js +4 -3
  165. package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
  166. package/dist/core/constraints/checkers/types.d.ts +43 -4
  167. package/dist/core/constraints/checkers/types.d.ts.map +1 -1
  168. package/dist/core/constraints/checkers/types.js +33 -0
  169. package/dist/core/constraints/checkers/types.js.map +1 -1
  170. package/dist/core/constraints/context-builder.d.ts +15 -14
  171. package/dist/core/constraints/context-builder.d.ts.map +1 -1
  172. package/dist/core/constraints/context-builder.js +32 -96
  173. package/dist/core/constraints/context-builder.js.map +1 -1
  174. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  175. package/dist/core/constraints/doc-freshness/runner.js +2 -1
  176. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  177. package/dist/core/constraints/git-evidence.d.ts +60 -0
  178. package/dist/core/constraints/git-evidence.d.ts.map +1 -0
  179. package/dist/core/constraints/git-evidence.js +88 -0
  180. package/dist/core/constraints/git-evidence.js.map +1 -0
  181. package/dist/core/constraints/injection-drift.d.ts +3 -20
  182. package/dist/core/constraints/injection-drift.d.ts.map +1 -1
  183. package/dist/core/constraints/injection-drift.js +10 -57
  184. package/dist/core/constraints/injection-drift.js.map +1 -1
  185. package/dist/core/constraints/injection-writer.d.ts +113 -0
  186. package/dist/core/constraints/injection-writer.d.ts.map +1 -0
  187. package/dist/core/constraints/injection-writer.js +228 -0
  188. package/dist/core/constraints/injection-writer.js.map +1 -0
  189. package/dist/core/constraints/usage-report.d.ts +22 -1
  190. package/dist/core/constraints/usage-report.d.ts.map +1 -1
  191. package/dist/core/constraints/usage-report.js +27 -20
  192. package/dist/core/constraints/usage-report.js.map +1 -1
  193. package/dist/core/index.d.ts +2 -2
  194. package/dist/core/index.d.ts.map +1 -1
  195. package/dist/core/index.js +4 -2
  196. package/dist/core/index.js.map +1 -1
  197. package/dist/core/project-config-loader.d.ts +32 -1
  198. package/dist/core/project-config-loader.d.ts.map +1 -1
  199. package/dist/core/project-config-loader.js +39 -9
  200. package/dist/core/project-config-loader.js.map +1 -1
  201. package/dist/core/validators/passes-gate.d.ts +15 -64
  202. package/dist/core/validators/passes-gate.d.ts.map +1 -1
  203. package/dist/core/validators/passes-gate.js +84 -222
  204. package/dist/core/validators/passes-gate.js.map +1 -1
  205. package/dist/core/validators/test-output.d.ts +58 -0
  206. package/dist/core/validators/test-output.d.ts.map +1 -0
  207. package/dist/core/validators/test-output.js +109 -0
  208. package/dist/core/validators/test-output.js.map +1 -0
  209. package/dist/failure/recorder.d.ts +6 -0
  210. package/dist/failure/recorder.d.ts.map +1 -1
  211. package/dist/failure/recorder.js +12 -8
  212. package/dist/failure/recorder.js.map +1 -1
  213. package/dist/gates/acceptance.d.ts +1 -48
  214. package/dist/gates/acceptance.d.ts.map +1 -1
  215. package/dist/gates/acceptance.js +6 -38
  216. package/dist/gates/acceptance.js.map +1 -1
  217. package/dist/gates/checker-gate.d.ts +2 -1
  218. package/dist/gates/checker-gate.d.ts.map +1 -1
  219. package/dist/gates/checker-gate.js +11 -13
  220. package/dist/gates/checker-gate.js.map +1 -1
  221. package/dist/gates/command.d.ts.map +1 -1
  222. package/dist/gates/command.js +10 -22
  223. package/dist/gates/command.js.map +1 -1
  224. package/dist/gates/contract.d.ts.map +1 -1
  225. package/dist/gates/contract.js +22 -61
  226. package/dist/gates/contract.js.map +1 -1
  227. package/dist/gates/performance.d.ts.map +1 -1
  228. package/dist/gates/performance.js +13 -31
  229. package/dist/gates/performance.js.map +1 -1
  230. package/dist/gates/review.d.ts.map +1 -1
  231. package/dist/gates/review.js +21 -53
  232. package/dist/gates/review.js.map +1 -1
  233. package/dist/gates/security.d.ts.map +1 -1
  234. package/dist/gates/security.js +14 -32
  235. package/dist/gates/security.js.map +1 -1
  236. package/dist/gates/types.d.ts +28 -5
  237. package/dist/gates/types.d.ts.map +1 -1
  238. package/dist/gates/types.js +38 -1
  239. package/dist/gates/types.js.map +1 -1
  240. package/dist/hooks/bootstrap.d.ts +2 -0
  241. package/dist/hooks/bootstrap.d.ts.map +1 -1
  242. package/dist/hooks/bootstrap.js +5 -2
  243. package/dist/hooks/bootstrap.js.map +1 -1
  244. package/dist/index.d.ts +1 -2
  245. package/dist/index.d.ts.map +1 -1
  246. package/dist/index.js.map +1 -1
  247. package/dist/knowledge/audit.d.ts +7 -2
  248. package/dist/knowledge/audit.d.ts.map +1 -1
  249. package/dist/knowledge/audit.js +59 -79
  250. package/dist/knowledge/audit.js.map +1 -1
  251. package/dist/knowledge/flywheel-metrics.d.ts +39 -0
  252. package/dist/knowledge/flywheel-metrics.d.ts.map +1 -0
  253. package/dist/knowledge/flywheel-metrics.js +47 -0
  254. package/dist/knowledge/flywheel-metrics.js.map +1 -0
  255. package/dist/knowledge/import.d.ts.map +1 -1
  256. package/dist/knowledge/import.js +4 -14
  257. package/dist/knowledge/import.js.map +1 -1
  258. package/dist/knowledge/index-generator.d.ts.map +1 -1
  259. package/dist/knowledge/index-generator.js +17 -14
  260. package/dist/knowledge/index-generator.js.map +1 -1
  261. package/dist/knowledge/ingest.d.ts.map +1 -1
  262. package/dist/knowledge/ingest.js +9 -4
  263. package/dist/knowledge/ingest.js.map +1 -1
  264. package/dist/knowledge/migration.d.ts.map +1 -1
  265. package/dist/knowledge/migration.js +11 -8
  266. package/dist/knowledge/migration.js.map +1 -1
  267. package/dist/knowledge/reference-tracker.d.ts.map +1 -1
  268. package/dist/knowledge/reference-tracker.js +12 -9
  269. package/dist/knowledge/reference-tracker.js.map +1 -1
  270. package/dist/knowledge/store.d.ts +15 -0
  271. package/dist/knowledge/store.d.ts.map +1 -1
  272. package/dist/knowledge/store.js +59 -10
  273. package/dist/knowledge/store.js.map +1 -1
  274. package/dist/monitoring/context-tracker.d.ts.map +1 -1
  275. package/dist/monitoring/context-tracker.js +11 -20
  276. package/dist/monitoring/context-tracker.js.map +1 -1
  277. package/dist/monitoring/index.d.ts +1 -1
  278. package/dist/monitoring/index.d.ts.map +1 -1
  279. package/dist/monitoring/index.js +1 -2
  280. package/dist/monitoring/index.js.map +1 -1
  281. package/dist/monitoring/trace-analyzer.d.ts +15 -0
  282. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  283. package/dist/monitoring/trace-analyzer.js +19 -2
  284. package/dist/monitoring/trace-analyzer.js.map +1 -1
  285. package/dist/monitoring/traces.d.ts +21 -2
  286. package/dist/monitoring/traces.d.ts.map +1 -1
  287. package/dist/monitoring/traces.js +38 -29
  288. package/dist/monitoring/traces.js.map +1 -1
  289. package/dist/release/index.d.ts +2 -2
  290. package/dist/release/index.d.ts.map +1 -1
  291. package/dist/release/index.js +1 -5
  292. package/dist/release/index.js.map +1 -1
  293. package/dist/sdd/index-generator.js +10 -11
  294. package/dist/sdd/index-generator.js.map +1 -1
  295. package/dist/test-setup/project-fixture.d.ts +48 -0
  296. package/dist/test-setup/project-fixture.d.ts.map +1 -0
  297. package/dist/test-setup/project-fixture.js +106 -0
  298. package/dist/test-setup/project-fixture.js.map +1 -0
  299. package/dist/types/constraint.d.ts +8 -18
  300. package/dist/types/constraint.d.ts.map +1 -1
  301. package/dist/types/constraint.js +6 -1
  302. package/dist/types/constraint.js.map +1 -1
  303. package/dist/types/passes-gate.d.ts +10 -25
  304. package/dist/types/passes-gate.d.ts.map +1 -1
  305. package/dist/types/project-config.d.ts +1 -1
  306. package/dist/types/session.d.ts +6 -5
  307. package/dist/types/session.d.ts.map +1 -1
  308. package/dist/types/trace.d.ts +9 -0
  309. package/dist/types/trace.d.ts.map +1 -1
  310. package/dist/utils/exec.d.ts +10 -0
  311. package/dist/utils/exec.d.ts.map +1 -1
  312. package/dist/utils/exec.js +12 -0
  313. package/dist/utils/exec.js.map +1 -1
  314. package/dist/utils/file-walk.d.ts +9 -1
  315. package/dist/utils/file-walk.d.ts.map +1 -1
  316. package/dist/utils/file-walk.js +10 -1
  317. package/dist/utils/file-walk.js.map +1 -1
  318. package/dist/utils/frontmatter.d.ts +73 -0
  319. package/dist/utils/frontmatter.d.ts.map +1 -0
  320. package/dist/utils/frontmatter.js +141 -0
  321. package/dist/utils/frontmatter.js.map +1 -0
  322. package/dist/utils/jsonl.d.ts +92 -0
  323. package/dist/utils/jsonl.d.ts.map +1 -0
  324. package/dist/utils/jsonl.js +166 -0
  325. package/dist/utils/jsonl.js.map +1 -0
  326. package/dist/utils/package-version.d.ts +16 -0
  327. package/dist/utils/package-version.d.ts.map +1 -0
  328. package/dist/utils/package-version.js +63 -0
  329. package/dist/utils/package-version.js.map +1 -0
  330. package/package.json +1 -1
  331. package/src/CONTEXT.md +8 -2
  332. package/src/__tests__/acceptance-gate.test.ts +3 -1
  333. package/src/__tests__/checker-extra.test.ts +7 -1218
  334. package/src/__tests__/context-files-resolution.test.ts +116 -0
  335. package/src/__tests__/effective-constraints.test.ts +10 -13
  336. package/src/__tests__/exec.test.ts +35 -1
  337. package/src/__tests__/layering.test.ts +152 -0
  338. package/src/__tests__/passes-gate-extra.test.ts +0 -145
  339. package/src/__tests__/passes-gate.test.ts +130 -101
  340. package/src/__tests__/skip-semantics.test.ts +5 -9
  341. package/src/cli/__tests__/command-contract.test.ts +170 -0
  342. package/src/cli/command-contract.ts +108 -0
  343. package/src/cli/commands/CONTEXT.md +23 -8
  344. package/src/cli/commands/__tests__/acceptance.test.ts +40 -33
  345. package/src/cli/commands/__tests__/analyze-sessions.test.ts +35 -29
  346. package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +107 -0
  347. package/src/cli/commands/__tests__/check-drift.test.ts +27 -34
  348. package/src/cli/commands/__tests__/check-skip-output.test.ts +21 -24
  349. package/src/cli/commands/__tests__/check.test.ts +448 -378
  350. package/src/cli/commands/__tests__/command.test.ts +85 -70
  351. package/src/cli/commands/__tests__/constraints-report.test.ts +100 -71
  352. package/src/cli/commands/__tests__/constraints-retire.test.ts +152 -95
  353. package/src/cli/commands/__tests__/contract.test.ts +27 -34
  354. package/src/cli/commands/__tests__/failure.test.ts +31 -34
  355. package/src/cli/commands/__tests__/init-injection.test.ts +58 -28
  356. package/src/cli/commands/__tests__/init.test.ts +77 -48
  357. package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +108 -0
  358. package/src/cli/commands/__tests__/knowledge.test.ts +69 -43
  359. package/src/cli/commands/__tests__/passes-gate.test.ts +43 -45
  360. package/src/cli/commands/__tests__/performance.test.ts +22 -23
  361. package/src/cli/commands/__tests__/posteval-plan.test.ts +39 -38
  362. package/src/cli/commands/__tests__/project-path-anchoring.test.ts +157 -0
  363. package/src/cli/commands/__tests__/project-path-convention.test.ts +231 -0
  364. package/src/cli/commands/__tests__/registry.test.ts +75 -35
  365. package/src/cli/commands/__tests__/release.test.ts +209 -80
  366. package/src/cli/commands/__tests__/report-extra.test.ts +16 -16
  367. package/src/cli/commands/__tests__/report.test.ts +25 -25
  368. package/src/cli/commands/__tests__/review.test.ts +33 -31
  369. package/src/cli/commands/__tests__/security.test.ts +23 -27
  370. package/src/cli/commands/__tests__/spec-baseline-check.test.ts +20 -20
  371. package/src/cli/commands/__tests__/spec.test.ts +41 -29
  372. package/src/cli/commands/__tests__/status-extra.test.ts +18 -18
  373. package/src/cli/commands/__tests__/status-skipped-lines.test.ts +72 -0
  374. package/src/cli/commands/__tests__/status.test.ts +34 -34
  375. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +77 -78
  376. package/src/cli/commands/__tests__/sync-docs.test.ts +72 -74
  377. package/src/cli/commands/__tests__/update-user-model.test.ts +97 -34
  378. package/src/cli/commands/__tests__/validate.test.ts +27 -29
  379. package/src/cli/commands/acceptance.ts +35 -24
  380. package/src/cli/commands/analyze-sessions.ts +25 -24
  381. package/src/cli/commands/check.ts +127 -66
  382. package/src/cli/commands/command.ts +30 -17
  383. package/src/cli/commands/constraints-report.ts +37 -34
  384. package/src/cli/commands/constraints-retire.ts +58 -55
  385. package/src/cli/commands/constraints.ts +10 -27
  386. package/src/cli/commands/contract.ts +51 -42
  387. package/src/cli/commands/definitions.ts +34 -227
  388. package/src/cli/commands/failure.ts +33 -26
  389. package/src/cli/commands/init.ts +166 -220
  390. package/src/cli/commands/knowledge.ts +192 -289
  391. package/src/cli/commands/passes-gate.ts +58 -68
  392. package/src/cli/commands/performance.ts +23 -18
  393. package/src/cli/commands/posteval-plan.ts +29 -15
  394. package/src/cli/commands/release.ts +70 -59
  395. package/src/cli/commands/report.ts +9 -5
  396. package/src/cli/commands/review.ts +39 -29
  397. package/src/cli/commands/sdd.ts +7 -5
  398. package/src/cli/commands/security.ts +35 -26
  399. package/src/cli/commands/spec-baseline-check.ts +16 -11
  400. package/src/cli/commands/spec.ts +66 -53
  401. package/src/cli/commands/status.ts +59 -53
  402. package/src/cli/commands/sync-docs/agents-syncer.ts +5 -2
  403. package/src/cli/commands/sync-docs/context-syncer.ts +3 -2
  404. package/src/cli/commands/sync-docs/index.ts +73 -100
  405. package/src/cli/commands/sync-docs/project-reader.ts +17 -18
  406. package/src/cli/commands/update-user-model.ts +64 -61
  407. package/src/cli/commands/validate.ts +31 -25
  408. package/src/cli/session-mining/__tests__/transcript.test.ts +118 -0
  409. package/src/cli/session-mining/transcript.ts +26 -8
  410. package/src/context/session-manager.ts +19 -33
  411. package/src/core/CONTEXT.md +10 -6
  412. package/src/core/constraints/__tests__/capabilities-reconcile.test.ts +200 -0
  413. package/src/core/constraints/__tests__/capability-counts.test.ts +94 -0
  414. package/src/core/constraints/__tests__/git-evidence.test.ts +208 -0
  415. package/src/core/constraints/__tests__/injection-drift.test.ts +3 -10
  416. package/src/core/constraints/__tests__/injection-writer.test.ts +231 -0
  417. package/src/core/constraints/__tests__/trace-injection.test.ts +71 -0
  418. package/src/core/constraints/__tests__/usage-report.test.ts +112 -0
  419. package/src/core/constraints/agent-prompt-renderer.ts +2 -6
  420. package/src/core/constraints/capabilities-parser.ts +53 -35
  421. package/src/core/constraints/capabilities-reconcile.ts +166 -0
  422. package/src/core/constraints/checker.ts +82 -133
  423. package/src/core/constraints/checkers/__tests__/capability-sync.test.ts +273 -0
  424. package/src/core/constraints/checkers/__tests__/check-outcome.test.ts +176 -0
  425. package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +77 -0
  426. package/src/core/constraints/checkers/__tests__/docs-freshness.test.ts +126 -0
  427. package/src/{__tests__ → core/constraints/checkers/__tests__}/governance-presence.test.ts +83 -9
  428. package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +50 -0
  429. package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +76 -0
  430. package/src/{__tests__ → core/constraints/checkers/__tests__}/no-hardcoded-credentials.test.ts +4 -3
  431. package/src/core/constraints/checkers/__tests__/no-test-simplification.test.ts +58 -0
  432. package/src/core/constraints/checkers/__tests__/three-state-consistency.test.ts +153 -0
  433. package/src/core/constraints/checkers/capability-sync.ts +75 -96
  434. package/src/core/constraints/checkers/context-doc-sync.ts +15 -25
  435. package/src/core/constraints/checkers/docs-freshness.ts +54 -70
  436. package/src/core/constraints/checkers/governance-presence.ts +7 -25
  437. package/src/core/constraints/checkers/index.ts +9 -2
  438. package/src/core/constraints/checkers/no-test-simplification.ts +4 -3
  439. package/src/core/constraints/checkers/types.ts +60 -4
  440. package/src/core/constraints/context-builder.ts +39 -91
  441. package/src/core/constraints/doc-freshness/runner.ts +2 -1
  442. package/src/core/constraints/git-evidence.ts +120 -0
  443. package/src/core/constraints/injection-drift.ts +13 -60
  444. package/src/core/constraints/injection-writer.ts +212 -0
  445. package/src/core/constraints/usage-report.ts +36 -17
  446. package/src/core/index.ts +4 -2
  447. package/src/core/project-config-loader.ts +49 -9
  448. package/src/core/validators/__tests__/passes-gate.test.ts +2 -56
  449. package/src/core/validators/__tests__/test-output.test.ts +372 -0
  450. package/src/core/validators/passes-gate.ts +86 -248
  451. package/src/core/validators/test-output.ts +128 -0
  452. package/src/failure/__tests__/recorder.test.ts +168 -0
  453. package/src/failure/recorder.ts +12 -10
  454. package/src/gates/CONTEXT.md +7 -4
  455. package/src/gates/__tests__/acceptance-types-single-source.test.ts +68 -0
  456. package/src/gates/__tests__/acceptance.test.ts +4 -86
  457. package/src/gates/__tests__/checker-gate.test.ts +24 -0
  458. package/src/gates/__tests__/effective-gates.test.ts +3 -8
  459. package/src/gates/__tests__/test-verdict-consistency.test.ts +140 -0
  460. package/src/gates/acceptance.ts +19 -84
  461. package/src/gates/checker-gate.ts +12 -15
  462. package/src/gates/command.ts +10 -16
  463. package/src/gates/contract.ts +22 -61
  464. package/src/gates/performance.ts +10 -22
  465. package/src/gates/review.ts +21 -41
  466. package/src/gates/security.ts +10 -22
  467. package/src/gates/types.ts +70 -5
  468. package/src/hooks/CONTEXT.md +1 -1
  469. package/src/hooks/bootstrap.ts +5 -2
  470. package/src/index.ts +0 -4
  471. package/src/knowledge/CONTEXT.md +8 -3
  472. package/src/knowledge/__tests__/audit-labels.test.ts +44 -0
  473. package/src/knowledge/__tests__/flywheel-metrics.test.ts +135 -0
  474. package/src/knowledge/__tests__/import.test.ts +44 -0
  475. package/src/knowledge/__tests__/index-generator.test.ts +53 -0
  476. package/src/knowledge/__tests__/ingest.test.ts +85 -0
  477. package/src/knowledge/__tests__/migration.test.ts +47 -0
  478. package/src/knowledge/__tests__/reference-tracker.test.ts +16 -0
  479. package/src/knowledge/__tests__/store.test.ts +194 -0
  480. package/src/knowledge/audit.ts +67 -71
  481. package/src/knowledge/flywheel-metrics.ts +71 -0
  482. package/src/knowledge/import.ts +4 -18
  483. package/src/knowledge/index-generator.ts +19 -14
  484. package/src/knowledge/ingest.ts +9 -4
  485. package/src/knowledge/migration.ts +11 -8
  486. package/src/knowledge/reference-tracker.ts +12 -8
  487. package/src/knowledge/store.ts +64 -11
  488. package/src/monitoring/CONTEXT.md +8 -3
  489. package/src/monitoring/__tests__/skipped-lines-report.test.ts +178 -0
  490. package/src/monitoring/__tests__/traces.test.ts +57 -0
  491. package/src/monitoring/context-tracker.ts +11 -23
  492. package/src/monitoring/index.ts +1 -1
  493. package/src/monitoring/trace-analyzer.ts +21 -2
  494. package/src/monitoring/traces.ts +39 -36
  495. package/src/release/CONTEXT.md +1 -0
  496. package/src/release/__tests__/barrel.test.ts +60 -0
  497. package/src/release/index.ts +2 -13
  498. package/src/sdd/__tests__/index-generator.test.ts +71 -0
  499. package/src/sdd/index-generator.ts +9 -9
  500. package/src/test-setup/__tests__/project-fixture.test.ts +178 -0
  501. package/src/test-setup/project-fixture.ts +87 -0
  502. package/src/tools/CONTEXT.md +6 -0
  503. package/src/tools/__tests__/registry.test.ts +221 -0
  504. package/src/types/__tests__/types.test.ts +4 -5
  505. package/src/types/constraint.ts +16 -27
  506. package/src/types/passes-gate.ts +10 -30
  507. package/src/types/project-config.ts +1 -1
  508. package/src/types/session.ts +5 -5
  509. package/src/types/trace.ts +10 -0
  510. package/src/utils/__tests__/frontmatter.test.ts +206 -0
  511. package/src/utils/__tests__/jsonl-skip-disposition.test.ts +175 -0
  512. package/src/utils/__tests__/jsonl.test.ts +187 -0
  513. package/src/utils/__tests__/package-version.test.ts +97 -0
  514. package/src/utils/exec.ts +15 -0
  515. package/src/utils/file-walk.ts +11 -2
  516. package/src/utils/frontmatter.ts +121 -0
  517. package/src/utils/jsonl.ts +167 -0
  518. package/src/utils/package-version.ts +27 -0
  519. package/dist/cli/commands/doc-freshness-check.d.ts +0 -53
  520. package/dist/cli/commands/doc-freshness-check.d.ts.map +0 -1
  521. package/dist/cli/commands/doc-freshness-check.js +0 -352
  522. package/dist/cli/commands/doc-freshness-check.js.map +0 -1
  523. package/dist/types/index.d.ts +0 -14
  524. package/dist/types/index.d.ts.map +0 -1
  525. package/dist/types/index.js +0 -39
  526. package/dist/types/index.js.map +0 -1
  527. package/src/__tests__/long-running.test.ts +0 -17
  528. package/src/__tests__/passes-gate-extension.test.ts +0 -151
  529. package/src/cli/commands/__tests__/doc-freshness-check.test.ts +0 -168
  530. package/src/cli/commands/doc-freshness-check.ts +0 -386
  531. package/src/spec/CONTEXT.md +0 -11
  532. package/src/types/index.ts +0 -47
@@ -3,9 +3,15 @@
3
3
  */
4
4
 
5
5
  import * as fs from 'fs';
6
+ import { captureIO, type CapturingIO } from '../../command-contract';
6
7
  import * as path from 'path';
7
8
  import { syncDocs } from '../sync-docs';
8
9
 
10
+ let io: CapturingIO;
11
+ beforeEach(() => {
12
+ io = captureIO();
13
+ });
14
+
9
15
  describe('sync-docs --agents', () => {
10
16
  const tempDir = path.join(process.cwd(), 'temp-test-sync-docs-agents');
11
17
 
@@ -21,15 +27,8 @@ describe('sync-docs --agents', () => {
21
27
  }
22
28
  });
23
29
 
24
- let consoleSpy: jest.SpyInstance;
25
-
26
30
  beforeEach(() => {
27
31
  jest.clearAllMocks();
28
- consoleSpy = jest.spyOn(console, 'log').mockImplementation();
29
- });
30
-
31
- afterEach(() => {
32
- consoleSpy.mockRestore();
33
32
  });
34
33
 
35
34
  /**
@@ -90,7 +89,7 @@ describe('sync-docs --agents', () => {
90
89
  const testDir = path.join(tempDir, 'gen-pnpm');
91
90
  createFixture(testDir, { pnpm: true });
92
91
 
93
- await syncDocs({ projectPath: testDir, agents: true });
92
+ await syncDocs({ projectPath: testDir, agents: true }, io);
94
93
 
95
94
  const agentsPath = path.join(testDir, 'AGENTS.md');
96
95
  expect(fs.existsSync(agentsPath)).toBe(true);
@@ -126,7 +125,7 @@ describe('sync-docs --agents', () => {
126
125
  const testDir = path.join(tempDir, 'gen-npm');
127
126
  createFixture(testDir);
128
127
 
129
- await syncDocs({ projectPath: testDir, agents: true });
128
+ await syncDocs({ projectPath: testDir, agents: true }, io);
130
129
 
131
130
  const content = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf-8');
132
131
  expect(content).toContain('npm run build');
@@ -141,18 +140,18 @@ describe('sync-docs --agents', () => {
141
140
  const testDir = path.join(tempDir, 'idempotent');
142
141
  createFixture(testDir, { pnpm: true, withCapabilities: true });
143
142
 
144
- await syncDocs({ projectPath: testDir, agents: true });
143
+ await syncDocs({ projectPath: testDir, agents: true }, io);
145
144
  const first = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf-8');
146
145
 
147
146
  // 第二次运行:无任何差异
148
- const secondRun = await syncDocs({ projectPath: testDir, agents: true });
147
+ const secondRun = await syncDocs({ projectPath: testDir, agents: true }, io);
149
148
  const second = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf-8');
150
149
  expect(second).toBe(first);
151
- expect(secondRun).toBe(true);
150
+ expect(secondRun).toEqual({ kind: 'ok' });
152
151
 
153
152
  // --check 模式应通过
154
- const checkResult = await syncDocs({ projectPath: testDir, agents: true, check: true });
155
- expect(checkResult).toBe(true);
153
+ const checkResult = await syncDocs({ projectPath: testDir, agents: true, check: true }, io);
154
+ expect(checkResult).toEqual({ kind: 'ok' });
156
155
 
157
156
  fs.rmSync(testDir, { recursive: true, force: true });
158
157
  });
@@ -161,9 +160,9 @@ describe('sync-docs --agents', () => {
161
160
  const testDir = path.join(tempDir, 'check-missing');
162
161
  createFixture(testDir, { withCapabilities: true });
163
162
 
164
- const result = await syncDocs({ projectPath: testDir, agents: true, check: true });
165
- expect(result).toBe(false);
166
- expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('缺少 AGENTS.md'));
163
+ const result = await syncDocs({ projectPath: testDir, agents: true, check: true }, io);
164
+ expect(result).toEqual({ kind: 'fail', reason: expect.stringContaining('文档不是最新') });
165
+ expect(io.outText()).toContain('缺少 AGENTS.md');
167
166
  // check 模式不写入
168
167
  expect(fs.existsSync(path.join(testDir, 'AGENTS.md'))).toBe(false);
169
168
 
@@ -175,22 +174,22 @@ describe('sync-docs --agents', () => {
175
174
  createFixture(testDir, { pnpm: true, withCapabilities: true });
176
175
 
177
176
  // 先生成
178
- await syncDocs({ projectPath: testDir, agents: true });
177
+ await syncDocs({ projectPath: testDir, agents: true }, io);
179
178
 
180
179
  // 漂移 1:手改 AGENTS.md
181
180
  fs.appendFileSync(path.join(testDir, 'AGENTS.md'), '\n手动追加的一行\n');
182
- expect(await syncDocs({ projectPath: testDir, agents: true, check: true })).toBe(false);
181
+ expect(await syncDocs({ projectPath: testDir, agents: true, check: true }, io)).toEqual({ kind: 'fail', reason: expect.stringContaining('文档不是最新') });
183
182
 
184
183
  // 重新生成恢复
185
- await syncDocs({ projectPath: testDir, agents: true });
186
- expect(await syncDocs({ projectPath: testDir, agents: true, check: true })).toBe(true);
184
+ await syncDocs({ projectPath: testDir, agents: true }, io);
185
+ expect(await syncDocs({ projectPath: testDir, agents: true, check: true }, io)).toEqual({ kind: 'ok' });
187
186
 
188
187
  // 漂移 2:package.json 删除一个 curated 脚本
189
188
  const pkgPath = path.join(testDir, 'package.json');
190
189
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
191
190
  delete pkg.scripts.lint;
192
191
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
193
- expect(await syncDocs({ projectPath: testDir, agents: true, check: true })).toBe(false);
192
+ expect(await syncDocs({ projectPath: testDir, agents: true, check: true }, io)).toEqual({ kind: 'fail', reason: expect.stringContaining('文档不是最新') });
194
193
 
195
194
  fs.rmSync(testDir, { recursive: true, force: true });
196
195
  });
@@ -199,8 +198,8 @@ describe('sync-docs --agents', () => {
199
198
  const testDir = path.join(tempDir, 'no-agents');
200
199
  createFixture(testDir, { withCapabilities: true });
201
200
 
202
- const result = await syncDocs({ projectPath: testDir });
203
- expect(result).toBe(true);
201
+ const result = await syncDocs({ projectPath: testDir }, io);
202
+ expect(result).toEqual({ kind: 'ok' });
204
203
  expect(fs.existsSync(path.join(testDir, 'AGENTS.md'))).toBe(false);
205
204
 
206
205
  fs.rmSync(testDir, { recursive: true, force: true });
@@ -210,10 +209,10 @@ describe('sync-docs --agents', () => {
210
209
  const testDir = path.join(tempDir, 'json-agents');
211
210
  createFixture(testDir, { withCapabilities: true });
212
211
 
213
- const result = await syncDocs({ projectPath: testDir, agents: true, check: true, json: true });
214
- expect(result).toBe(false);
212
+ const result = await syncDocs({ projectPath: testDir, agents: true, check: true, json: true }, io);
213
+ expect(result).toEqual({ kind: 'fail', reason: expect.stringContaining('文档不是最新') });
215
214
 
216
- const jsonCall = consoleSpy.mock.calls.find(
215
+ const jsonCall = io.outRecords().map(chunk => [chunk] as string[]).find(
217
216
  (call: unknown[]) => typeof call[0] === 'string' && call[0].startsWith('{')
218
217
  );
219
218
  expect(jsonCall).toBeDefined();
@@ -230,7 +229,7 @@ describe('sync-docs --agents', () => {
230
229
  createFixture(testDir);
231
230
  fs.writeFileSync(path.join(testDir, 'yarn.lock'), '# yarn lockfile v1\n');
232
231
 
233
- await syncDocs({ projectPath: testDir, agents: true });
232
+ await syncDocs({ projectPath: testDir, agents: true }, io);
234
233
 
235
234
  const content = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf-8');
236
235
  expect(content).toContain('yarn dev');
@@ -250,7 +249,7 @@ describe('sync-docs --agents', () => {
250
249
  fs.mkdirSync(path.join(testDir, '.harness'), { recursive: true });
251
250
  fs.writeFileSync(path.join(testDir, '.harness', 'CONTEXT.md'), '# 模块上下文\n\n## src\n\n职责:夹具\n');
252
251
 
253
- await syncDocs({ projectPath: testDir, agents: true });
252
+ await syncDocs({ projectPath: testDir, agents: true }, io);
254
253
 
255
254
  const content = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf-8');
256
255
  expect(content).toContain('模块上下文正本:`.harness/CONTEXT.md`(模块锚点组织),改动代码时同步更新');
@@ -260,9 +259,9 @@ describe('sync-docs --agents', () => {
260
259
  expect(content).toContain('项目知识库,用 `harness knowledge` 查询');
261
260
 
262
261
  // 幂等 + --check 通过(正本模型下无漂移)
263
- const second = await syncDocs({ projectPath: testDir, agents: true });
264
- expect(second).toBe(true);
265
- expect(await syncDocs({ projectPath: testDir, agents: true, check: true })).toBe(true);
262
+ const second = await syncDocs({ projectPath: testDir, agents: true }, io);
263
+ expect(second).toEqual({ kind: 'ok' });
264
+ expect(await syncDocs({ projectPath: testDir, agents: true, check: true }, io)).toEqual({ kind: 'ok' });
266
265
 
267
266
  fs.rmSync(testDir, { recursive: true, force: true });
268
267
  });
@@ -272,7 +271,7 @@ describe('sync-docs --agents', () => {
272
271
  createFixture(legacyDir, { pnpm: true, withCapabilities: true });
273
272
  fs.mkdirSync(path.join(legacyDir, '.studio'), { recursive: true });
274
273
  fs.writeFileSync(path.join(legacyDir, '.studio', 'CONTEXT.md'), '# 模块上下文\n\n## src\n\n职责:夹具\n');
275
- await syncDocs({ projectPath: legacyDir, agents: true });
274
+ await syncDocs({ projectPath: legacyDir, agents: true }, io);
276
275
  expect(fs.readFileSync(path.join(legacyDir, 'AGENTS.md'), 'utf-8'))
277
276
  .toContain('模块上下文正本:`.studio/CONTEXT.md`(模块锚点组织),改动代码时同步更新');
278
277
  fs.rmSync(legacyDir, { recursive: true, force: true });
@@ -283,7 +282,7 @@ describe('sync-docs --agents', () => {
283
282
  fs.writeFileSync(path.join(bothDir, '.studio', 'CONTEXT.md'), '# 旧\n');
284
283
  fs.mkdirSync(path.join(bothDir, '.harness'), { recursive: true });
285
284
  fs.writeFileSync(path.join(bothDir, '.harness', 'CONTEXT.md'), '# 新\n');
286
- await syncDocs({ projectPath: bothDir, agents: true });
285
+ await syncDocs({ projectPath: bothDir, agents: true }, io);
287
286
  const content = fs.readFileSync(path.join(bothDir, 'AGENTS.md'), 'utf-8');
288
287
  expect(content).toContain('模块上下文正本:`.harness/CONTEXT.md`');
289
288
  expect(content).not.toContain('.studio/CONTEXT.md');
@@ -294,13 +293,13 @@ describe('sync-docs --agents', () => {
294
293
  const testDir = path.join(tempDir, 'scattered-context-model');
295
294
  createFixture(testDir, { pnpm: true, withCapabilities: true });
296
295
 
297
- await syncDocs({ projectPath: testDir, agents: true });
296
+ await syncDocs({ projectPath: testDir, agents: true }, io);
298
297
 
299
298
  const content = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf-8');
300
299
  expect(content).toContain('各源码目录的 `CONTEXT.md` 是权威模块文档(现有 1 个),改动代码时同步更新');
301
300
  expect(content).not.toContain('模块上下文正本');
302
301
 
303
- expect(await syncDocs({ projectPath: testDir, agents: true, check: true })).toBe(true);
302
+ expect(await syncDocs({ projectPath: testDir, agents: true, check: true }, io)).toEqual({ kind: 'ok' });
304
303
 
305
304
  fs.rmSync(testDir, { recursive: true, force: true });
306
305
  });
@@ -310,7 +309,7 @@ describe('sync-docs --agents', () => {
310
309
  createFixture(testDir, { pnpm: true, withCapabilities: true });
311
310
  fs.mkdirSync(path.join(testDir, '.harness', 'CONTEXT.md'), { recursive: true });
312
311
 
313
- await syncDocs({ projectPath: testDir, agents: true });
312
+ await syncDocs({ projectPath: testDir, agents: true }, io);
314
313
 
315
314
  const content = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf-8');
316
315
  expect(content).toContain('各源码目录的 `CONTEXT.md` 是权威模块文档');
@@ -328,8 +327,8 @@ describe('sync-docs --agents', () => {
328
327
  fs.mkdirSync(path.join(testDir, '.next'), { recursive: true });
329
328
  fs.mkdirSync(path.join(testDir, '.vscode'), { recursive: true });
330
329
 
331
- const result = await syncDocs({ projectPath: testDir, agents: true });
332
- expect(result).toBe(false); // AGENTS.md 缺失 → 有问题,但已生成
330
+ const result = await syncDocs({ projectPath: testDir, agents: true }, io);
331
+ expect(result).toEqual({ kind: 'ok' }); // AGENTS.md 缺失 → 有问题,但已生成
333
332
 
334
333
  const content = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf-8');
335
334
  // 项目名回退到目录名,无 description
@@ -359,8 +358,8 @@ describe('sync-docs --agents', () => {
359
358
  '',
360
359
  ].join('\n'));
361
360
 
362
- const result = await syncDocs({ projectPath: testDir, agents: true });
363
- expect(result).toBe(false);
361
+ const result = await syncDocs({ projectPath: testDir, agents: true }, io);
362
+ expect(result).toEqual({ kind: 'ok' });
364
363
 
365
364
  // src 目录不存在:扫描/统计静默跳过,不中断生成
366
365
  const content = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf-8');
@@ -397,8 +396,8 @@ describe('sync-docs --agents', () => {
397
396
  scripts: { test: 'jest' },
398
397
  }));
399
398
 
400
- const result = await syncDocs({ projectPath: testDir, agents: true });
401
- expect(result).toBe(false); // CAPABILITIES.md/AGENTS.md 缺失 → 已生成
399
+ const result = await syncDocs({ projectPath: testDir, agents: true }, io);
400
+ expect(result).toEqual({ kind: 'ok' }); // CAPABILITIES.md/AGENTS.md 缺失 → 已生成
402
401
 
403
402
  const content = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf-8');
404
403
  expect(content).toContain('| `apps/` | monorepo 应用:api、web |');
@@ -419,7 +418,7 @@ describe('sync-docs --agents', () => {
419
418
  createFixture(testDir, { withCapabilities: true });
420
419
  fs.writeFileSync(path.join(testDir, 'CLAUDE.md'), '# CLAUDE.md\n\n## Governance Rules\n\n(尚未配置约束)\n');
421
420
 
422
- await syncDocs({ projectPath: testDir, agents: true });
421
+ await syncDocs({ projectPath: testDir, agents: true }, io);
423
422
 
424
423
  const content = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf-8');
425
424
  expect(content).toContain('Governance Rules 块');
@@ -432,12 +431,12 @@ describe('sync-docs --agents', () => {
432
431
  const testDir = path.join(tempDir, 'json-idempotent');
433
432
  createFixture(testDir, { pnpm: true, withCapabilities: true });
434
433
 
435
- await syncDocs({ projectPath: testDir, agents: true });
434
+ await syncDocs({ projectPath: testDir, agents: true }, io);
436
435
 
437
- const result = await syncDocs({ projectPath: testDir, agents: true, json: true });
438
- expect(result).toBe(true);
436
+ const result = await syncDocs({ projectPath: testDir, agents: true, json: true }, io);
437
+ expect(result).toEqual({ kind: 'ok' });
439
438
 
440
- const jsonCall = consoleSpy.mock.calls.find(
439
+ const jsonCall = io.outRecords().map(chunk => [chunk] as string[]).find(
441
440
  (call: unknown[]) => typeof call[0] === 'string' && call[0].startsWith('{')
442
441
  );
443
442
  expect(jsonCall).toBeDefined();
@@ -475,9 +474,9 @@ describe('sync-docs --agents', () => {
475
474
  fs.utimesSync(path.join(testDir, 'src', 'modules', 'CONTEXT.md'), past, past);
476
475
 
477
476
  // json 模式:结构化输出缺失/过时
478
- const jsonResult = await syncDocs({ projectPath: testDir, agents: true, check: true, json: true });
479
- expect(jsonResult).toBe(false);
480
- const jsonCall = consoleSpy.mock.calls.find(
477
+ const jsonResult = await syncDocs({ projectPath: testDir, agents: true, check: true, json: true }, io);
478
+ expect(jsonResult).toEqual({ kind: 'fail', reason: expect.stringContaining('文档不是最新') });
479
+ const jsonCall = io.outRecords().map(chunk => [chunk] as string[]).find(
481
480
  (call: unknown[]) => typeof call[0] === 'string' && call[0].startsWith('{')
482
481
  );
483
482
  expect(jsonCall).toBeDefined();
@@ -491,11 +490,11 @@ describe('sync-docs --agents', () => {
491
490
  expect(parsed.agentsMd).toEqual({ file: 'AGENTS.md', exists: false, stale: true });
492
491
 
493
492
  // 人读模式:输出过时提示
494
- consoleSpy.mockClear();
495
- const humanResult = await syncDocs({ projectPath: testDir, agents: true, check: true });
496
- expect(humanResult).toBe(false);
497
- expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('可能过时'));
498
- expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('缺少 CONTEXT.md'));
493
+ io = captureIO();
494
+ const humanResult = await syncDocs({ projectPath: testDir, agents: true, check: true }, io);
495
+ expect(humanResult).toEqual({ kind: 'fail', reason: expect.stringContaining('文档不是最新') });
496
+ expect(io.outText()).toContain('可能过时');
497
+ expect(io.outText()).toContain('缺少 CONTEXT.md');
499
498
 
500
499
  fs.rmSync(testDir, { recursive: true, force: true });
501
500
  });
@@ -517,18 +516,18 @@ describe('sync-docs --agents', () => {
517
516
  const testDir = path.join(tempDir, 'preserve-basic');
518
517
  createFixture(testDir, { pnpm: true, withCapabilities: true });
519
518
 
520
- await syncDocs({ projectPath: testDir, agents: true });
519
+ await syncDocs({ projectPath: testDir, agents: true }, io);
521
520
  appendBlock(testDir, MANUAL_BLOCK);
522
521
 
523
522
  // 追加块后构成漂移 → 写入模式重组:生成部分 + PRESERVE 块
524
- await syncDocs({ projectPath: testDir, agents: true });
523
+ await syncDocs({ projectPath: testDir, agents: true }, io);
525
524
  const content = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf-8');
526
525
  expect(content).toContain(MANUAL_BLOCK);
527
526
  // 块位于生成内容之后
528
527
  expect(content.indexOf('## 知识入口')).toBeLessThan(content.indexOf('<!-- PRESERVE:manual -->'));
529
528
 
530
529
  // 重组后幂等:--check 通过
531
- expect(await syncDocs({ projectPath: testDir, agents: true, check: true })).toBe(true);
530
+ expect(await syncDocs({ projectPath: testDir, agents: true, check: true }, io)).toEqual({ kind: 'ok' });
532
531
 
533
532
  fs.rmSync(testDir, { recursive: true, force: true });
534
533
  });
@@ -537,10 +536,10 @@ describe('sync-docs --agents', () => {
537
536
  const testDir = path.join(tempDir, 'preserve-order');
538
537
  createFixture(testDir, { pnpm: true, withCapabilities: true });
539
538
 
540
- await syncDocs({ projectPath: testDir, agents: true });
539
+ await syncDocs({ projectPath: testDir, agents: true }, io);
541
540
  appendBlock(testDir, '<!-- PRESERVE:one -->\n第一块\n<!-- /PRESERVE:one -->');
542
541
  appendBlock(testDir, '<!-- PRESERVE:two -->\n第二块\n<!-- /PRESERVE:two -->');
543
- await syncDocs({ projectPath: testDir, agents: true });
542
+ await syncDocs({ projectPath: testDir, agents: true }, io);
544
543
 
545
544
  const content = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf-8');
546
545
  expect(content.indexOf('PRESERVE:one')).toBeLessThan(content.indexOf('PRESERVE:two'));
@@ -554,20 +553,20 @@ describe('sync-docs --agents', () => {
554
553
  const testDir = path.join(tempDir, 'preserve-drift');
555
554
  createFixture(testDir, { pnpm: true, withCapabilities: true });
556
555
 
557
- await syncDocs({ projectPath: testDir, agents: true });
556
+ await syncDocs({ projectPath: testDir, agents: true }, io);
558
557
  appendBlock(testDir, MANUAL_BLOCK);
559
- await syncDocs({ projectPath: testDir, agents: true }); // 重组为规范形态
558
+ await syncDocs({ projectPath: testDir, agents: true }, io); // 重组为规范形态
560
559
 
561
560
  // 块内手改 → 不报漂移
562
561
  const agentsPath = path.join(testDir, 'AGENTS.md');
563
562
  const edited = fs.readFileSync(agentsPath, 'utf-8').replace('使用者自有内容', '改过的内容');
564
563
  fs.writeFileSync(agentsPath, edited);
565
- expect(await syncDocs({ projectPath: testDir, agents: true, check: true })).toBe(true);
564
+ expect(await syncDocs({ projectPath: testDir, agents: true, check: true }, io)).toEqual({ kind: 'ok' });
566
565
 
567
566
  // 块外手改(生成部分) → 报漂移;重生成后块仍在、块外改动被还原
568
567
  fs.appendFileSync(agentsPath, '\n块外手动追加\n');
569
- expect(await syncDocs({ projectPath: testDir, agents: true, check: true })).toBe(false);
570
- await syncDocs({ projectPath: testDir, agents: true });
568
+ expect(await syncDocs({ projectPath: testDir, agents: true, check: true }, io)).toEqual({ kind: 'fail', reason: expect.stringContaining('文档不是最新') });
569
+ await syncDocs({ projectPath: testDir, agents: true }, io);
571
570
  const regenerated = fs.readFileSync(agentsPath, 'utf-8');
572
571
  expect(regenerated).toContain('改过的内容');
573
572
  expect(regenerated).not.toContain('块外手动追加');
@@ -579,11 +578,11 @@ describe('sync-docs --agents', () => {
579
578
  const testDir = path.join(tempDir, 'preserve-malformed');
580
579
  createFixture(testDir, { pnpm: true, withCapabilities: true });
581
580
 
582
- await syncDocs({ projectPath: testDir, agents: true });
581
+ await syncDocs({ projectPath: testDir, agents: true }, io);
583
582
  appendBlock(testDir, '<!-- PRESERVE:broken -->\n没有结束标记');
584
- await syncDocs({ projectPath: testDir, agents: true });
583
+ await syncDocs({ projectPath: testDir, agents: true }, io);
585
584
 
586
- expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('未闭合'));
585
+ expect(io.outText()).toContain('未闭合');
587
586
  const content = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf-8');
588
587
  expect(content).not.toContain('PRESERVE:broken');
589
588
  expect(content).not.toContain('没有结束标记');
@@ -595,9 +594,9 @@ describe('sync-docs --agents', () => {
595
594
  const testDir = path.join(tempDir, 'preserve-repo-change');
596
595
  createFixture(testDir, { pnpm: true, withCapabilities: true });
597
596
 
598
- await syncDocs({ projectPath: testDir, agents: true });
597
+ await syncDocs({ projectPath: testDir, agents: true }, io);
599
598
  appendBlock(testDir, MANUAL_BLOCK);
600
- await syncDocs({ projectPath: testDir, agents: true });
599
+ await syncDocs({ projectPath: testDir, agents: true }, io);
601
600
 
602
601
  // package.json 删除一个 curated 脚本 → 生成部分变化
603
602
  const pkgPath = path.join(testDir, 'package.json');
@@ -605,13 +604,13 @@ describe('sync-docs --agents', () => {
605
604
  delete pkg.scripts.lint;
606
605
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
607
606
 
608
- expect(await syncDocs({ projectPath: testDir, agents: true, check: true })).toBe(false);
609
- await syncDocs({ projectPath: testDir, agents: true });
607
+ expect(await syncDocs({ projectPath: testDir, agents: true, check: true }, io)).toEqual({ kind: 'fail', reason: expect.stringContaining('文档不是最新') });
608
+ await syncDocs({ projectPath: testDir, agents: true }, io);
610
609
 
611
610
  const content = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf-8');
612
611
  expect(content).not.toContain('pnpm lint');
613
612
  expect(content).toContain(MANUAL_BLOCK);
614
- expect(await syncDocs({ projectPath: testDir, agents: true, check: true })).toBe(true);
613
+ expect(await syncDocs({ projectPath: testDir, agents: true, check: true }, io)).toEqual({ kind: 'ok' });
615
614
 
616
615
  fs.rmSync(testDir, { recursive: true, force: true });
617
616
  });
@@ -630,16 +629,16 @@ describe('sync-docs --agents', () => {
630
629
  '<!-- /PRESERVE:governance -->',
631
630
  ].join('\n');
632
631
 
633
- await syncDocs({ projectPath: testDir, agents: true });
632
+ await syncDocs({ projectPath: testDir, agents: true }, io);
634
633
  appendBlock(testDir, GOVERNANCE_BLOCK);
635
- await syncDocs({ projectPath: testDir, agents: true });
634
+ await syncDocs({ projectPath: testDir, agents: true }, io);
636
635
 
637
636
  const content = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf-8');
638
637
  expect(content).toContain(GOVERNANCE_BLOCK);
639
638
 
640
639
  // 幂等 + --check 通过(块内含 HARNESS_CONSTRAINTS 标记不影响漂移比对)
641
- expect(await syncDocs({ projectPath: testDir, agents: true })).toBe(true);
642
- expect(await syncDocs({ projectPath: testDir, agents: true, check: true })).toBe(true);
640
+ expect(await syncDocs({ projectPath: testDir, agents: true }, io)).toEqual({ kind: 'ok' });
641
+ expect(await syncDocs({ projectPath: testDir, agents: true, check: true }, io)).toEqual({ kind: 'ok' });
643
642
 
644
643
  fs.rmSync(testDir, { recursive: true, force: true });
645
644
  });