@dommaker/harness 1.3.0 → 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 (517) hide show
  1. package/CHANGELOG.md +8 -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 +29 -214
  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 +21 -15
  53. package/dist/cli/commands/knowledge.d.ts.map +1 -1
  54. package/dist/cli/commands/knowledge.js +214 -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 +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 +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/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 +64 -115
  143. package/dist/core/constraints/checker.js.map +1 -1
  144. package/dist/core/constraints/checkers/capability-sync.d.ts +4 -3
  145. package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
  146. package/dist/core/constraints/checkers/capability-sync.js +29 -69
  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 +37 -63
  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/no-test-simplification.d.ts.map +1 -1
  160. package/dist/core/constraints/checkers/no-test-simplification.js +4 -3
  161. package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
  162. package/dist/core/constraints/checkers/types.d.ts +2 -2
  163. package/dist/core/constraints/context-builder.d.ts +15 -14
  164. package/dist/core/constraints/context-builder.d.ts.map +1 -1
  165. package/dist/core/constraints/context-builder.js +32 -96
  166. package/dist/core/constraints/context-builder.js.map +1 -1
  167. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  168. package/dist/core/constraints/doc-freshness/runner.js +2 -1
  169. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  170. package/dist/core/constraints/git-evidence.d.ts +60 -0
  171. package/dist/core/constraints/git-evidence.d.ts.map +1 -0
  172. package/dist/core/constraints/git-evidence.js +88 -0
  173. package/dist/core/constraints/git-evidence.js.map +1 -0
  174. package/dist/core/constraints/injection-drift.d.ts +3 -20
  175. package/dist/core/constraints/injection-drift.d.ts.map +1 -1
  176. package/dist/core/constraints/injection-drift.js +10 -57
  177. package/dist/core/constraints/injection-drift.js.map +1 -1
  178. package/dist/core/constraints/injection-writer.d.ts +113 -0
  179. package/dist/core/constraints/injection-writer.d.ts.map +1 -0
  180. package/dist/core/constraints/injection-writer.js +228 -0
  181. package/dist/core/constraints/injection-writer.js.map +1 -0
  182. package/dist/core/constraints/usage-report.d.ts +22 -1
  183. package/dist/core/constraints/usage-report.d.ts.map +1 -1
  184. package/dist/core/constraints/usage-report.js +27 -20
  185. package/dist/core/constraints/usage-report.js.map +1 -1
  186. package/dist/core/index.d.ts +2 -2
  187. package/dist/core/index.d.ts.map +1 -1
  188. package/dist/core/index.js +4 -2
  189. package/dist/core/index.js.map +1 -1
  190. package/dist/core/project-config-loader.d.ts +32 -1
  191. package/dist/core/project-config-loader.d.ts.map +1 -1
  192. package/dist/core/project-config-loader.js +39 -9
  193. package/dist/core/project-config-loader.js.map +1 -1
  194. package/dist/core/validators/passes-gate.d.ts +15 -64
  195. package/dist/core/validators/passes-gate.d.ts.map +1 -1
  196. package/dist/core/validators/passes-gate.js +84 -222
  197. package/dist/core/validators/passes-gate.js.map +1 -1
  198. package/dist/core/validators/test-output.d.ts +58 -0
  199. package/dist/core/validators/test-output.d.ts.map +1 -0
  200. package/dist/core/validators/test-output.js +109 -0
  201. package/dist/core/validators/test-output.js.map +1 -0
  202. package/dist/failure/recorder.d.ts +6 -0
  203. package/dist/failure/recorder.d.ts.map +1 -1
  204. package/dist/failure/recorder.js +12 -8
  205. package/dist/failure/recorder.js.map +1 -1
  206. package/dist/gates/acceptance.d.ts +1 -48
  207. package/dist/gates/acceptance.d.ts.map +1 -1
  208. package/dist/gates/acceptance.js +6 -38
  209. package/dist/gates/acceptance.js.map +1 -1
  210. package/dist/gates/checker-gate.d.ts.map +1 -1
  211. package/dist/gates/checker-gate.js +6 -11
  212. package/dist/gates/checker-gate.js.map +1 -1
  213. package/dist/gates/command.d.ts.map +1 -1
  214. package/dist/gates/command.js +10 -22
  215. package/dist/gates/command.js.map +1 -1
  216. package/dist/gates/contract.d.ts.map +1 -1
  217. package/dist/gates/contract.js +22 -61
  218. package/dist/gates/contract.js.map +1 -1
  219. package/dist/gates/performance.d.ts.map +1 -1
  220. package/dist/gates/performance.js +13 -31
  221. package/dist/gates/performance.js.map +1 -1
  222. package/dist/gates/review.d.ts.map +1 -1
  223. package/dist/gates/review.js +21 -53
  224. package/dist/gates/review.js.map +1 -1
  225. package/dist/gates/security.d.ts.map +1 -1
  226. package/dist/gates/security.js +14 -32
  227. package/dist/gates/security.js.map +1 -1
  228. package/dist/gates/types.d.ts +28 -5
  229. package/dist/gates/types.d.ts.map +1 -1
  230. package/dist/gates/types.js +38 -1
  231. package/dist/gates/types.js.map +1 -1
  232. package/dist/hooks/bootstrap.d.ts +2 -0
  233. package/dist/hooks/bootstrap.d.ts.map +1 -1
  234. package/dist/hooks/bootstrap.js +5 -2
  235. package/dist/hooks/bootstrap.js.map +1 -1
  236. package/dist/index.d.ts +1 -2
  237. package/dist/index.d.ts.map +1 -1
  238. package/dist/index.js.map +1 -1
  239. package/dist/knowledge/audit.d.ts +7 -2
  240. package/dist/knowledge/audit.d.ts.map +1 -1
  241. package/dist/knowledge/audit.js +59 -79
  242. package/dist/knowledge/audit.js.map +1 -1
  243. package/dist/knowledge/flywheel-metrics.d.ts +39 -0
  244. package/dist/knowledge/flywheel-metrics.d.ts.map +1 -0
  245. package/dist/knowledge/flywheel-metrics.js +47 -0
  246. package/dist/knowledge/flywheel-metrics.js.map +1 -0
  247. package/dist/knowledge/import.d.ts.map +1 -1
  248. package/dist/knowledge/import.js +4 -14
  249. package/dist/knowledge/import.js.map +1 -1
  250. package/dist/knowledge/index-generator.d.ts.map +1 -1
  251. package/dist/knowledge/index-generator.js +17 -14
  252. package/dist/knowledge/index-generator.js.map +1 -1
  253. package/dist/knowledge/ingest.d.ts.map +1 -1
  254. package/dist/knowledge/ingest.js +9 -4
  255. package/dist/knowledge/ingest.js.map +1 -1
  256. package/dist/knowledge/migration.d.ts.map +1 -1
  257. package/dist/knowledge/migration.js +11 -8
  258. package/dist/knowledge/migration.js.map +1 -1
  259. package/dist/knowledge/reference-tracker.d.ts.map +1 -1
  260. package/dist/knowledge/reference-tracker.js +12 -9
  261. package/dist/knowledge/reference-tracker.js.map +1 -1
  262. package/dist/knowledge/store.d.ts +15 -0
  263. package/dist/knowledge/store.d.ts.map +1 -1
  264. package/dist/knowledge/store.js +59 -10
  265. package/dist/knowledge/store.js.map +1 -1
  266. package/dist/monitoring/context-tracker.d.ts.map +1 -1
  267. package/dist/monitoring/context-tracker.js +11 -20
  268. package/dist/monitoring/context-tracker.js.map +1 -1
  269. package/dist/monitoring/index.d.ts +1 -1
  270. package/dist/monitoring/index.d.ts.map +1 -1
  271. package/dist/monitoring/index.js +1 -2
  272. package/dist/monitoring/index.js.map +1 -1
  273. package/dist/monitoring/trace-analyzer.d.ts +15 -0
  274. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  275. package/dist/monitoring/trace-analyzer.js +19 -2
  276. package/dist/monitoring/trace-analyzer.js.map +1 -1
  277. package/dist/monitoring/traces.d.ts +21 -2
  278. package/dist/monitoring/traces.d.ts.map +1 -1
  279. package/dist/monitoring/traces.js +38 -29
  280. package/dist/monitoring/traces.js.map +1 -1
  281. package/dist/release/index.d.ts +2 -2
  282. package/dist/release/index.d.ts.map +1 -1
  283. package/dist/release/index.js +1 -5
  284. package/dist/release/index.js.map +1 -1
  285. package/dist/sdd/index-generator.js +10 -11
  286. package/dist/sdd/index-generator.js.map +1 -1
  287. package/dist/test-setup/project-fixture.d.ts +48 -0
  288. package/dist/test-setup/project-fixture.d.ts.map +1 -0
  289. package/dist/test-setup/project-fixture.js +106 -0
  290. package/dist/test-setup/project-fixture.js.map +1 -0
  291. package/dist/types/constraint.d.ts +0 -18
  292. package/dist/types/constraint.d.ts.map +1 -1
  293. package/dist/types/constraint.js.map +1 -1
  294. package/dist/types/passes-gate.d.ts +10 -25
  295. package/dist/types/passes-gate.d.ts.map +1 -1
  296. package/dist/types/project-config.d.ts +1 -1
  297. package/dist/types/session.d.ts +6 -5
  298. package/dist/types/session.d.ts.map +1 -1
  299. package/dist/utils/exec.d.ts +10 -0
  300. package/dist/utils/exec.d.ts.map +1 -1
  301. package/dist/utils/exec.js +12 -0
  302. package/dist/utils/exec.js.map +1 -1
  303. package/dist/utils/file-walk.d.ts +9 -1
  304. package/dist/utils/file-walk.d.ts.map +1 -1
  305. package/dist/utils/file-walk.js +10 -1
  306. package/dist/utils/file-walk.js.map +1 -1
  307. package/dist/utils/frontmatter.d.ts +73 -0
  308. package/dist/utils/frontmatter.d.ts.map +1 -0
  309. package/dist/utils/frontmatter.js +141 -0
  310. package/dist/utils/frontmatter.js.map +1 -0
  311. package/dist/utils/jsonl.d.ts +92 -0
  312. package/dist/utils/jsonl.d.ts.map +1 -0
  313. package/dist/utils/jsonl.js +166 -0
  314. package/dist/utils/jsonl.js.map +1 -0
  315. package/dist/utils/package-version.d.ts +16 -0
  316. package/dist/utils/package-version.d.ts.map +1 -0
  317. package/dist/utils/package-version.js +63 -0
  318. package/dist/utils/package-version.js.map +1 -0
  319. package/package.json +1 -1
  320. package/src/CONTEXT.md +7 -2
  321. package/src/__tests__/acceptance-gate.test.ts +3 -1
  322. package/src/__tests__/checker-extra.test.ts +7 -1218
  323. package/src/__tests__/context-files-resolution.test.ts +116 -0
  324. package/src/__tests__/effective-constraints.test.ts +10 -13
  325. package/src/__tests__/exec.test.ts +35 -1
  326. package/src/__tests__/layering.test.ts +152 -0
  327. package/src/__tests__/passes-gate-extra.test.ts +0 -145
  328. package/src/__tests__/passes-gate.test.ts +142 -106
  329. package/src/__tests__/skip-semantics.test.ts +5 -9
  330. package/src/cli/__tests__/command-contract.test.ts +170 -0
  331. package/src/cli/command-contract.ts +108 -0
  332. package/src/cli/commands/CONTEXT.md +22 -7
  333. package/src/cli/commands/__tests__/acceptance.test.ts +40 -33
  334. package/src/cli/commands/__tests__/analyze-sessions.test.ts +35 -29
  335. package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +101 -0
  336. package/src/cli/commands/__tests__/check-drift.test.ts +27 -34
  337. package/src/cli/commands/__tests__/check-skip-output.test.ts +21 -24
  338. package/src/cli/commands/__tests__/check.test.ts +378 -382
  339. package/src/cli/commands/__tests__/command.test.ts +85 -70
  340. package/src/cli/commands/__tests__/constraints-report.test.ts +100 -71
  341. package/src/cli/commands/__tests__/constraints-retire.test.ts +152 -95
  342. package/src/cli/commands/__tests__/contract.test.ts +27 -34
  343. package/src/cli/commands/__tests__/failure.test.ts +31 -34
  344. package/src/cli/commands/__tests__/init-injection.test.ts +58 -28
  345. package/src/cli/commands/__tests__/init.test.ts +77 -48
  346. package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +108 -0
  347. package/src/cli/commands/__tests__/knowledge.test.ts +69 -43
  348. package/src/cli/commands/__tests__/passes-gate.test.ts +43 -45
  349. package/src/cli/commands/__tests__/performance.test.ts +22 -23
  350. package/src/cli/commands/__tests__/posteval-plan.test.ts +39 -38
  351. package/src/cli/commands/__tests__/project-path-anchoring.test.ts +157 -0
  352. package/src/cli/commands/__tests__/project-path-convention.test.ts +169 -0
  353. package/src/cli/commands/__tests__/registry.test.ts +69 -35
  354. package/src/cli/commands/__tests__/release.test.ts +209 -80
  355. package/src/cli/commands/__tests__/report-extra.test.ts +16 -16
  356. package/src/cli/commands/__tests__/report.test.ts +25 -25
  357. package/src/cli/commands/__tests__/review.test.ts +33 -31
  358. package/src/cli/commands/__tests__/security.test.ts +23 -27
  359. package/src/cli/commands/__tests__/spec-baseline-check.test.ts +20 -20
  360. package/src/cli/commands/__tests__/spec.test.ts +41 -29
  361. package/src/cli/commands/__tests__/status-extra.test.ts +18 -18
  362. package/src/cli/commands/__tests__/status-skipped-lines.test.ts +72 -0
  363. package/src/cli/commands/__tests__/status.test.ts +34 -34
  364. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +77 -78
  365. package/src/cli/commands/__tests__/sync-docs.test.ts +72 -74
  366. package/src/cli/commands/__tests__/update-user-model.test.ts +62 -33
  367. package/src/cli/commands/__tests__/validate.test.ts +27 -29
  368. package/src/cli/commands/acceptance.ts +35 -24
  369. package/src/cli/commands/analyze-sessions.ts +25 -24
  370. package/src/cli/commands/check.ts +88 -65
  371. package/src/cli/commands/command.ts +30 -17
  372. package/src/cli/commands/constraints-report.ts +37 -34
  373. package/src/cli/commands/constraints-retire.ts +58 -55
  374. package/src/cli/commands/constraints.ts +10 -27
  375. package/src/cli/commands/contract.ts +51 -42
  376. package/src/cli/commands/definitions.ts +36 -222
  377. package/src/cli/commands/failure.ts +33 -26
  378. package/src/cli/commands/init.ts +166 -220
  379. package/src/cli/commands/knowledge.ts +225 -203
  380. package/src/cli/commands/passes-gate.ts +58 -68
  381. package/src/cli/commands/performance.ts +23 -18
  382. package/src/cli/commands/posteval-plan.ts +29 -15
  383. package/src/cli/commands/release.ts +70 -59
  384. package/src/cli/commands/report.ts +9 -5
  385. package/src/cli/commands/review.ts +39 -29
  386. package/src/cli/commands/sdd.ts +7 -5
  387. package/src/cli/commands/security.ts +35 -26
  388. package/src/cli/commands/spec-baseline-check.ts +16 -11
  389. package/src/cli/commands/spec.ts +66 -53
  390. package/src/cli/commands/status.ts +59 -53
  391. package/src/cli/commands/sync-docs/agents-syncer.ts +5 -2
  392. package/src/cli/commands/sync-docs/context-syncer.ts +3 -2
  393. package/src/cli/commands/sync-docs/index.ts +73 -100
  394. package/src/cli/commands/sync-docs/project-reader.ts +17 -18
  395. package/src/cli/commands/update-user-model.ts +33 -45
  396. package/src/cli/commands/validate.ts +31 -25
  397. package/src/cli/session-mining/__tests__/transcript.test.ts +118 -0
  398. package/src/cli/session-mining/transcript.ts +26 -8
  399. package/src/context/session-manager.ts +19 -33
  400. package/src/core/CONTEXT.md +9 -6
  401. package/src/core/constraints/__tests__/capabilities-reconcile.test.ts +200 -0
  402. package/src/core/constraints/__tests__/capability-counts.test.ts +94 -0
  403. package/src/core/constraints/__tests__/git-evidence.test.ts +208 -0
  404. package/src/core/constraints/__tests__/injection-drift.test.ts +3 -10
  405. package/src/core/constraints/__tests__/injection-writer.test.ts +231 -0
  406. package/src/core/constraints/__tests__/trace-injection.test.ts +71 -0
  407. package/src/core/constraints/__tests__/usage-report.test.ts +112 -0
  408. package/src/core/constraints/agent-prompt-renderer.ts +2 -6
  409. package/src/core/constraints/capabilities-parser.ts +53 -35
  410. package/src/core/constraints/capabilities-reconcile.ts +166 -0
  411. package/src/core/constraints/checker.ts +75 -130
  412. package/src/core/constraints/checkers/__tests__/capability-sync.test.ts +214 -0
  413. package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +77 -0
  414. package/src/core/constraints/checkers/__tests__/docs-freshness.test.ts +117 -0
  415. package/src/{__tests__ → core/constraints/checkers/__tests__}/governance-presence.test.ts +83 -9
  416. package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +50 -0
  417. package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +76 -0
  418. package/src/{__tests__ → core/constraints/checkers/__tests__}/no-hardcoded-credentials.test.ts +4 -3
  419. package/src/core/constraints/checkers/__tests__/no-test-simplification.test.ts +58 -0
  420. package/src/core/constraints/checkers/__tests__/three-state-consistency.test.ts +145 -0
  421. package/src/core/constraints/checkers/capability-sync.ts +29 -88
  422. package/src/core/constraints/checkers/context-doc-sync.ts +15 -25
  423. package/src/core/constraints/checkers/docs-freshness.ts +40 -69
  424. package/src/core/constraints/checkers/governance-presence.ts +7 -25
  425. package/src/core/constraints/checkers/no-test-simplification.ts +4 -3
  426. package/src/core/constraints/checkers/types.ts +2 -2
  427. package/src/core/constraints/context-builder.ts +39 -91
  428. package/src/core/constraints/doc-freshness/runner.ts +2 -1
  429. package/src/core/constraints/git-evidence.ts +120 -0
  430. package/src/core/constraints/injection-drift.ts +13 -60
  431. package/src/core/constraints/injection-writer.ts +212 -0
  432. package/src/core/constraints/usage-report.ts +36 -17
  433. package/src/core/index.ts +4 -2
  434. package/src/core/project-config-loader.ts +49 -9
  435. package/src/core/validators/__tests__/passes-gate.test.ts +2 -56
  436. package/src/core/validators/__tests__/test-output.test.ts +372 -0
  437. package/src/core/validators/passes-gate.ts +86 -248
  438. package/src/core/validators/test-output.ts +128 -0
  439. package/src/failure/__tests__/recorder.test.ts +168 -0
  440. package/src/failure/recorder.ts +12 -10
  441. package/src/gates/CONTEXT.md +6 -3
  442. package/src/gates/__tests__/acceptance-types-single-source.test.ts +68 -0
  443. package/src/gates/__tests__/acceptance.test.ts +4 -86
  444. package/src/gates/__tests__/effective-gates.test.ts +3 -8
  445. package/src/gates/__tests__/test-verdict-consistency.test.ts +140 -0
  446. package/src/gates/acceptance.ts +19 -84
  447. package/src/gates/checker-gate.ts +8 -7
  448. package/src/gates/command.ts +10 -16
  449. package/src/gates/contract.ts +22 -61
  450. package/src/gates/performance.ts +10 -22
  451. package/src/gates/review.ts +21 -41
  452. package/src/gates/security.ts +10 -22
  453. package/src/gates/types.ts +70 -5
  454. package/src/hooks/CONTEXT.md +1 -1
  455. package/src/hooks/bootstrap.ts +5 -2
  456. package/src/index.ts +0 -4
  457. package/src/knowledge/CONTEXT.md +8 -3
  458. package/src/knowledge/__tests__/audit-labels.test.ts +44 -0
  459. package/src/knowledge/__tests__/flywheel-metrics.test.ts +135 -0
  460. package/src/knowledge/__tests__/import.test.ts +44 -0
  461. package/src/knowledge/__tests__/index-generator.test.ts +53 -0
  462. package/src/knowledge/__tests__/ingest.test.ts +85 -0
  463. package/src/knowledge/__tests__/migration.test.ts +47 -0
  464. package/src/knowledge/__tests__/reference-tracker.test.ts +16 -0
  465. package/src/knowledge/__tests__/store.test.ts +194 -0
  466. package/src/knowledge/audit.ts +67 -71
  467. package/src/knowledge/flywheel-metrics.ts +71 -0
  468. package/src/knowledge/import.ts +4 -18
  469. package/src/knowledge/index-generator.ts +19 -14
  470. package/src/knowledge/ingest.ts +9 -4
  471. package/src/knowledge/migration.ts +11 -8
  472. package/src/knowledge/reference-tracker.ts +12 -8
  473. package/src/knowledge/store.ts +64 -11
  474. package/src/monitoring/CONTEXT.md +8 -3
  475. package/src/monitoring/__tests__/skipped-lines-report.test.ts +178 -0
  476. package/src/monitoring/__tests__/traces.test.ts +57 -0
  477. package/src/monitoring/context-tracker.ts +11 -23
  478. package/src/monitoring/index.ts +1 -1
  479. package/src/monitoring/trace-analyzer.ts +21 -2
  480. package/src/monitoring/traces.ts +39 -36
  481. package/src/release/CONTEXT.md +1 -0
  482. package/src/release/__tests__/barrel.test.ts +60 -0
  483. package/src/release/index.ts +2 -13
  484. package/src/sdd/__tests__/index-generator.test.ts +71 -0
  485. package/src/sdd/index-generator.ts +9 -9
  486. package/src/test-setup/__tests__/project-fixture.test.ts +178 -0
  487. package/src/test-setup/project-fixture.ts +87 -0
  488. package/src/tools/CONTEXT.md +6 -0
  489. package/src/tools/__tests__/registry.test.ts +221 -0
  490. package/src/types/__tests__/types.test.ts +4 -5
  491. package/src/types/constraint.ts +0 -25
  492. package/src/types/passes-gate.ts +10 -30
  493. package/src/types/project-config.ts +1 -1
  494. package/src/types/session.ts +5 -5
  495. package/src/utils/__tests__/frontmatter.test.ts +206 -0
  496. package/src/utils/__tests__/jsonl-skip-disposition.test.ts +175 -0
  497. package/src/utils/__tests__/jsonl.test.ts +187 -0
  498. package/src/utils/__tests__/package-version.test.ts +97 -0
  499. package/src/utils/exec.ts +15 -0
  500. package/src/utils/file-walk.ts +11 -2
  501. package/src/utils/frontmatter.ts +121 -0
  502. package/src/utils/jsonl.ts +167 -0
  503. package/src/utils/package-version.ts +27 -0
  504. package/dist/cli/commands/doc-freshness-check.d.ts +0 -53
  505. package/dist/cli/commands/doc-freshness-check.d.ts.map +0 -1
  506. package/dist/cli/commands/doc-freshness-check.js +0 -352
  507. package/dist/cli/commands/doc-freshness-check.js.map +0 -1
  508. package/dist/types/index.d.ts +0 -14
  509. package/dist/types/index.d.ts.map +0 -1
  510. package/dist/types/index.js +0 -39
  511. package/dist/types/index.js.map +0 -1
  512. package/src/__tests__/long-running.test.ts +0 -17
  513. package/src/__tests__/passes-gate-extension.test.ts +0 -151
  514. package/src/cli/commands/__tests__/doc-freshness-check.test.ts +0 -168
  515. package/src/cli/commands/doc-freshness-check.ts +0 -386
  516. package/src/spec/CONTEXT.md +0 -11
  517. package/src/types/index.ts +0 -47
