@dommaker/harness 1.2.3 → 1.4.0

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