@@ -9,8 +9,11 @@ import * as fs from 'fs';
9
9
  import * as path from 'path';
10
10
  import { TraceCollector } from '../../monitoring/traces';
11
11
  import { TraceAnalyzer } from '../../monitoring/trace-analyzer';
12
+ import { readJsonl } from '../../utils/jsonl';
12
13
  import { DEFAULT_TRACE_FILE } from '../../types/trace';
14
+ import type { ExecutionTrace } from '../../types/trace';
13
15
  import type { TraceSummary, TraceAnomaly } from '../../types/trace';
16
+ import { log, logError, processIO, type CommandIO, type CommandResult } from '../command-contract';
14
17
 
15
18
  export interface StatusOptions {
16
19
  /** 项目路径 */
@@ -26,42 +29,38 @@ export interface StatusOptions {
26
29
  /**
27
30
  * 显示 Harness 状态
28
31
  */
29
- export async function status(options: StatusOptions): Promise<void> {
32
+ export async function status(options: StatusOptions, io: CommandIO = processIO): Promise<CommandResult> {
30
33
  const projectPath = options.projectPath || process.cwd();
31
34
  const harnessDir = path.join(projectPath, '.harness');
32
35
  const tracesPath = path.join(projectPath, DEFAULT_TRACE_FILE);
33
36
  const statePath = path.join(harnessDir, '.state.json');
34
37
 
35
- console.log(chalk.blue('📊 Harness 状态'));
36
- console.log();
38
+ log(io, chalk.blue('📊 Harness 状态'));
39
+ log(io);
37
40
 
38
41
  // 检查是否初始化
39
42
  if (!fs.existsSync(harnessDir)) {
40
- console.log(chalk.yellow('⚠️ 未初始化'));
41
- console.log(chalk.gray('💡 运行 harness init 初始化项目'));
42
- return;
43
+ log(io, chalk.yellow('⚠️ 未初始化'));
44
+ log(io, chalk.gray('💡 运行 harness init 初始化项目'));
45
+ return { kind: 'ok' };
43
46
  }
44
47
 
45
48
  // 检查 trace 文件
46
49
  if (!fs.existsSync(tracesPath)) {
47
- console.log(chalk.yellow('⚠️ 暂无 Trace 记录'));
48
- console.log(chalk.gray('💡 运行 harness check 开始记录'));
49
- return;
50
+ log(io, chalk.yellow('⚠️ 暂无 Trace 记录'));
51
+ log(io, chalk.gray('💡 运行 harness check 开始记录'));
52
+ return { kind: 'ok' };
50
53
  }
51
54
 
52
- // 读取 trace 文件
53
- const tracesContent = fs.readFileSync(tracesPath, 'utf-8');
54
- const lines = tracesContent.trim().split('\n').filter(Boolean);
55
- const traceCount = lines.length;
56
-
57
- // 解析 traces
58
- const traces = lines.map(line => {
59
- try {
60
- return JSON.parse(line);
61
- } catch {
62
- return null;
63
- }
64
- }).filter(Boolean);
55
+ // 读取 trace 文件:坏行策略 skip(原 null + filter(Boolean) 语义不变,harness#82)
56
+ // traceCount 保持原始非空行数口径(合法 + 跳过),与改前显示逐字节一致;
57
+ // filter(Boolean) 沿用原语义:parse 成功但值为 falsy 的行(如 "null")不进分析
58
+ // 计数去向:harness#100——skippedLines 另起一行走 stderr 告知(口径不改,只补告知)。
59
+ // 不经 TraceCollector.readReport():本命令按 -p 的 projectPath 直读读链正本,且要的是
60
+ // records + skippedLines 的原始行数口径;下面另建的 collector/analyzer 只用于 summarize/detectAnomalies
61
+ const { records, skippedLines } = readJsonl<ExecutionTrace>(tracesPath, 'skip');
62
+ const traces = records.filter(Boolean);
63
+ const traceCount = records.length + skippedLines;
65
64
 
66
65
  // 创建收集器和分析器
67
66
  const collector = new TraceCollector({ traceFile: tracesPath });
@@ -72,71 +71,77 @@ export async function status(options: StatusOptions): Promise<void> {
72
71
  const anomalies = analyzer.detectAnomalies(summaries);
73
72
 
74
73
  // 基本统计
75
- console.log(chalk.gray(`记录数: ${traceCount} 条`));
76
- console.log();
74
+ log(io, chalk.gray(`记录数: ${traceCount} 条`));
75
+ log(io);
76
+
77
+ // 数据不完整必须让看报告的人知道(harness#100):诊断信息走 stderr,
78
+ // stdout 报告体口径与字节不变;无损坏零噪声
79
+ if (skippedLines > 0) {
80
+ logError(io, `⚠️ trace 文件有 ${skippedLines} 行损坏已跳过,上述记录数含坏行,统计只基于其余合法记录`);
81
+ }
77
82
 
78
83
  if (options.anomalies) {
79
84
  // 只显示异常
80
85
  if (anomalies.length === 0) {
81
- console.log(chalk.green('✅ 未发现异常'));
86
+ log(io, chalk.green('✅ 未发现异常'));
82
87
  } else {
83
- console.log(chalk.yellow(`⚠️ 发现 ${anomalies.length} 个异常:`));
84
- console.log();
88
+ log(io, chalk.yellow(`⚠️ 发现 ${anomalies.length} 个异常:`));
89
+ log(io);
85
90
  anomalies.forEach((a: TraceAnomaly) => {
86
- console.log(chalk.yellow(` ${a.constraintId}`));
87
- console.log(chalk.gray(` 类型: ${a.type}`));
88
- console.log(chalk.gray(` 当前值: ${a.data?.currentRate ?? (a as any).current ?? 'N/A'}`));
89
- console.log(chalk.gray(` 阈值: ${a.data?.threshold ?? (a as any).threshold ?? 'N/A'}`));
90
- console.log();
91
+ log(io, chalk.yellow(` ${a.constraintId}`));
92
+ log(io, chalk.gray(` 类型: ${a.type}`));
93
+ log(io, chalk.gray(` 当前值: ${a.data?.currentRate ?? (a as any).current ?? 'N/A'}`));
94
+ log(io, chalk.gray(` 阈值: ${a.data?.threshold ?? (a as any).threshold ?? 'N/A'}`));
95
+ log(io);
91
96
  });
92
97
 
93
98
  // 下一步建议
94
- console.log(chalk.blue('💡 下一步建议:'));
95
- console.log(chalk.gray(' 运行 harness status --detail 查看详情'));
99
+ log(io, chalk.blue('💡 下一步建议:'));
100
+ log(io, chalk.gray(' 运行 harness status --detail 查看详情'));
96
101
  }
97
- return;
102
+ return { kind: 'ok' };
98
103
  }
99
104
 
100
105
  // 显示约束统计
101
- console.log(chalk.blue('📈 约束统计:'));
102
- console.log();
106
+ log(io, chalk.blue('📈 约束统计:'));
107
+ log(io);
103
108
 
104
109
  // Iron Laws
105
110
  const ironLawSummaries = summaries.filter((s: TraceSummary) => s.level === 'iron_law');
106
111
  if (ironLawSummaries.length > 0) {
107
- console.log(chalk.red('🔴 Iron Laws:'));
112
+ log(io, chalk.red('🔴 Iron Laws:'));
108
113
  ironLawSummaries.forEach((s: TraceSummary) => {
109
114
  const status = s.passRate >= 1 ? '✅' : '❌';
110
- console.log(` ${status} ${s.constraintId}`);
115
+ log(io, ` ${status} ${s.constraintId}`);
111
116
  if (options.detail) {
112
- console.log(chalk.gray(` 检查: ${s.totalChecks} | 通过: ${(s.passRate * 100).toFixed(0)}% | 失败: ${(s.failRate * 100).toFixed(0)}%`));
117
+ log(io, chalk.gray(` 检查: ${s.totalChecks} | 通过: ${(s.passRate * 100).toFixed(0)}% | 失败: ${(s.failRate * 100).toFixed(0)}%`));
113
118
  }
114
119
  });
115
- console.log();
120
+ log(io);
116
121
  }
117
122
 
118
123
  // Guidelines
119
124
  const guidelineSummaries = summaries.filter((s: TraceSummary) => s.level === 'guideline');
120
125
  if (guidelineSummaries.length > 0) {
121
- console.log(chalk.yellow('🟡 Guidelines:'));
126
+ log(io, chalk.yellow('🟡 Guidelines:'));
122
127
  guidelineSummaries.forEach((s: TraceSummary) => {
123
128
  const status = s.passRate >= 0.8 ? '✅' : s.passRate >= 0.5 ? '⚠️' : '❌';
124
- console.log(` ${status} ${s.constraintId}`);
129
+ log(io, ` ${status} ${s.constraintId}`);
125
130
  if (options.detail) {
126
- console.log(chalk.gray(` 检查: ${s.totalChecks} | 通过: ${(s.passRate * 100).toFixed(0)}% | 失败: ${(s.failRate * 100).toFixed(0)}%`));
131
+ log(io, chalk.gray(` 检查: ${s.totalChecks} | 通过: ${(s.passRate * 100).toFixed(0)}% | 失败: ${(s.failRate * 100).toFixed(0)}%`));
127
132
  }
128
133
  });
129
- console.log();
134
+ log(io);
130
135
  }
131
136
 
132
137
  // 异常检测
133
138
  if (anomalies.length > 0) {
134
- console.log(chalk.yellow(`⚠️ 发现 ${anomalies.length} 个异常`));
135
- console.log();
139
+ log(io, chalk.yellow(`⚠️ 发现 ${anomalies.length} 个异常`));
140
+ log(io);
136
141
  anomalies.slice(0, 3).forEach((a: TraceAnomaly) => {
137
- console.log(chalk.yellow(` - ${a.constraintId}: ${a.type}`));
142
+ log(io, chalk.yellow(` - ${a.constraintId}: ${a.type}`));
138
143
  });
139
- console.log();
144
+ log(io);
140
145
  }
141
146
 
142
147
  // 更新状态文件
@@ -147,12 +152,13 @@ export async function status(options: StatusOptions): Promise<void> {
147
152
  fs.writeFileSync(statePath, JSON.stringify(state, null, 2));
148
153
 
149
154
  // 下一步建议
150
- console.log(chalk.blue('💡 下一步建议:'));
155
+ log(io, chalk.blue('💡 下一步建议:'));
151
156
  if (anomalies.length > 0) {
152
- console.log(chalk.gray(' harness status --detail # 查看详细状态'));
157
+ log(io, chalk.gray(' harness status --detail # 查看详细状态'));
153
158
  } else if (traceCount >= 100) {
154
- console.log(chalk.gray(' • 状态良好,继续保持!'));
159
+ log(io, chalk.gray(' • 状态良好,继续保持!'));
155
160
  } else {
156
- console.log(chalk.gray(' • 继续积累数据,记录越多分析越准确'));
161
+ log(io, chalk.gray(' • 继续积累数据,记录越多分析越准确'));
157
162
  }
163
+ return { kind: 'ok' };
158
164
  }
@@ -6,6 +6,8 @@ import * as fs from 'fs/promises';
6
6
  import { existsSync } from 'fs';
7
7
  import * as path from 'path';
8
8
  import { loadRawProjectConfig } from '../../../core/project-config-loader';
9
+ import { hasGovernanceContract } from '../../../core/constraints/injection-writer';
10
+ import { DEFAULT_SKIP_DIRS } from '../../../utils/file-walk';
9
11
  import {
10
12
  readPackageJsonLite,
11
13
  getConfigDescription,
@@ -222,7 +224,8 @@ async function getGovernanceInfo(projectPath: string): Promise<GovernanceInfo> {
222
224
 
223
225
  try {
224
226
  const claude = await fs.readFile(path.join(projectPath, 'CLAUDE.md'), 'utf-8');
225
- if (/^##\s+Governance Rules/m.test(claude) || claude.includes('HARNESS_CONSTRAINTS_START')) {
227
+ // 契约在场判定收口 injection-writer.hasGovernanceContract(#83,与 presence/landing 同谓词)
228
+ if (hasGovernanceContract(claude)) {
226
229
  info.hasClaudeGovernance = true;
227
230
  info.ironLaws = countConstraintBullets(claude, 'Iron Laws');
228
231
  info.guidelines = countConstraintBullets(claude, 'Guidelines');
@@ -293,7 +296,7 @@ async function countContextDocs(projectPath: string, srcDirs: string[]): Promise
293
296
  }
294
297
  for (const entry of entries) {
295
298
  if (!entry.isDirectory()) continue;
296
- if (entry.name === 'node_modules' || entry.name === '__tests__' || entry.name === 'dist') continue;
299
+ if (DEFAULT_SKIP_DIRS.includes(entry.name)) continue;
297
300
  const entryPath = path.join(dir, entry.name);
298
301
  if (existsSync(path.join(entryPath, 'CONTEXT.md'))) {
299
302
  found.add(entryPath);
@@ -4,6 +4,7 @@
4
4
 
5
5
  import * as fs from 'fs/promises';
6
6
  import * as path from 'path';
7
+ import { DEFAULT_SKIP_DIRS } from '../../../utils/file-walk';
7
8
 
8
9
  /**
9
10
  * 创建 CONTEXT.md 模板
@@ -53,7 +54,7 @@ export async function findExistingContextFiles(projectPath: string, srcDirs: str
53
54
  return;
54
55
  }
55
56
  for (const entry of entries) {
56
- if (entry === 'node_modules' || entry === '__tests__' || entry === 'dist') continue;
57
+ if (DEFAULT_SKIP_DIRS.includes(entry)) continue;
57
58
  const entryPath = path.join(dir, entry);
58
59
  const stat = await fs.stat(entryPath);
59
60
  if (stat.isDirectory()) {
@@ -90,7 +91,7 @@ export async function getLatestTsMtime(dirPath: string): Promise<number | null>
90
91
  return;
91
92
  }
92
93
  for (const entry of entries) {
93
- if (entry === 'node_modules' || entry === '__tests__' || entry === 'dist') continue;
94
+ if (DEFAULT_SKIP_DIRS.includes(entry)) continue;
94
95
  const entryPath = path.join(dir, entry);
95
96
  const stat = await fs.stat(entryPath);
96
97
  if (stat.isDirectory()) {
@@ -17,12 +17,14 @@ import * as fs from 'fs/promises';
17
17
  import { existsSync } from 'fs';
18
18
  import * as path from 'path';
19
19
  import {
20
- aggregateToSourceSubdir,
21
- readCapabilitiesEntries,
22
20
  isCapabilityListingFormat,
23
21
  checkCapabilityCounts,
24
22
  updateCapabilityCounts,
23
+ type CapabilityDefinitionSource,
25
24
  } from '../../../core/constraints/capabilities-parser';
25
+ import { COMMAND_DEFINITIONS } from '../definitions';
26
+ import { GATE_DEFINITIONS } from '../../../gates/definitions';
27
+ import { reconcileCapabilities } from '../../../core/constraints/capabilities-reconcile';
26
28
  import { detectSourceRoots } from '../../../utils/detect-source-roots';
27
29
  import { getCapabilitiesMode } from '../../../core/project-config-loader';
28
30
  import { getSourceDirs, scanSourceModules, getRequiredContextDirs } from './project-reader';
@@ -35,6 +37,7 @@ import {
35
37
  import { createContextMd, findExistingContextFiles, getLatestTsMtime } from './context-syncer';
36
38
  import { buildAgentsMd } from './agents-syncer';
37
39
  import { extractPreserveBlocks, composeAgentsMd } from './preserve-block';
40
+ import { log, processIO, type CommandIO, type CommandResult } from '../../command-contract';
38
41
 
39
42
  export interface SyncDocsOptions {
40
43
  /** 项目路径 */
@@ -51,12 +54,28 @@ export interface SyncDocsOptions {
51
54
  compact?: boolean;
52
55
  }
53
56
 
57
+ /**
58
+ * 能力清单计数源(harness#88)
59
+ *
60
+ * core 的 capabilities-parser 不再值导入定义表(单向分层),命令/门禁计数由
61
+ * cli 侧在此组装后注入;两张表都是纯数据模块(ADR-0002 命令形状单一来源)。
62
+ */
63
+ const CAPABILITY_DEFINITIONS: CapabilityDefinitionSource = {
64
+ commands: COMMAND_DEFINITIONS,
65
+ gates: GATE_DEFINITIONS,
66
+ };
67
+
54
68
  /**
55
69
  * 同步文档
56
70
  */
57
- export async function syncDocs(options: SyncDocsOptions): Promise<boolean> {
71
+ export async function syncDocs(
72
+ options: SyncDocsOptions,
73
+ io: CommandIO = processIO,
74
+ ): Promise<CommandResult> {
58
75
  const projectPath = options.projectPath || process.cwd();
59
76
  const isCheck = options.check === true;
77
+ // 漂移译成 kind:--check 下判定失败;写入模式下漂移已被修掉 → 退出码面不变(历史行为)
78
+ const drift = (reason: string): CommandResult => (isCheck ? { kind: 'fail', reason } : { kind: 'ok' });
60
79
  const isJson = options.json === true;
61
80
  const capsMode = getCapabilitiesMode(projectPath);
62
81
 
@@ -68,22 +87,22 @@ export async function syncDocs(options: SyncDocsOptions): Promise<boolean> {
68
87
  const compacted = compactCapabilitiesContent(content);
69
88
  if (compacted !== content) {
70
89
  await fs.writeFile(capsPath, compacted, 'utf-8');
71
- if (!isJson) console.log(chalk.green('✅ 已将 CAPABILITIES.md 文件表格折叠为目录条目'));
90
+ if (!isJson) log(io, chalk.green('✅ 已将 CAPABILITIES.md 文件表格折叠为目录条目'));
72
91
  } else {
73
- if (!isJson) console.log(chalk.green('✅ CAPABILITIES.md 无需折叠'));
92
+ if (!isJson) log(io, chalk.green('✅ CAPABILITIES.md 无需折叠'));
74
93
  }
75
- return true;
94
+ return { kind: 'ok' };
76
95
  } catch {
77
- if (!isJson) console.log(chalk.red('❌ CAPABILITIES.md 不存在,无法折叠'));
78
- return false;
96
+ if (!isJson) log(io, chalk.red('❌ CAPABILITIES.md 不存在,无法折叠'));
97
+ return drift('CAPABILITIES.md 不存在,无法折叠');
79
98
  }
80
99
  }
81
100
 
82
101
  if (!isJson) {
83
102
  if (isCheck) {
84
- console.log(chalk.blue('🔍 检查文档新鲜度...'));
103
+ log(io, chalk.blue('🔍 检查文档新鲜度...'));
85
104
  } else {
86
- console.log(chalk.blue('📝 同步文档...'));
105
+ log(io, chalk.blue('📝 同步文档...'));
87
106
  }
88
107
  }
89
108
 
@@ -103,7 +122,7 @@ export async function syncDocs(options: SyncDocsOptions): Promise<boolean> {
103
122
  currentModules.push(...modules);
104
123
  } catch {
105
124
  if (!isJson) {
106
- console.log(chalk.yellow(`⚠️ 未找到 ${srcDir} 目录,跳过`));
125
+ log(io, chalk.yellow(`⚠️ 未找到 ${srcDir} 目录,跳过`));
107
126
  }
108
127
  }
109
128
  }
@@ -130,77 +149,31 @@ export async function syncDocs(options: SyncDocsOptions): Promise<boolean> {
130
149
 
131
150
  if (capsIsCapabilityListing) {
132
151
  // 能力清单格式:委托 FreshnessRunner 对比计数
133
- const countCheck = checkCapabilityCounts(projectPath);
152
+ const countCheck = checkCapabilityCounts(projectPath, CAPABILITY_DEFINITIONS);
134
153
  capCountMismatches = countCheck.mismatches;
135
154
  // 不填充 result.added/removed(文件级对比不适用于此格式)
136
155
  } else {
137
- // 传统的文件表格格式
156
+ // 传统的文件表格格式:覆盖/幽灵判定统一走 capabilities-reconcile(ADR-0009),
157
+ // 与 capability_sync / docs_freshness 检查器共享同一份规则,check 与 fix 口径必然一致
138
158
  const getBasename = (f: string) => {
139
159
  const clean = f.endsWith('/') ? f.slice(0, -1) : f;
140
160
  return clean.split('/').pop()!;
141
161
  };
142
- const currentBasenames = currentModules.map(m => getBasename(m.file));
143
- // 目录条目(如 src/、agents/)按前缀覆盖其中的文件,且不参与 removed 对比
144
- // (basename 列表里永远不会有目录,直接对比会把目录行误报为「已删除的模块」)
145
- const existingDirs = existingFiles.filter(f => f.endsWith('/'));
146
- const existingFileNames = existingFiles.filter(f => !f.endsWith('/'));
162
+ const verdict = reconcileCapabilities({
163
+ content: capsContent,
164
+ populationFiles: currentModules.map((m) => m.file),
165
+ fileExists: (rel) => existsSync(path.join(projectPath, rel)),
166
+ sourceRoots: detectSourceRoots(projectPath),
167
+ });
147
168
  if (capsMode === 'module') {
148
- // module 模式:文件条目精确匹配或目录条目前缀覆盖;
149
- // added 不再是逐文件列表,聚合为「未覆盖目录」(与 capability_sync checker 同规则)
150
- const entries = readCapabilitiesEntries(capabilitiesPath, { includeDirs: true });
151
- const fileEntries = entries.filter(e => !e.endsWith('/'));
152
- const dirEntries = entries.filter(e => e.endsWith('/'));
153
- const uncoveredDirs = new Set<string>();
154
- for (const m of currentModules) {
155
- const covered =
156
- fileEntries.includes(m.file) || dirEntries.some(d => m.file.startsWith(d));
157
- if (!covered) {
158
- const root = srcDirs.find(d => m.file.startsWith(d + '/')) || srcDirs[0] || '';
159
- uncoveredDirs.add(aggregateToSourceSubdir(root, m.file));
160
- }
161
- }
162
- result.added = [...uncoveredDirs];
169
+ // module 模式:added 为聚合后的「未覆盖目录」,需人工登记目录条目
170
+ result.added = verdict.uncoveredDirs;
163
171
  } else {
164
- result.added = currentModules
165
- .filter(
166
- m =>
167
- !existingDirs.some(d => m.file.startsWith(d)) &&
168
- !existingFileNames.includes(getBasename(m.file))
169
- )
170
- .map(m => getBasename(m.file));
171
- }
172
- // removed:basename 对比 + 全路径存在性兜底(#33)。
173
- // 表格格式承诺 ts|tsx|js|jsx,但扫描只覆盖 .ts/.tsx——
174
- // 指向真实存在文件的登记行(.js/.jsx 等扫描盲区)不得误判为「已删除」。
175
- // 兜底按 basename 豁免;路径不存在的幽灵行进不了豁免,由下方清扫按完整路径补入 removed。
176
- const sourceRoots = detectSourceRoots(projectPath);
177
- const entryExists = (entry: string): boolean =>
178
- existsSync(path.join(projectPath, entry)) ||
179
- sourceRoots.some(root => existsSync(path.join(projectPath, root, entry)));
180
-
181
- const registeredEntries = readCapabilitiesEntries(capabilitiesPath);
182
- const livePathBasenames = new Set(
183
- registeredEntries
184
- .filter(e => !e.endsWith('/') && e.includes('/') && entryExists(e))
185
- .map(getBasename)
186
- );
187
- result.removed = existingFileNames.filter(
188
- f => !currentBasenames.includes(f) && !livePathBasenames.has(f)
189
- );
190
-
191
- // 幽灵条目清扫(2026-08-08 studio CI 4 连红事故):上方按 basename 对比,
192
- // 同名碰撞时幽灵不可见(如 agent-configs/routes.ts 已删但 agents/routes.ts 仍存在,
193
- // basename routes.ts 仍在扫描结果中,永远不会被判 removed)。
194
- // 这里按完整路径直接判存在性(与 docs_freshness 检查器同语义:项目根 + 源码根前缀)。
195
- for (const entry of registeredEntries) {
196
- // 纯文件名条目(无路径)无法用存在性判定,交由上方 basename 对比
197
- if (!entry.includes('/') || entryExists(entry)) continue;
198
- const basename = entry.split('/').pop()!;
199
- // basename 对比已覆盖(无碰撞场景)时不重复加入
200
- if (!result.removed.includes(basename) && !result.removed.includes(entry)) {
201
- result.removed.push(entry);
202
- }
172
+ result.added = verdict.uncoveredFiles.map((f) => getBasename(f));
203
173
  }
174
+ // removed:文件与目录条目一起查(幽灵目录行若不清除,docs_freshness 从严后
175
+ // check 会持续报失败而 fix 修不掉——check/fix 必须同规则才能收敛)
176
+ result.removed = verdict.deadEntries;
204
177
  }
205
178
 
206
179
  // 4. 检查 CONTEXT.md(缺失 + 过时)
@@ -342,55 +315,55 @@ export async function syncDocs(options: SyncDocsOptions): Promise<boolean> {
342
315
  }
343
316
  }
344
317
 
345
- console.log(JSON.stringify(jsonOutput, null, 2));
346
- return !hasIssues;
318
+ log(io, JSON.stringify(jsonOutput, null, 2));
319
+ return hasIssues ? drift('文档不是最新的(详见 --json 输出 issues 字段)') : { kind: 'ok' };
347
320
  }
348
321
 
349
322
  // 6. 人读输出模式
350
323
  if (capsIsCapabilityListing && hasCapIssues) {
351
- console.log(chalk.yellow(`\n📊 CAPABILITIES.md 计数不一致:`));
352
- capCountMismatches.forEach(m => console.log(chalk.gray(` - ${m}`)));
324
+ log(io, chalk.yellow(`\n📊 CAPABILITIES.md 计数不一致:`));
325
+ capCountMismatches.forEach(m => log(io, chalk.gray(` - ${m}`)));
353
326
  }
354
327
 
355
328
  if (result.added.length > 0) {
356
329
  if (capsMode === 'module') {
357
- console.log(chalk.yellow(`\n📄 CAPABILITIES.md 未登记以下模块(目录):`));
358
- result.added.forEach(d => console.log(chalk.gray(` + ${d}`)));
359
- console.log(
330
+ log(io, chalk.yellow(`\n📄 CAPABILITIES.md 未登记以下模块(目录):`));
331
+ result.added.forEach(d => log(io, chalk.gray(` + ${d}`)));
332
+ log(io,
360
333
  chalk.gray(' 请在 CAPABILITIES.md 中为这些目录登记一行目录条目(如 `| 模块名 | src/xxx/ | 说明 |`)')
361
334
  );
362
335
  } else {
363
- console.log(chalk.yellow(`\n📄 CAPABILITIES.md 缺少以下模块:`));
364
- result.added.forEach(f => console.log(chalk.gray(` + ${f}`)));
336
+ log(io, chalk.yellow(`\n📄 CAPABILITIES.md 缺少以下模块:`));
337
+ result.added.forEach(f => log(io, chalk.gray(` + ${f}`)));
365
338
  }
366
339
  }
367
340
 
368
341
  if (result.removed.length > 0) {
369
- console.log(chalk.yellow(`\n📄 CAPABILITIES.md 包含已删除的模块:`));
370
- result.removed.forEach(f => console.log(chalk.gray(` - ${f}`)));
342
+ log(io, chalk.yellow(`\n📄 CAPABILITIES.md 包含已删除的模块:`));
343
+ result.removed.forEach(f => log(io, chalk.gray(` - ${f}`)));
371
344
  }
372
345
 
373
346
  if (result.contextMissing.length > 0) {
374
- console.log(chalk.yellow(`\n📋 缺少 CONTEXT.md:`));
375
- result.contextMissing.forEach(d => console.log(chalk.gray(` - ${d}/CONTEXT.md`)));
347
+ log(io, chalk.yellow(`\n📋 缺少 CONTEXT.md:`));
348
+ result.contextMissing.forEach(d => log(io, chalk.gray(` - ${d}/CONTEXT.md`)));
376
349
  }
377
350
 
378
351
  if (result.contextStale.length > 0) {
379
- console.log(chalk.yellow(`\n📋 CONTEXT.md 可能过时(源码比文档新):`));
380
- result.contextStale.forEach(d => console.log(chalk.gray(` - ${d}/CONTEXT.md`)));
352
+ log(io, chalk.yellow(`\n📋 CONTEXT.md 可能过时(源码比文档新):`));
353
+ result.contextStale.forEach(d => log(io, chalk.gray(` - ${d}/CONTEXT.md`)));
381
354
  }
382
355
 
383
356
  if (hasAgentsIssues) {
384
- console.log(
357
+ log(io,
385
358
  chalk.yellow(agentsMdExists
386
359
  ? `\n🤖 AGENTS.md 与当前项目状态不一致:`
387
360
  : `\n🤖 缺少 AGENTS.md(agent 导读):`)
388
361
  );
389
- console.log(chalk.gray(` - AGENTS.md`));
362
+ log(io, chalk.gray(` - AGENTS.md`));
390
363
  }
391
364
 
392
365
  if (agentsMdMalformedPreserve.length > 0) {
393
- console.log(
366
+ log(io,
394
367
  chalk.yellow(
395
368
  `\n⚠️ AGENTS.md 中 PRESERVE 标记块未闭合(不予保留,重新生成将丢弃): ${agentsMdMalformedPreserve.join(', ')}`
396
369
  )
@@ -398,37 +371,37 @@ export async function syncDocs(options: SyncDocsOptions): Promise<boolean> {
398
371
  }
399
372
 
400
373
  if (!hasIssues) {
401
- console.log(chalk.green('✅ 所有文档都是最新的'));
402
- return true;
374
+ log(io, chalk.green('✅ 所有文档都是最新的'));
375
+ return { kind: 'ok' };
403
376
  }
404
377
 
405
378
  // 7. 检查模式:只报告,不修改
406
379
  if (isCheck) {
407
- console.log(chalk.red('\n❌ 文档不是最新的,请运行 harness sync-docs 更新'));
408
- return false;
380
+ log(io, chalk.red('\n❌ 文档不是最新的,请运行 harness sync-docs 更新'));
381
+ return drift('文档不是最新的,请运行 harness sync-docs 更新');
409
382
  }
410
383
 
411
384
  // 8. 写入模式:更新文档
412
385
  if (capsIsCapabilityListing && hasCapIssues) {
413
- capsContent = updateCapabilityCounts(capsContent, projectPath);
386
+ capsContent = updateCapabilityCounts(capsContent, CAPABILITY_DEFINITIONS);
414
387
  await fs.writeFile(capabilitiesPath, capsContent, 'utf-8');
415
- console.log(chalk.green(`\n✅ 已更新 CAPABILITIES.md 计数`));
388
+ log(io, chalk.green(`\n✅ 已更新 CAPABILITIES.md 计数`));
416
389
  }
417
390
 
418
391
  if (!capsIsCapabilityListing && hasTableIssues) {
419
392
  await updateCapabilitiesFile(capabilitiesPath, currentModules, existingFiles, result, capsMode);
420
- console.log(chalk.green(`\n✅ 已更新 CAPABILITIES.md`));
393
+ log(io, chalk.green(`\n✅ 已更新 CAPABILITIES.md`));
421
394
  }
422
395
 
423
396
  for (const dir of result.contextMissing) {
424
397
  await createContextMd(projectPath, dir);
425
- console.log(chalk.green(`✅ 已创建 ${dir}/CONTEXT.md`));
398
+ log(io, chalk.green(`✅ 已创建 ${dir}/CONTEXT.md`));
426
399
  }
427
400
 
428
401
  if (hasAgentsIssues && agentsMdExpected !== null) {
429
402
  await fs.writeFile(path.join(projectPath, 'AGENTS.md'), agentsMdExpected, 'utf-8');
430
- console.log(chalk.green(agentsMdExists ? `✅ 已更新 AGENTS.md` : `✅ 已生成 AGENTS.md`));
403
+ log(io, chalk.green(agentsMdExists ? `✅ 已更新 AGENTS.md` : `✅ 已生成 AGENTS.md`));
431
404
  }
432
405
 
433
- return !hasIssues;
406
+ return hasIssues ? drift('写入模式已修复漂移(历史面:退出码仍为 0)') : { kind: 'ok' };
434
407
  }
@@ -5,9 +5,9 @@
5
5
  import * as fs from 'fs/promises';
6
6
  import { existsSync } from 'fs';
7
7
  import * as path from 'path';
8
- import { loadRawProjectConfig } from '../../../core/project-config-loader';
8
+ import { loadRawProjectConfig, resolveContextFiles } from '../../../core/project-config-loader';
9
9
  import { detectSourceRoots } from '../../../utils/detect-source-roots';
10
- import { findTsSourceFiles, isTsSourceFile } from '../../../utils/file-walk';
10
+ import { DEFAULT_SKIP_DIRS, findTsSourceFiles, isTsSourceFile } from '../../../utils/file-walk';
11
11
 
12
12
  export interface ModuleInfo {
13
13
  name: string;
@@ -65,28 +65,27 @@ export function formatScriptCommand(pm: 'pnpm' | 'yarn' | 'npm', name: string):
65
65
 
66
66
  /**
67
67
  * 获取需要 CONTEXT.md 的目录列表
68
+ *
69
+ * context_files 三态语义(工单 84):enabled 且非空 → 配置目录;
70
+ * enabled 但无目标 → 工具流兜底回落自动探测(回落保留在调用方,不进访问器);
71
+ * 未配置 → 空列表。
68
72
  */
69
73
  export async function getRequiredContextDirs(projectPath: string): Promise<string[]> {
70
- try {
71
- const config = loadRawProjectConfig(projectPath) ?? {};
72
- const governance = config.governance as Record<string, unknown> | undefined;
73
- const contextFiles = governance?.context_files as Record<string, unknown> | undefined;
74
- if (contextFiles?.enabled) {
75
- if (Array.isArray(contextFiles.required_dirs) && contextFiles.required_dirs.length > 0) {
76
- return contextFiles.required_dirs as string[];
77
- }
78
- // Fallback: auto-discover from source roots
79
- return detectSourceRoots(projectPath);
80
- }
81
- } catch {
82
- // 配置不存在或无法解析
74
+ const resolution = resolveContextFiles(projectPath);
75
+ if (resolution.state === 'enabled') {
76
+ return resolution.dirs;
77
+ }
78
+ if (resolution.state === 'enabled-empty') {
79
+ // Fallback: auto-discover from source roots
80
+ return detectSourceRoots(projectPath);
83
81
  }
84
82
  return [];
85
83
  }
86
84
 
87
85
  /**
88
86
  * 获取源码扫描目录列表
89
- * 优先从 governance.context_files.required_dirs 读取,默认 ['src']
87
+ * 优先从 governance.context_files.required_dirs 读取;未配置或配置为空时
88
+ * 回落自动探测(detectSourceRoots),并非固定 ['src']
90
89
  */
91
90
  export async function getSourceDirs(projectPath: string): Promise<string[]> {
92
91
  const requiredDirs = await getRequiredContextDirs(projectPath);
@@ -108,8 +107,8 @@ export async function scanSourceModules(srcDir: string, projectPath: string): Pr
108
107
  }
109
108
 
110
109
  for (const entry of entries) {
111
- // 跳过测试目录和非源码目录
112
- if (entry === '__tests__' || entry === 'node_modules' || entry === 'dist') continue;
110
+ // 跳过依赖/旁测/构建产物目录(名单正本见 utils/file-walk)
111
+ if (DEFAULT_SKIP_DIRS.includes(entry)) continue;
113
112
 
114
113
  const entryPath = path.join(srcDir, entry);
115
114
  const stat = await fs.stat(entryPath);