@dommaker/harness 1.6.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (560) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/bin/harness.js +17 -1
  3. package/dist/agents/index.d.ts +6 -2
  4. package/dist/agents/index.d.ts.map +1 -1
  5. package/dist/agents/index.js +6 -16
  6. package/dist/agents/index.js.map +1 -1
  7. package/dist/cli/commands/__tests__/gate-decision.d.ts +18 -0
  8. package/dist/cli/commands/__tests__/gate-decision.d.ts.map +1 -0
  9. package/dist/cli/commands/__tests__/gate-decision.js +26 -0
  10. package/dist/cli/commands/__tests__/gate-decision.js.map +1 -0
  11. package/dist/cli/commands/acceptance.d.ts.map +1 -1
  12. package/dist/cli/commands/acceptance.js +17 -28
  13. package/dist/cli/commands/acceptance.js.map +1 -1
  14. package/dist/cli/commands/check.d.ts +11 -3
  15. package/dist/cli/commands/check.d.ts.map +1 -1
  16. package/dist/cli/commands/check.js +26 -11
  17. package/dist/cli/commands/check.js.map +1 -1
  18. package/dist/cli/commands/command.d.ts +0 -1
  19. package/dist/cli/commands/command.d.ts.map +1 -1
  20. package/dist/cli/commands/command.js +8 -6
  21. package/dist/cli/commands/command.js.map +1 -1
  22. package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
  23. package/dist/cli/commands/constraints-retire.js +36 -57
  24. package/dist/cli/commands/constraints-retire.js.map +1 -1
  25. package/dist/cli/commands/contract.d.ts.map +1 -1
  26. package/dist/cli/commands/contract.js +36 -42
  27. package/dist/cli/commands/contract.js.map +1 -1
  28. package/dist/cli/commands/definitions.d.ts +2 -0
  29. package/dist/cli/commands/definitions.d.ts.map +1 -1
  30. package/dist/cli/commands/definitions.js +11 -6
  31. package/dist/cli/commands/definitions.js.map +1 -1
  32. package/dist/cli/commands/failure.d.ts.map +1 -1
  33. package/dist/cli/commands/failure.js +16 -2
  34. package/dist/cli/commands/failure.js.map +1 -1
  35. package/dist/cli/commands/init.d.ts +14 -8
  36. package/dist/cli/commands/init.d.ts.map +1 -1
  37. package/dist/cli/commands/init.js +172 -364
  38. package/dist/cli/commands/init.js.map +1 -1
  39. package/dist/cli/commands/knowledge-view.d.ts +90 -0
  40. package/dist/cli/commands/knowledge-view.d.ts.map +1 -0
  41. package/dist/cli/commands/knowledge-view.js +198 -0
  42. package/dist/cli/commands/knowledge-view.js.map +1 -0
  43. package/dist/cli/commands/knowledge.d.ts +137 -52
  44. package/dist/cli/commands/knowledge.d.ts.map +1 -1
  45. package/dist/cli/commands/knowledge.js +534 -400
  46. package/dist/cli/commands/knowledge.js.map +1 -1
  47. package/dist/cli/commands/passes-gate.d.ts +4 -1
  48. package/dist/cli/commands/passes-gate.d.ts.map +1 -1
  49. package/dist/cli/commands/passes-gate.js +11 -2
  50. package/dist/cli/commands/passes-gate.js.map +1 -1
  51. package/dist/cli/commands/performance.d.ts.map +1 -1
  52. package/dist/cli/commands/performance.js +19 -31
  53. package/dist/cli/commands/performance.js.map +1 -1
  54. package/dist/cli/commands/report.d.ts.map +1 -1
  55. package/dist/cli/commands/report.js +2 -1
  56. package/dist/cli/commands/report.js.map +1 -1
  57. package/dist/cli/commands/review.d.ts.map +1 -1
  58. package/dist/cli/commands/review.js +13 -25
  59. package/dist/cli/commands/review.js.map +1 -1
  60. package/dist/cli/commands/scaffold-templates.d.ts +73 -0
  61. package/dist/cli/commands/scaffold-templates.d.ts.map +1 -0
  62. package/dist/cli/commands/scaffold-templates.js +424 -0
  63. package/dist/cli/commands/scaffold-templates.js.map +1 -0
  64. package/dist/cli/commands/scaffold.d.ts +105 -0
  65. package/dist/cli/commands/scaffold.d.ts.map +1 -0
  66. package/dist/cli/commands/scaffold.js +263 -0
  67. package/dist/cli/commands/scaffold.js.map +1 -0
  68. package/dist/cli/commands/security.d.ts.map +1 -1
  69. package/dist/cli/commands/security.js +22 -24
  70. package/dist/cli/commands/security.js.map +1 -1
  71. package/dist/cli/commands/spec-baseline-check.d.ts +43 -0
  72. package/dist/cli/commands/spec-baseline-check.d.ts.map +1 -1
  73. package/dist/cli/commands/spec-baseline-check.js +109 -34
  74. package/dist/cli/commands/spec-baseline-check.js.map +1 -1
  75. package/dist/cli/commands/sync-docs/context-syncer.d.ts +21 -2
  76. package/dist/cli/commands/sync-docs/context-syncer.d.ts.map +1 -1
  77. package/dist/cli/commands/sync-docs/context-syncer.js +112 -28
  78. package/dist/cli/commands/sync-docs/context-syncer.js.map +1 -1
  79. package/dist/cli/commands/sync-docs/index.d.ts +1 -1
  80. package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
  81. package/dist/cli/commands/sync-docs/index.js +86 -27
  82. package/dist/cli/commands/sync-docs/index.js.map +1 -1
  83. package/dist/cli/commands/sync-docs/project-reader.d.ts +24 -1
  84. package/dist/cli/commands/sync-docs/project-reader.d.ts.map +1 -1
  85. package/dist/cli/commands/sync-docs/project-reader.js +33 -7
  86. package/dist/cli/commands/sync-docs/project-reader.js.map +1 -1
  87. package/dist/cli/commands/validate.d.ts +0 -16
  88. package/dist/cli/commands/validate.d.ts.map +1 -1
  89. package/dist/cli/commands/validate.js +2 -97
  90. package/dist/cli/commands/validate.js.map +1 -1
  91. package/dist/cli/gate-command.d.ts +37 -0
  92. package/dist/cli/gate-command.d.ts.map +1 -0
  93. package/dist/cli/gate-command.js +64 -0
  94. package/dist/cli/gate-command.js.map +1 -0
  95. package/dist/completion-checkers/index.d.ts +1 -1
  96. package/dist/completion-checkers/index.d.ts.map +1 -1
  97. package/dist/completion-checkers/index.js +0 -15
  98. package/dist/completion-checkers/index.js.map +1 -1
  99. package/dist/context/knowledge-injector.d.ts +4 -2
  100. package/dist/context/knowledge-injector.d.ts.map +1 -1
  101. package/dist/context/knowledge-injector.js +13 -6
  102. package/dist/context/knowledge-injector.js.map +1 -1
  103. package/dist/core/constraints/agent-prompt-renderer.js +2 -2
  104. package/dist/core/constraints/agent-prompt-renderer.js.map +1 -1
  105. package/dist/core/constraints/capabilities-reconcile.d.ts +20 -0
  106. package/dist/core/constraints/capabilities-reconcile.d.ts.map +1 -1
  107. package/dist/core/constraints/capabilities-reconcile.js +26 -4
  108. package/dist/core/constraints/capabilities-reconcile.js.map +1 -1
  109. package/dist/core/constraints/check-cache.d.ts +1 -1
  110. package/dist/core/constraints/check-cache.js +1 -1
  111. package/dist/core/constraints/checker.d.ts +8 -3
  112. package/dist/core/constraints/checker.d.ts.map +1 -1
  113. package/dist/core/constraints/checker.js +30 -19
  114. package/dist/core/constraints/checker.js.map +1 -1
  115. package/dist/core/constraints/checkers/capability-sync.d.ts +3 -1
  116. package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
  117. package/dist/core/constraints/checkers/capability-sync.js +17 -24
  118. package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
  119. package/dist/core/constraints/checkers/context-doc-sync.d.ts +1 -1
  120. package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
  121. package/dist/core/constraints/checkers/context-doc-sync.js +9 -6
  122. package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
  123. package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
  124. package/dist/core/constraints/checkers/docs-freshness.js +24 -30
  125. package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
  126. package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
  127. package/dist/core/constraints/checkers/governance-presence.js +10 -4
  128. package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
  129. package/dist/core/constraints/checkers/types.d.ts +10 -6
  130. package/dist/core/constraints/checkers/types.d.ts.map +1 -1
  131. package/dist/core/constraints/checkers/types.js +7 -3
  132. package/dist/core/constraints/checkers/types.js.map +1 -1
  133. package/dist/core/constraints/context-builder.d.ts +11 -10
  134. package/dist/core/constraints/context-builder.d.ts.map +1 -1
  135. package/dist/core/constraints/context-builder.js +31 -27
  136. package/dist/core/constraints/context-builder.js.map +1 -1
  137. package/dist/core/constraints/context-reconcile.d.ts +75 -0
  138. package/dist/core/constraints/context-reconcile.d.ts.map +1 -0
  139. package/dist/core/constraints/context-reconcile.js +161 -0
  140. package/dist/core/constraints/context-reconcile.js.map +1 -0
  141. package/dist/core/constraints/doc-freshness/runner.d.ts +7 -0
  142. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  143. package/dist/core/constraints/doc-freshness/runner.js +8 -3
  144. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  145. package/dist/core/constraints/git-evidence.js +1 -1
  146. package/dist/core/constraints/git-evidence.js.map +1 -1
  147. package/dist/core/constraints/injection-drift.d.ts +7 -2
  148. package/dist/core/constraints/injection-drift.d.ts.map +1 -1
  149. package/dist/core/constraints/injection-drift.js +13 -7
  150. package/dist/core/constraints/injection-drift.js.map +1 -1
  151. package/dist/core/constraints/run-env.d.ts +92 -0
  152. package/dist/core/constraints/run-env.d.ts.map +1 -0
  153. package/dist/core/constraints/run-env.js +175 -0
  154. package/dist/core/constraints/run-env.js.map +1 -0
  155. package/dist/core/constraints/triggers.d.ts +22 -0
  156. package/dist/core/constraints/triggers.d.ts.map +1 -0
  157. package/dist/core/constraints/triggers.js +31 -0
  158. package/dist/core/constraints/triggers.js.map +1 -0
  159. package/dist/core/effective-constraints.d.ts +13 -4
  160. package/dist/core/effective-constraints.d.ts.map +1 -1
  161. package/dist/core/effective-constraints.js +16 -6
  162. package/dist/core/effective-constraints.js.map +1 -1
  163. package/dist/core/effective-set.d.ts +8 -15
  164. package/dist/core/effective-set.d.ts.map +1 -1
  165. package/dist/core/effective-set.js +7 -12
  166. package/dist/core/effective-set.js.map +1 -1
  167. package/dist/core/index.d.ts +2 -1
  168. package/dist/core/index.d.ts.map +1 -1
  169. package/dist/core/index.js.map +1 -1
  170. package/dist/core/project-config-loader.d.ts +39 -17
  171. package/dist/core/project-config-loader.d.ts.map +1 -1
  172. package/dist/core/project-config-loader.js +51 -122
  173. package/dist/core/project-config-loader.js.map +1 -1
  174. package/dist/core/validators/check-handlers/http.d.ts +2 -2
  175. package/dist/core/validators/check-handlers/http.d.ts.map +1 -1
  176. package/dist/core/validators/check-handlers/http.js +12 -7
  177. package/dist/core/validators/check-handlers/http.js.map +1 -1
  178. package/dist/core/validators/index.d.ts +1 -1
  179. package/dist/core/validators/index.d.ts.map +1 -1
  180. package/dist/core/validators/passes-gate.d.ts.map +1 -1
  181. package/dist/core/validators/passes-gate.js +2 -2
  182. package/dist/core/validators/passes-gate.js.map +1 -1
  183. package/dist/failure/classifier.d.ts +1 -1
  184. package/dist/failure/classifier.d.ts.map +1 -1
  185. package/dist/failure/classifier.js +8 -8
  186. package/dist/failure/classifier.js.map +1 -1
  187. package/dist/failure/index.d.ts +2 -1
  188. package/dist/failure/index.d.ts.map +1 -1
  189. package/dist/failure/index.js +9 -17
  190. package/dist/failure/index.js.map +1 -1
  191. package/dist/failure/recorder.d.ts +1 -1
  192. package/dist/failure/recorder.d.ts.map +1 -1
  193. package/dist/gates/checker-gate.d.ts +2 -1
  194. package/dist/gates/checker-gate.d.ts.map +1 -1
  195. package/dist/gates/checker-gate.js +3 -2
  196. package/dist/gates/checker-gate.js.map +1 -1
  197. package/dist/gates/command.d.ts +10 -4
  198. package/dist/gates/command.d.ts.map +1 -1
  199. package/dist/gates/command.js +36 -62
  200. package/dist/gates/command.js.map +1 -1
  201. package/dist/gates/contract.d.ts.map +1 -1
  202. package/dist/gates/contract.js +0 -4
  203. package/dist/gates/contract.js.map +1 -1
  204. package/dist/gates/definitions.d.ts +2 -1
  205. package/dist/gates/definitions.d.ts.map +1 -1
  206. package/dist/gates/definitions.js +6 -5
  207. package/dist/gates/definitions.js.map +1 -1
  208. package/dist/gates/index.d.ts +0 -4
  209. package/dist/gates/index.d.ts.map +1 -1
  210. package/dist/gates/index.js +1 -6
  211. package/dist/gates/index.js.map +1 -1
  212. package/dist/gates/performance.d.ts.map +1 -1
  213. package/dist/gates/performance.js +0 -4
  214. package/dist/gates/performance.js.map +1 -1
  215. package/dist/gates/runner.d.ts +1 -1
  216. package/dist/gates/runner.js +1 -1
  217. package/dist/gates/security.d.ts +1 -1
  218. package/dist/gates/security.d.ts.map +1 -1
  219. package/dist/gates/security.js +5 -6
  220. package/dist/gates/security.js.map +1 -1
  221. package/dist/gates/types.d.ts +14 -17
  222. package/dist/gates/types.d.ts.map +1 -1
  223. package/dist/gates/types.js.map +1 -1
  224. package/dist/hooks/bootstrap.d.ts +6 -4
  225. package/dist/hooks/bootstrap.d.ts.map +1 -1
  226. package/dist/hooks/bootstrap.js +25 -13
  227. package/dist/hooks/bootstrap.js.map +1 -1
  228. package/dist/hooks/config.d.ts +6 -2
  229. package/dist/hooks/config.d.ts.map +1 -1
  230. package/dist/hooks/config.js +5 -1
  231. package/dist/hooks/config.js.map +1 -1
  232. package/dist/hooks/index.d.ts +1 -1
  233. package/dist/hooks/index.d.ts.map +1 -1
  234. package/dist/hooks/index.js +0 -15
  235. package/dist/hooks/index.js.map +1 -1
  236. package/dist/hooks/pipeline.d.ts.map +1 -1
  237. package/dist/hooks/pipeline.js +2 -4
  238. package/dist/hooks/pipeline.js.map +1 -1
  239. package/dist/hooks/registry.d.ts +11 -8
  240. package/dist/hooks/registry.d.ts.map +1 -1
  241. package/dist/hooks/registry.js +22 -7
  242. package/dist/hooks/registry.js.map +1 -1
  243. package/dist/hooks/types.d.ts +23 -7
  244. package/dist/hooks/types.d.ts.map +1 -1
  245. package/dist/index.d.ts +4 -4
  246. package/dist/index.d.ts.map +1 -1
  247. package/dist/index.js +3 -4
  248. package/dist/index.js.map +1 -1
  249. package/dist/knowledge/audit-scoring.d.ts +87 -0
  250. package/dist/knowledge/audit-scoring.d.ts.map +1 -0
  251. package/dist/knowledge/audit-scoring.js +470 -0
  252. package/dist/knowledge/audit-scoring.js.map +1 -0
  253. package/dist/knowledge/audit.d.ts +11 -59
  254. package/dist/knowledge/audit.d.ts.map +1 -1
  255. package/dist/knowledge/audit.js +47 -522
  256. package/dist/knowledge/audit.js.map +1 -1
  257. package/dist/knowledge/import.d.ts.map +1 -1
  258. package/dist/knowledge/import.js +2 -0
  259. package/dist/knowledge/import.js.map +1 -1
  260. package/dist/knowledge/index-generator.d.ts.map +1 -1
  261. package/dist/knowledge/index-generator.js +4 -5
  262. package/dist/knowledge/index-generator.js.map +1 -1
  263. package/dist/knowledge/index.d.ts +3 -2
  264. package/dist/knowledge/index.d.ts.map +1 -1
  265. package/dist/knowledge/index.js +3 -16
  266. package/dist/knowledge/index.js.map +1 -1
  267. package/dist/knowledge/ingest.js +1 -1
  268. package/dist/knowledge/ingest.js.map +1 -1
  269. package/dist/knowledge/lifecycle.d.ts.map +1 -1
  270. package/dist/knowledge/lifecycle.js +4 -2
  271. package/dist/knowledge/lifecycle.js.map +1 -1
  272. package/dist/knowledge/migration.d.ts +3 -0
  273. package/dist/knowledge/migration.d.ts.map +1 -1
  274. package/dist/knowledge/migration.js +8 -2
  275. package/dist/knowledge/migration.js.map +1 -1
  276. package/dist/knowledge/query.d.ts +6 -0
  277. package/dist/knowledge/query.d.ts.map +1 -1
  278. package/dist/knowledge/query.js +8 -2
  279. package/dist/knowledge/query.js.map +1 -1
  280. package/dist/knowledge/reference-tracker.d.ts.map +1 -1
  281. package/dist/knowledge/reference-tracker.js +4 -6
  282. package/dist/knowledge/reference-tracker.js.map +1 -1
  283. package/dist/knowledge/store.d.ts +22 -16
  284. package/dist/knowledge/store.d.ts.map +1 -1
  285. package/dist/knowledge/store.js +53 -12
  286. package/dist/knowledge/store.js.map +1 -1
  287. package/dist/knowledge/tree-walker.d.ts +19 -0
  288. package/dist/knowledge/tree-walker.d.ts.map +1 -0
  289. package/dist/knowledge/tree-walker.js +30 -0
  290. package/dist/knowledge/tree-walker.js.map +1 -0
  291. package/dist/knowledge/types.d.ts +16 -0
  292. package/dist/knowledge/types.d.ts.map +1 -1
  293. package/dist/monitoring/traces.d.ts +5 -3
  294. package/dist/monitoring/traces.d.ts.map +1 -1
  295. package/dist/monitoring/traces.js +19 -5
  296. package/dist/monitoring/traces.js.map +1 -1
  297. package/dist/test-setup/jsonl-fake-fs.d.ts +30 -0
  298. package/dist/test-setup/jsonl-fake-fs.d.ts.map +1 -0
  299. package/dist/test-setup/jsonl-fake-fs.js +49 -0
  300. package/dist/test-setup/jsonl-fake-fs.js.map +1 -0
  301. package/dist/tools/index.d.ts +4 -1
  302. package/dist/tools/index.d.ts.map +1 -1
  303. package/dist/tools/index.js +7 -15
  304. package/dist/tools/index.js.map +1 -1
  305. package/dist/types/checkpoint.d.ts +5 -0
  306. package/dist/types/checkpoint.d.ts.map +1 -1
  307. package/dist/types/passes-gate.d.ts +0 -13
  308. package/dist/types/passes-gate.d.ts.map +1 -1
  309. package/dist/types/project-config.d.ts +18 -0
  310. package/dist/types/project-config.d.ts.map +1 -1
  311. package/dist/types/trace.d.ts +13 -4
  312. package/dist/types/trace.d.ts.map +1 -1
  313. package/dist/types/trace.js +4 -3
  314. package/dist/types/trace.js.map +1 -1
  315. package/dist/utils/exec.d.ts +4 -27
  316. package/dist/utils/exec.d.ts.map +1 -1
  317. package/dist/utils/exec.js +4 -94
  318. package/dist/utils/exec.js.map +1 -1
  319. package/dist/utils/jsonl.d.ts +17 -7
  320. package/dist/utils/jsonl.d.ts.map +1 -1
  321. package/dist/utils/jsonl.js +144 -17
  322. package/dist/utils/jsonl.js.map +1 -1
  323. package/dist/utils/numeric-flag.d.ts +44 -0
  324. package/dist/utils/numeric-flag.d.ts.map +1 -0
  325. package/dist/utils/numeric-flag.js +62 -0
  326. package/dist/utils/numeric-flag.js.map +1 -0
  327. package/package.json +1 -1
  328. package/src/CONTEXT.md +5 -2
  329. package/src/__tests__/acceptance-gate.test.ts +7 -4
  330. package/src/__tests__/checker-extra.test.ts +3 -2
  331. package/src/__tests__/checker.test.ts +4 -3
  332. package/src/__tests__/checkpoint.test.ts +26 -9
  333. package/src/__tests__/context-files-resolution.test.ts +29 -15
  334. package/src/__tests__/contract-gate.test.ts +19 -40
  335. package/src/__tests__/error-classifier.test.ts +1 -1
  336. package/src/__tests__/exec.test.ts +45 -107
  337. package/src/__tests__/failure-recorder.test.ts +7 -5
  338. package/src/__tests__/hooks-pipeline.test.ts +34 -47
  339. package/src/__tests__/layering.test.ts +217 -25
  340. package/src/__tests__/passes-gate.test.ts +9 -4
  341. package/src/__tests__/performance-gate.test.ts +3 -15
  342. package/src/__tests__/project-config-loader-extra.test.ts +31 -57
  343. package/src/__tests__/project-config-loader.test.ts +6 -3
  344. package/src/__tests__/public-exports.test.ts +27 -6
  345. package/src/__tests__/public-type-surface.test.ts +360 -0
  346. package/src/__tests__/public-value-surface.test.ts +182 -0
  347. package/src/__tests__/review-gate.test.ts +4 -6
  348. package/src/__tests__/security-gate.test.ts +5 -20
  349. package/src/__tests__/skip-semantics.test.ts +5 -3
  350. package/src/__tests__/spec-validator.test.ts +3 -2
  351. package/src/__tests__/sub-barrels-explicit.test.ts +87 -0
  352. package/src/__tests__/test-fixture-hygiene.test.ts +53 -0
  353. package/src/__tests__/trace-analyzer-extra.test.ts +5 -3
  354. package/src/__tests__/trace-analyzer.test.ts +5 -3
  355. package/src/__tests__/trace-collector.test.ts +5 -3
  356. package/src/agents/index.ts +13 -2
  357. package/src/cli/__tests__/gate-command.test.ts +162 -0
  358. package/src/cli/commands/CONTEXT.md +29 -5
  359. package/src/cli/commands/__tests__/acceptance.test.ts +24 -25
  360. package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +15 -0
  361. package/src/cli/commands/__tests__/check-drift.test.ts +42 -1
  362. package/src/cli/commands/__tests__/check-read-count.test.ts +167 -0
  363. package/src/cli/commands/__tests__/check-skip-output.test.ts +4 -1
  364. package/src/cli/commands/__tests__/check.test.ts +43 -36
  365. package/src/cli/commands/__tests__/command-gate-dead-options.test.ts +60 -0
  366. package/src/cli/commands/__tests__/command.test.ts +33 -17
  367. package/src/cli/commands/__tests__/constraints-retire.test.ts +52 -0
  368. package/src/cli/commands/__tests__/context-syncer.test.ts +141 -0
  369. package/src/cli/commands/__tests__/contract.test.ts +20 -20
  370. package/src/cli/commands/__tests__/failure.test.ts +5 -4
  371. package/src/cli/commands/__tests__/gate-decision.ts +30 -0
  372. package/src/cli/commands/__tests__/init-dead-options.test.ts +50 -0
  373. package/src/cli/commands/__tests__/init-governance-io.test.ts +139 -0
  374. package/src/cli/commands/__tests__/init-injection.test.ts +3 -3
  375. package/src/cli/commands/__tests__/init-ondisk.test.ts +867 -0
  376. package/src/cli/commands/__tests__/init.test.ts +82 -6
  377. package/src/cli/commands/__tests__/knowledge-audit-threshold.test.ts +146 -0
  378. package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +1 -1
  379. package/src/cli/commands/__tests__/knowledge-tone-mapping.test.ts +244 -0
  380. package/src/cli/commands/__tests__/knowledge-view.test.ts +839 -0
  381. package/src/cli/commands/__tests__/knowledge.test.ts +33 -15
  382. package/src/cli/commands/__tests__/numeric-flag-assembly.test.ts +368 -0
  383. package/src/cli/commands/__tests__/passes-gate-dead-options.test.ts +89 -1
  384. package/src/cli/commands/__tests__/performance.test.ts +19 -21
  385. package/src/cli/commands/__tests__/posteval-plan.test.ts +5 -3
  386. package/src/cli/commands/__tests__/pre-push-hook.test.ts +181 -0
  387. package/src/cli/commands/__tests__/project-path-anchoring.test.ts +95 -2
  388. package/src/cli/commands/__tests__/project-path-convention.test.ts +103 -25
  389. package/src/cli/commands/__tests__/registry.test.ts +2 -0
  390. package/src/cli/commands/__tests__/report.test.ts +23 -1
  391. package/src/cli/commands/__tests__/review.test.ts +10 -17
  392. package/src/cli/commands/__tests__/scaffold.test.ts +505 -0
  393. package/src/cli/commands/__tests__/security-default-scan.test.ts +75 -0
  394. package/src/cli/commands/__tests__/security.test.ts +21 -24
  395. package/src/cli/commands/__tests__/spec-baseline-check.test.ts +3 -2
  396. package/src/cli/commands/__tests__/spec-baseline-index-cap.test.ts +131 -0
  397. package/src/cli/commands/__tests__/spec-baseline-read-count.test.ts +325 -0
  398. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +19 -4
  399. package/src/cli/commands/__tests__/sync-docs-context-drift.test.ts +232 -0
  400. package/src/cli/commands/__tests__/sync-docs-module-order.test.ts +111 -0
  401. package/src/cli/commands/__tests__/sync-docs-read-count.test.ts +192 -0
  402. package/src/cli/commands/__tests__/sync-docs.test.ts +3 -2
  403. package/src/cli/commands/__tests__/validate.test.ts +1 -48
  404. package/src/cli/commands/acceptance.ts +24 -29
  405. package/src/cli/commands/check.ts +40 -17
  406. package/src/cli/commands/command.ts +9 -8
  407. package/src/cli/commands/constraints-retire.ts +57 -70
  408. package/src/cli/commands/contract.ts +44 -47
  409. package/src/cli/commands/definitions.ts +12 -6
  410. package/src/cli/commands/failure.ts +16 -3
  411. package/src/cli/commands/init.ts +242 -389
  412. package/src/cli/commands/knowledge-view.ts +224 -0
  413. package/src/cli/commands/knowledge.ts +643 -448
  414. package/src/cli/commands/passes-gate.ts +12 -3
  415. package/src/cli/commands/performance.ts +31 -38
  416. package/src/cli/commands/report.ts +3 -2
  417. package/src/cli/commands/review.ts +22 -28
  418. package/src/cli/commands/scaffold-templates.ts +406 -0
  419. package/src/cli/commands/scaffold.ts +302 -0
  420. package/src/cli/commands/security.ts +27 -23
  421. package/src/cli/commands/spec-baseline-check.ts +158 -33
  422. package/src/cli/commands/sync-docs/__tests__/context-export-surface.test.ts +102 -0
  423. package/src/cli/commands/sync-docs/__tests__/module-descriptions.test.ts +196 -0
  424. package/src/cli/commands/sync-docs/context-syncer.ts +137 -31
  425. package/src/cli/commands/sync-docs/index.ts +103 -28
  426. package/src/cli/commands/sync-docs/project-reader.ts +51 -7
  427. package/src/cli/commands/validate.ts +1 -104
  428. package/src/cli/gate-command.ts +87 -0
  429. package/src/completion-checkers/index.ts +10 -1
  430. package/src/context/CONTEXT.md +2 -1
  431. package/src/context/__tests__/knowledge-injector.test.ts +27 -0
  432. package/src/context/__tests__/session-manager.test.ts +19 -12
  433. package/src/context/knowledge-injector.ts +14 -7
  434. package/src/core/CONTEXT.md +10 -7
  435. package/src/core/__tests__/effective-set.test.ts +36 -60
  436. package/src/core/constraints/__tests__/context-reconcile.test.ts +246 -0
  437. package/src/core/constraints/__tests__/run-env.test.ts +377 -0
  438. package/src/core/constraints/__tests__/triggers.test.ts +70 -0
  439. package/src/core/constraints/agent-prompt-renderer.ts +1 -1
  440. package/src/core/constraints/capabilities-reconcile.ts +28 -4
  441. package/src/core/constraints/check-cache.ts +1 -1
  442. package/src/core/constraints/checker.ts +30 -14
  443. package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +8 -3
  444. package/src/core/constraints/checkers/__tests__/governance-presence.test.ts +16 -7
  445. package/src/core/constraints/checkers/__tests__/no-hardcoded-credentials.test.ts +2 -1
  446. package/src/core/constraints/checkers/capability-sync.ts +27 -28
  447. package/src/core/constraints/checkers/context-doc-sync.ts +9 -7
  448. package/src/core/constraints/checkers/docs-freshness.ts +28 -33
  449. package/src/core/constraints/checkers/governance-presence.ts +10 -6
  450. package/src/core/constraints/checkers/types.ts +14 -8
  451. package/src/core/constraints/context-builder.ts +35 -26
  452. package/src/core/constraints/context-reconcile.ts +200 -0
  453. package/src/core/constraints/doc-freshness/__tests__/runner.test.ts +25 -0
  454. package/src/core/constraints/doc-freshness/runner.ts +18 -4
  455. package/src/core/constraints/git-evidence.ts +1 -1
  456. package/src/core/constraints/injection-drift.ts +19 -8
  457. package/src/core/constraints/run-env.ts +207 -0
  458. package/src/core/constraints/triggers.ts +33 -0
  459. package/src/core/effective-constraints.ts +17 -6
  460. package/src/core/effective-set.ts +8 -24
  461. package/src/core/index.ts +2 -1
  462. package/src/core/project-config-loader.ts +54 -102
  463. package/src/core/validators/__tests__/http-retry-sleep.test.ts +231 -0
  464. package/src/core/validators/check-handlers/http.ts +18 -7
  465. package/src/core/validators/index.ts +0 -1
  466. package/src/core/validators/passes-gate.ts +2 -3
  467. package/src/failure/CONTEXT.md +2 -1
  468. package/src/failure/classifier.ts +2 -2
  469. package/src/failure/index.ts +14 -2
  470. package/src/failure/recorder.ts +1 -1
  471. package/src/gates/CONTEXT.md +13 -7
  472. package/src/gates/__tests__/checker-gate.test.ts +1 -1
  473. package/src/gates/__tests__/command-single-predicate.test.ts +135 -0
  474. package/src/gates/__tests__/command.test.ts +8 -15
  475. package/src/gates/__tests__/contract.test.ts +5 -5
  476. package/src/gates/__tests__/gate-interface.test.ts +0 -17
  477. package/src/gates/__tests__/performance.test.ts +0 -13
  478. package/src/gates/__tests__/review.test.ts +0 -1
  479. package/src/gates/__tests__/runner.test.ts +1 -1
  480. package/src/gates/__tests__/security-default-scan.test.ts +97 -0
  481. package/src/gates/__tests__/security.test.ts +0 -13
  482. package/src/gates/checker-gate.ts +3 -2
  483. package/src/gates/command.ts +51 -74
  484. package/src/gates/contract.ts +0 -5
  485. package/src/gates/definitions.ts +6 -5
  486. package/src/gates/index.ts +0 -5
  487. package/src/gates/performance.ts +1 -6
  488. package/src/gates/runner.ts +1 -1
  489. package/src/gates/security.ts +9 -11
  490. package/src/gates/types.ts +20 -23
  491. package/src/hooks/CONTEXT.md +10 -9
  492. package/src/hooks/__tests__/bootstrap.test.ts +29 -6
  493. package/src/hooks/__tests__/pipeline.test.ts +28 -17
  494. package/src/hooks/__tests__/registry.test.ts +51 -3
  495. package/src/hooks/bootstrap.ts +35 -15
  496. package/src/hooks/config.ts +6 -2
  497. package/src/hooks/index.ts +10 -1
  498. package/src/hooks/pipeline.ts +4 -6
  499. package/src/hooks/registry.ts +31 -12
  500. package/src/hooks/types.ts +24 -7
  501. package/src/index.ts +1 -3
  502. package/src/knowledge/CONTEXT.md +14 -7
  503. package/src/knowledge/__tests__/audit-labels.test.ts +8 -5
  504. package/src/knowledge/__tests__/audit-scoring.test.ts +230 -0
  505. package/src/knowledge/__tests__/audit-write-count.test.ts +131 -0
  506. package/src/knowledge/__tests__/audit.test.ts +291 -208
  507. package/src/knowledge/__tests__/doctor.test.ts +3 -2
  508. package/src/knowledge/__tests__/ingest.test.ts +3 -2
  509. package/src/knowledge/__tests__/lifecycle.test.ts +57 -2
  510. package/src/knowledge/__tests__/migration.test.ts +35 -6
  511. package/src/knowledge/__tests__/query.test.ts +3 -2
  512. package/src/knowledge/__tests__/reference-tracker.test.ts +23 -2
  513. package/src/knowledge/__tests__/store.test.ts +149 -4
  514. package/src/knowledge/__tests__/tree-walker.test.ts +132 -0
  515. package/src/knowledge/audit-scoring.ts +589 -0
  516. package/src/knowledge/audit.ts +55 -599
  517. package/src/knowledge/import.ts +2 -0
  518. package/src/knowledge/index-generator.ts +4 -6
  519. package/src/knowledge/index.ts +27 -2
  520. package/src/knowledge/ingest.ts +1 -1
  521. package/src/knowledge/lifecycle.ts +5 -2
  522. package/src/knowledge/migration.ts +8 -2
  523. package/src/knowledge/query.ts +8 -1
  524. package/src/knowledge/reference-tracker.ts +5 -7
  525. package/src/knowledge/store.ts +75 -16
  526. package/src/knowledge/tree-walker.ts +29 -0
  527. package/src/knowledge/types.ts +19 -0
  528. package/src/monitoring/CONTEXT.md +5 -2
  529. package/src/monitoring/__tests__/context-tracker.test.ts +16 -8
  530. package/src/monitoring/__tests__/trace-file-anchoring.test.ts +80 -0
  531. package/src/monitoring/traces.ts +19 -5
  532. package/src/release/CONTEXT.md +1 -1
  533. package/src/release/__tests__/integrity.test.ts +21 -12
  534. package/src/test-setup/__tests__/jsonl-fake-fs.test.ts +98 -0
  535. package/src/test-setup/jsonl-fake-fs.ts +59 -0
  536. package/src/tools/index.ts +5 -1
  537. package/src/types/checkpoint.ts +6 -0
  538. package/src/types/passes-gate.ts +0 -17
  539. package/src/types/project-config.ts +21 -0
  540. package/src/types/trace.ts +14 -4
  541. package/src/utils/__tests__/file-walk.test.ts +3 -2
  542. package/src/utils/__tests__/jsonl-bounded-read.test.ts +106 -0
  543. package/src/utils/__tests__/jsonl-skip-disposition.test.ts +10 -6
  544. package/src/utils/__tests__/jsonl.test.ts +61 -14
  545. package/src/utils/__tests__/numeric-flag.test.ts +80 -0
  546. package/src/utils/__tests__/package-version.test.ts +2 -2
  547. package/src/utils/exec.ts +4 -64
  548. package/src/utils/jsonl.ts +159 -20
  549. package/src/utils/numeric-flag.ts +64 -0
  550. package/dist/failure/types.d.ts +0 -9
  551. package/dist/failure/types.d.ts.map +0 -1
  552. package/dist/failure/types.js +0 -25
  553. package/dist/failure/types.js.map +0 -1
  554. package/dist/gates/effective-gates.d.ts +0 -30
  555. package/dist/gates/effective-gates.d.ts.map +0 -1
  556. package/dist/gates/effective-gates.js +0 -81
  557. package/dist/gates/effective-gates.js.map +0 -1
  558. package/src/failure/types.ts +0 -9
  559. package/src/gates/__tests__/effective-gates.test.ts +0 -77
  560. package/src/gates/effective-gates.ts +0 -99
@@ -7,7 +7,7 @@
7
7
  * 实现拆分:
8
8
  * - project-reader.ts 项目信息读取(package.json/config.yml/源码扫描)
9
9
  * - capabilities-syncer.ts CAPABILITIES.md 文件表格格式的对比与维护(能力清单格式解析/计数走 core/constraints/capabilities-parser)
10
- * - context-syncer.ts CONTEXT.md 模板生成/发现/过时判定
10
+ * - context-syncer.ts CONTEXT.md 模板生成/发现/过时判定 + 目录导出面采集(harness#142)
11
11
  * - agents-syncer.ts AGENTS.md 生成
12
12
  * - preserve-block.ts PRESERVE 标记块提取与组合
13
13
  */
@@ -25,6 +25,7 @@ import {
25
25
  import { COMMAND_DEFINITIONS } from '../definitions';
26
26
  import { GATE_DEFINITIONS } from '../../../gates/definitions';
27
27
  import { reconcileCapabilities } from '../../../core/constraints/capabilities-reconcile';
28
+ import { reconcileContext } from '../../../core/constraints/context-reconcile';
28
29
  import { detectSourceRoots } from '../../../utils/detect-source-roots';
29
30
  import { getCapabilitiesMode } from '../../../core/project-config-loader';
30
31
  import { getSourceDirs, scanSourceModules, getRequiredContextDirs } from './project-reader';
@@ -34,7 +35,12 @@ import {
34
35
  updateCapabilitiesFile,
35
36
  compactCapabilitiesContent,
36
37
  } from './capabilities-syncer';
37
- import { createContextMd, findExistingContextFiles, getLatestTsMtime } from './context-syncer';
38
+ import {
39
+ createContextMd,
40
+ findExistingContextFiles,
41
+ getLatestTsMtime,
42
+ collectContextExportSurface,
43
+ } from './context-syncer';
38
44
  import { buildAgentsMd } from './agents-syncer';
39
45
  import { extractPreserveBlocks, composeAgentsMd } from './preserve-block';
40
46
  import { log, processIO, type CommandIO, type CommandResult } from '../../command-contract';
@@ -111,23 +117,12 @@ export async function syncDocs(
111
117
  removed: [],
112
118
  contextMissing: [],
113
119
  contextStale: [],
120
+ contextContentDrift: [],
114
121
  };
115
122
 
116
- // 1. 扫描源码模块(从 governance config 读取目录列表,默认 src/)
123
+ // 1. 先判定本次运行需要什么:源码目录列表 + 现有 CAPABILITIES.md 的格式
124
+ // (格式判定必须在扫描之前——它决定下面那份清单要不要去取)
117
125
  const srcDirs = await getSourceDirs(projectPath);
118
- const currentModules: ModuleInfo[] = [];
119
- for (const srcDir of srcDirs) {
120
- try {
121
- const modules = await scanSourceModules(path.join(projectPath, srcDir), projectPath);
122
- currentModules.push(...modules);
123
- } catch {
124
- if (!isJson) {
125
- log(io, chalk.yellow(`⚠️ 未找到 ${srcDir} 目录,跳过`));
126
- }
127
- }
128
- }
129
-
130
- // 2. 解析现有 CAPABILITIES.md,检测格式
131
126
  const capabilitiesPath = path.join(projectPath, 'CAPABILITIES.md');
132
127
  let existingFiles: string[] = [];
133
128
  let capsContent = '';
@@ -144,6 +139,23 @@ export async function syncDocs(
144
139
  // CAPABILITIES.md 不存在,将创建
145
140
  }
146
141
 
142
+ // 2. 扫描源码模块(从 governance config 读取目录列表,默认 src/)
143
+ // capability-listing 格式不做条目级比对(计数才是判定面),模块清单在本次运行里无人
144
+ // 消费 → 整树一份 .ts 内容都不读(harness#147;改造前是无条件全树逐文件读首行注释)。
145
+ const currentModules: ModuleInfo[] = [];
146
+ if (!capsIsCapabilityListing) {
147
+ for (const srcDir of srcDirs) {
148
+ try {
149
+ const modules = await scanSourceModules(path.join(projectPath, srcDir), projectPath);
150
+ currentModules.push(...modules);
151
+ } catch {
152
+ if (!isJson) {
153
+ log(io, chalk.yellow(`⚠️ 未找到 ${srcDir} 目录,跳过`));
154
+ }
155
+ }
156
+ }
157
+ }
158
+
147
159
  // 3. 对比差异(分两种格式)
148
160
  let capCountMismatches: string[] = [];
149
161
 
@@ -176,7 +188,11 @@ export async function syncDocs(
176
188
  result.removed = verdict.deadEntries;
177
189
  }
178
190
 
179
- // 4. 检查 CONTEXT.md(缺失 + 过时)
191
+ // 4. 检查 CONTEXT.md(缺失 + 内容漂移 + mtime 提示)
192
+ // CI 标志:CI runner 上是全新 checkout,mtime 由 clone 顺序决定(同目录 CONTEXT.md 排序
193
+ // 恒先于 .ts),比出来的「过时」是假灯(harness#142 实测证据)→ 连提示都不给。
194
+ const isCi = Boolean(process.env.CI) && process.env.CI !== 'false';
195
+
180
196
  // 4a. 配置中要求的目录:检查缺失
181
197
  const contextDirs = await getRequiredContextDirs(projectPath);
182
198
  for (const dir of contextDirs) {
@@ -188,16 +204,35 @@ export async function syncDocs(
188
204
  }
189
205
  }
190
206
 
191
- // 4b. 自动发现已有的 CONTEXT.md:检查过时
207
+ // 4b. 自动发现已有的 CONTEXT.md:内容判定(漂移 = 判定面)+ mtime(仅本地提示)
192
208
  const existingContextFiles = await findExistingContextFiles(projectPath, srcDirs);
193
209
  for (const dir of existingContextFiles) {
194
210
  const contextPath = path.join(projectPath, dir, 'CONTEXT.md');
195
211
  try {
196
- const contextStat = await fs.stat(contextPath);
197
- const dirPath = path.join(projectPath, dir);
198
- const latestTsMtime = await getLatestTsMtime(dirPath);
199
- if (latestTsMtime && latestTsMtime > contextStat.mtimeMs) {
200
- result.contextStale.push(dir);
212
+ const contextMd = await fs.readFile(contextPath, 'utf-8');
213
+ const { surface, barrelExports } = await collectContextExportSurface(
214
+ path.join(projectPath, dir),
215
+ projectPath
216
+ );
217
+ const verdict = reconcileContext({
218
+ contextMdContent: contextMd,
219
+ exportSurface: surface,
220
+ barrelExports,
221
+ });
222
+ if (verdict.ghosts.length > 0 || verdict.unlistedBarrelExports.length > 0) {
223
+ result.contextContentDrift.push({
224
+ dir,
225
+ ghosts: verdict.ghosts,
226
+ unlisted: verdict.unlistedBarrelExports,
227
+ });
228
+ }
229
+
230
+ if (!isCi) {
231
+ const contextStat = await fs.stat(contextPath);
232
+ const latestTsMtime = await getLatestTsMtime(path.join(projectPath, dir));
233
+ if (latestTsMtime && latestTsMtime > contextStat.mtimeMs) {
234
+ result.contextStale.push(dir);
235
+ }
201
236
  }
202
237
  } catch {
203
238
  // 目录不存在或无法访问,跳过
@@ -231,8 +266,14 @@ export async function syncDocs(
231
266
 
232
267
  const hasTableIssues = result.added.length > 0 || result.removed.length > 0;
233
268
  const hasCapIssues = capCountMismatches.length > 0;
234
- const hasContextIssues = result.contextMissing.length > 0 || result.contextStale.length > 0;
269
+ // mtime 只作提示,不参与判定(harness#142):判定面是内容漂移与缺失
270
+ const hasContextIssues = result.contextMissing.length > 0 || result.contextContentDrift.length > 0;
235
271
  const hasIssues = hasTableIssues || hasCapIssues || hasContextIssues || hasAgentsIssues;
272
+ const contextDriftReason = result.contextContentDrift
273
+ .map((d) =>
274
+ `${d.dir}/CONTEXT.md(幽灵符号: ${d.ghosts.join(', ') || '无'};` +
275
+ `barrel 未登记: ${d.unlisted.join(', ') || '无'})`)
276
+ .join('、');
236
277
 
237
278
  // 5. JSON 输出模式:结构化输出供 LLM 消费
238
279
  if (isJson) {
@@ -245,6 +286,7 @@ export async function syncDocs(
245
286
  capCountMismatches: capCountMismatches.length,
246
287
  contextMissing: result.contextMissing.length,
247
288
  contextStale: result.contextStale.length,
289
+ contextContentDrift: result.contextContentDrift.length,
248
290
  },
249
291
  contextMissing: result.contextMissing.map(d => ({
250
292
  dir: d,
@@ -254,6 +296,12 @@ export async function syncDocs(
254
296
  dir: d,
255
297
  file: `${d}/CONTEXT.md`,
256
298
  })),
299
+ contentDrift: result.contextContentDrift.map((d) => ({
300
+ dir: d.dir,
301
+ file: `${d.dir}/CONTEXT.md`,
302
+ ghosts: d.ghosts,
303
+ unlisted: d.unlisted,
304
+ })),
257
305
  resolution: [] as Array<Record<string, unknown>>,
258
306
  };
259
307
 
@@ -298,6 +346,15 @@ export async function syncDocs(
298
346
  ...(result.contextMissing.length > 0
299
347
  ? [{ action: 'create-context-md', command: 'harness sync-docs', dirs: result.contextMissing }]
300
348
  : []),
349
+ ...(result.contextContentDrift.length > 0
350
+ ? [{
351
+ action: 'fix-context-content-drift',
352
+ details:
353
+ 'CONTEXT.md「核心导出」节与目录导出面不一致:要么按实现改文档,要么在票面记录'
354
+ + '「文档正确、代码待改」;散文不可机械生成,sync-docs 不改写 CONTEXT.md',
355
+ drift: result.contextContentDrift,
356
+ }]
357
+ : []),
301
358
  ...(result.contextStale.length > 0
302
359
  ? [{ action: 'update-context-md', command: 'harness sync-docs', dirs: result.contextStale }]
303
360
  : []),
@@ -348,8 +405,22 @@ export async function syncDocs(
348
405
  result.contextMissing.forEach(d => log(io, chalk.gray(` - ${d}/CONTEXT.md`)));
349
406
  }
350
407
 
408
+ if (result.contextContentDrift.length > 0) {
409
+ log(io, chalk.yellow(`\n📋 CONTEXT.md 与实现漂移(「核心导出」节 vs 目录导出面):`));
410
+ result.contextContentDrift.forEach((d) => {
411
+ log(io, chalk.gray(` - ${d.dir}/CONTEXT.md`));
412
+ if (d.ghosts.length > 0) {
413
+ log(io, chalk.gray(` 幽灵符号(文档声明、导出面已无): ${d.ghosts.join(', ')}`));
414
+ }
415
+ if (d.unlisted.length > 0) {
416
+ log(io, chalk.gray(` barrel 未登记(公开值符号未进「核心导出」节): ${d.unlisted.join(', ')}`));
417
+ }
418
+ });
419
+ log(io, chalk.gray(' 散文不可机械生成:按实现改文档,或在票面记录「文档正确、代码待改」'));
420
+ }
421
+
351
422
  if (result.contextStale.length > 0) {
352
- log(io, chalk.yellow(`\n📋 CONTEXT.md 可能过时(源码比文档新):`));
423
+ log(io, chalk.gray(`\n💡 提示:以下 CONTEXT.md 的源码比文档新(仅提示,不判失败):`));
353
424
  result.contextStale.forEach(d => log(io, chalk.gray(` - ${d}/CONTEXT.md`)));
354
425
  }
355
426
 
@@ -378,7 +449,12 @@ export async function syncDocs(
378
449
  // 7. 检查模式:只报告,不修改
379
450
  if (isCheck) {
380
451
  log(io, chalk.red('\n❌ 文档不是最新的,请运行 harness sync-docs 更新'));
381
- return drift('文档不是最新的,请运行 harness sync-docs 更新');
452
+ // reason 必须可定位(harness#142):CI 判红时要直接拿到文件与符号,不靠翻 stdout
453
+ return drift(
454
+ contextDriftReason
455
+ ? `文档不是最新的(CONTEXT.md 与实现漂移:${contextDriftReason})`
456
+ : '文档不是最新的,请运行 harness sync-docs 更新'
457
+ );
382
458
  }
383
459
 
384
460
  // 8. 写入模式:更新文档
@@ -394,8 +470,7 @@ export async function syncDocs(
394
470
  }
395
471
 
396
472
  for (const dir of result.contextMissing) {
397
- await createContextMd(projectPath, dir);
398
- log(io, chalk.green(`✅ 已创建 ${dir}/CONTEXT.md`));
473
+ await createContextMd(projectPath, dir, io);
399
474
  }
400
475
 
401
476
  if (hasAgentsIssues && agentsMdExpected !== null) {
@@ -15,11 +15,28 @@ export interface ModuleInfo {
15
15
  description: string;
16
16
  }
17
17
 
18
+ /** 描述提取的并发上限(#147 固定缺省,按票下裁决不做成配置面) */
19
+ const DESCRIPTION_CONCURRENCY = 16;
20
+
21
+ /**
22
+ * CONTEXT.md「核心导出」节与目录导出面的内容漂移(harness#142 / ADR-0025)。
23
+ * 两清单任一非空即漂移;判定本体在 core/constraints/context-reconcile。
24
+ */
25
+ export interface ContextContentDrift {
26
+ dir: string;
27
+ /** 幽灵:文档声明、导出面已无 */
28
+ ghosts: string[];
29
+ /** 未登记:barrel 公开值符号未进「核心导出」节 */
30
+ unlisted: string[];
31
+ }
32
+
18
33
  export interface SyncResult {
19
34
  added: string[];
20
35
  removed: string[];
21
36
  contextMissing: string[];
37
+ /** mtime 提示(源码比文档新):不参与判定,只在本地给提示 */
22
38
  contextStale: string[];
39
+ contextContentDrift: ContextContentDrift[];
23
40
  }
24
41
 
25
42
  export interface PackageJsonLite {
@@ -94,10 +111,13 @@ export async function getSourceDirs(projectPath: string): Promise<string[]> {
94
111
  }
95
112
 
96
113
  /**
97
- * 扫描源码目录,提取模块信息
114
+ * 扫描源码目录,提取模块信息(harness#147:路径与描述分两步取)
115
+ *
116
+ * 第一步定清单(目录遍历序,逐字保持改造前口径),第二步并发取描述并按**原索引**回填
117
+ * ——清单顺序是对外可见面(`--check` 逐行比对生成的表格),不随读取完成先后漂移。
98
118
  */
99
119
  export async function scanSourceModules(srcDir: string, projectPath: string): Promise<ModuleInfo[]> {
100
- const modules: ModuleInfo[] = [];
120
+ const found: { absPath: string; name: string; file: string }[] = [];
101
121
 
102
122
  let entries: string[];
103
123
  try {
@@ -119,22 +139,46 @@ export async function scanSourceModules(srcDir: string, projectPath: string): Pr
119
139
  // 与 .ts 同口径属预期,一次性补登记即可)
120
140
  const subFiles = findTsSourceFiles(entryPath, { skipIndex: true, includeTsx: true });
121
141
  for (const f of subFiles) {
122
- modules.push({
142
+ found.push({
143
+ absPath: f,
123
144
  name: path.basename(f, path.extname(f)),
124
145
  file: path.relative(projectPath, f),
125
- description: await extractFileDescription(f),
126
146
  });
127
147
  }
128
148
  } else if (isTsSourceFile(entry, { skipIndex: true, includeTsx: true })) {
129
- modules.push({
149
+ found.push({
150
+ absPath: entryPath,
130
151
  name: path.basename(entry, path.extname(entry)),
131
152
  file: path.relative(projectPath, entryPath),
132
- description: await extractFileDescription(entryPath),
133
153
  });
134
154
  }
135
155
  }
136
156
 
137
- return modules;
157
+ const descriptions = await extractFileDescriptions(found.map((m) => m.absPath));
158
+ return found.map((m, i) => ({ name: m.name, file: m.file, description: descriptions[i] }));
159
+ }
160
+
161
+ /**
162
+ * 批量提取首行注释描述:并发取数、按索引回填(harness#147)
163
+ *
164
+ * 返回数组与入参同序同长,故调用方拿到的是「问哪份文件答哪份文件」。
165
+ * 上限是固定缺省(不是配置面):描述提取是几百次小文件读,再放大并发只会压线程池。
166
+ */
167
+ export async function extractFileDescriptions(filePaths: string[]): Promise<string[]> {
168
+ const descriptions = new Array<string>(filePaths.length);
169
+ let cursor = 0;
170
+
171
+ async function worker(): Promise<void> {
172
+ while (cursor < filePaths.length) {
173
+ const index = cursor++;
174
+ descriptions[index] = await extractFileDescription(filePaths[index]);
175
+ }
176
+ }
177
+
178
+ const workers = Math.min(DESCRIPTION_CONCURRENCY, filePaths.length);
179
+ await Promise.all(Array.from({ length: workers }, () => worker()));
180
+
181
+ return descriptions;
138
182
  }
139
183
 
140
184
  /**
@@ -11,6 +11,7 @@ import * as yaml from 'js-yaml';
11
11
  import { CheckpointValidator } from '../../core/validators/checkpoint';
12
12
  import type { Checkpoint, CheckpointContext } from '../../types/checkpoint';
13
13
  import { log, processIO, type CommandIO, type CommandResult } from '../command-contract';
14
+ import { DEFAULT_CHECKPOINT_FILE } from './scaffold-templates';
14
15
 
15
16
  export interface ValidateOptions {
16
17
  /** 检查点文件路径 */
@@ -21,11 +22,6 @@ export interface ValidateOptions {
21
22
  strict?: boolean;
22
23
  }
23
24
 
24
- /**
25
- * 默认检查点文件路径
26
- */
27
- const DEFAULT_CHECKPOINT_FILE = '.harness/checkpoints.yml';
28
-
29
25
  /**
30
26
  * 加载检查点配置
31
27
  */
@@ -107,102 +103,3 @@ export async function validate(
107
103
  log(io, chalk.green('\n✅ 所有检查点验证通过'));
108
104
  return { kind: 'ok' };
109
105
  }
110
-
111
- /**
112
- * 默认检查点列表(供 init 和 upgrade 共用)
113
- *
114
- * 注:no-console 检查点已移除(工单 23/24)——CLI 产品 src/ 必然有合法 console 输出,
115
- * 且旧配置 expected:'' 语义恒错;output_* 族修复为真正执行 config.command 后该检查会恒失败。
116
- */
117
- export const DEFAULT_CHECKPOINTS: Checkpoint[] = [
118
- {
119
- id: 'build-success',
120
- name: '构建成功',
121
- checks: [
122
- {
123
- id: 'build-command',
124
- type: 'command_success',
125
- config: { command: 'npm run build' },
126
- message: '构建命令必须成功执行',
127
- },
128
- ],
129
- },
130
- {
131
- id: 'test-pass',
132
- name: '测试通过',
133
- checks: [
134
- {
135
- id: 'test-command',
136
- type: 'command_success',
137
- config: { command: 'npm test' },
138
- message: '测试命令必须成功执行',
139
- },
140
- ],
141
- },
142
- ];
143
-
144
- /**
145
- * 创建示例检查点文件
146
- */
147
- export async function createExampleCheckpoint(projectPath: string, io: CommandIO = processIO): Promise<void> {
148
- const filePath = path.join(projectPath, DEFAULT_CHECKPOINT_FILE);
149
- const dir = path.dirname(filePath);
150
-
151
- // 如果已存在,不覆盖
152
- try {
153
- await fs.access(filePath);
154
- log(io, chalk.gray(`checkpoints.yml 已存在,跳过`));
155
- return;
156
- } catch {
157
- // 文件不存在,创建
158
- }
159
-
160
- await fs.mkdir(dir, { recursive: true });
161
-
162
- const content = yaml.dump({ checkpoints: DEFAULT_CHECKPOINTS }, { indent: 2 });
163
- await fs.writeFile(filePath, content, 'utf-8');
164
-
165
- log(io, chalk.green(`✅ 已创建示例检查点文件: ${filePath}`));
166
- }
167
-
168
- /**
169
- * 默认 Resolutions(RKB — 约束 → 已知解法映射)
170
- */
171
- const DEFAULT_RESOLUTIONS = {
172
- no_fuzzy_completion_claim: {
173
- title: 'commit message 缺少验证证据',
174
- fix: '在 commit message body 中附上验证输出:\n`npx @dommaker/harness check --staged` | `npx @dommaker/harness validate` | `npm test -- --coverage`\n确认全部通过后重新 commit。',
175
- },
176
- capability_sync: {
177
- title: '缺少 CAPABILITIES.md',
178
- fix: '在项目根目录创建 CAPABILITIES.md,列出所有模块能力清单。运行 `npx @dommaker/harness sync-docs` 可自动生成模板。',
179
- },
180
- context_doc_sync: {
181
- title: '关键目录缺少 CONTEXT.md',
182
- fix: '在 required_dirs 目录下创建 CONTEXT.md。运行 `npx @dommaker/harness sync-docs` 可自动生成模板。',
183
- },
184
- };
185
-
186
- /**
187
- * 创建示例 Resolutions 文件(RKB dogfood)
188
- */
189
- export async function createExampleResolutions(projectPath: string, io: CommandIO = processIO): Promise<void> {
190
- const harnessDir = path.join(projectPath, '.harness');
191
- const filePath = path.join(harnessDir, 'resolutions.json');
192
-
193
- // 如果已存在,不覆盖
194
- try {
195
- await fs.access(filePath);
196
- log(io, chalk.gray(`resolutions.json 已存在,跳过`));
197
- return;
198
- } catch {
199
- // 文件不存在,创建
200
- }
201
-
202
- await fs.mkdir(harnessDir, { recursive: true });
203
-
204
- const content = JSON.stringify(DEFAULT_RESOLUTIONS, null, 2);
205
- await fs.writeFile(filePath, content, 'utf-8');
206
-
207
- log(io, chalk.green(`✅ 已创建 Resolutions 文件: ${filePath}`));
208
- }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * 门禁命令共享面(架构评审候选1)
3
+ *
4
+ * 六个门禁命令原先各写一遍「报告 → 成败」的翻译与 ✓/✗ 输出,同一句话抄 6 处。
5
+ * 这里只留一份:`GateDecision → CommandResult` 的唯一映射,加人类视图骨架。
6
+ *
7
+ * `command` 门禁的输出是单行机器友好形态(自带 ✓/✗ 与 --json),只复用映射、
8
+ * 不套骨架——套的话等于逼它换掉已对外的输出形状。
9
+ */
10
+
11
+ import chalk from 'chalk';
12
+ import type { GateDecision, GateResult } from '../gates/types';
13
+ import { log, type CommandIO, type CommandResult } from './command-contract';
14
+
15
+ /**
16
+ * 门禁命令的人类视图
17
+ */
18
+ export interface GateCommandView {
19
+ /** 门禁 id(与注册表一致),决定失败 reason 的前缀 */
20
+ gateId: string;
21
+ /** 横幅用词,如「代码审查门控」「验收标准」 */
22
+ label: string;
23
+ /** 通过时追加的指标行(自行上色,空串表示空行) */
24
+ onPass?: (result: GateResult) => string[];
25
+ /** 未通过时在原因行之后追加的详情行 */
26
+ onFail?: (result: GateResult) => string[];
27
+ }
28
+
29
+ /**
30
+ * 决策 → 命令结果:六个门禁唯一一处映射
31
+ */
32
+ export function gateCommandResult(gateId: string, decision: GateDecision): CommandResult {
33
+ if (decision.status === 'abstain') {
34
+ return { kind: 'ok' };
35
+ }
36
+ // ask 无实现者,按拦下计——与 runGates 的 fail-closed 聚合同一条规则
37
+ return { kind: 'fail', reason: `${gateId} gate denied: ${decision.result.message}` };
38
+ }
39
+
40
+ /**
41
+ * 打印决策并给出命令结果
42
+ */
43
+ export function reportGateDecision(
44
+ io: CommandIO,
45
+ view: GateCommandView,
46
+ decision: GateDecision
47
+ ): CommandResult {
48
+ const result = decision.result;
49
+ log(io);
50
+
51
+ if (decision.status === 'abstain') {
52
+ log(io, chalk.green(`✅ ${view.label}检查通过`));
53
+ for (const line of view.onPass?.(result) ?? []) {
54
+ log(io, line);
55
+ }
56
+ } else {
57
+ log(io, chalk.red(`❌ ${view.label}检查失败`));
58
+ log(io, chalk.red(` ${result.message}`));
59
+ for (const line of view.onFail?.(result) ?? []) {
60
+ log(io, line);
61
+ }
62
+ }
63
+
64
+ return gateCommandResult(view.gateId, decision);
65
+ }
66
+
67
+ /**
68
+ * 门禁自身之外的抛出物(git / fs)→ 出错横幅 + fail
69
+ */
70
+ export function reportGateError(
71
+ io: CommandIO,
72
+ gateId: string,
73
+ label: string,
74
+ error: unknown,
75
+ extra?: (message: string) => string[]
76
+ ): CommandResult {
77
+ const message = error instanceof Error ? error.message : String(error);
78
+
79
+ log(io);
80
+ log(io, chalk.red(`❌ ${label}检查出错`));
81
+ log(io, chalk.red(` ${message}`));
82
+ for (const line of extra?.(message) ?? []) {
83
+ log(io, line);
84
+ }
85
+
86
+ return { kind: 'fail', reason: `${gateId} gate error: ${message}` };
87
+ }
@@ -10,7 +10,16 @@
10
10
  * 函数不碰文件系统与 git;commits 由调用方(completion-gates 类守卫)供给。
11
11
  */
12
12
 
13
- export * from './types';
13
+ export type {
14
+ CommitInput,
15
+ CheckerVerdict,
16
+ CommitVerdict,
17
+ TddChainResult,
18
+ PhaseFormatResult,
19
+ ContractPresenceContext,
20
+ ContractPresenceResult,
21
+ CompletionCheckersConfig,
22
+ } from './types';
14
23
  export { DEFAULT_TEST_GLOBS, DEFAULT_NONCODE_GLOBS, matchGlob, matchAnyGlob } from './glob-match';
15
24
  export { classifyCommitFiles, resolveGlobs } from './classify';
16
25
  export type { CommitFileClassification } from './classify';
@@ -5,8 +5,9 @@
5
5
 
6
6
  ## 核心导出
7
7
  - `TokenBudget` — 多级 token 预算(system/user/tool/reserve)
8
+ - `TokenEstimator` — 快速 token 估算静态类(`estimateText`:英文按 1 token≈4 字符、中文≈1.5 字符,非精确 tokenizer;仓内消费方是 `KnowledgeInjector` 的截断取数)
8
9
  - `SessionManager` — 会话管理
9
- - `KnowledgeInjector` — 知识注入引擎
10
+ - `KnowledgeInjector` — 知识注入引擎;`origin === 'external'` 条目注入时带 `[External Source — verify before acting]` 前缀(标记唯一正本是 `knowledge/query.ts` 的 `EXTERNAL_SOURCE_MARKER`,本目录不另存字面量),注入 source 的 metadata 带 `entryId`/`maturity`/`origin`(外部内容三层防御第二层,harness#161)
10
11
 
11
12
  ## 依赖关系
12
13
  - 依赖 `src/types/` 公共类型
@@ -143,6 +143,33 @@ describe('KnowledgeInjector', () => {
143
143
  });
144
144
  });
145
145
 
146
+ describe('外部内容 retrieval marking(harness#161 三层防御第二层接线)', () => {
147
+ it('origin: external 条目注入时 prompt 带来源标记,metadata 含 origin', () => {
148
+ saveEntry({ id: 'ext-1', title: 'External Tip', origin: 'external' });
149
+ const result = injector.inject({ budget: 8000 });
150
+ expect(result.sources).toHaveLength(1);
151
+ expect(result.sources[0].content).toContain('[External Source — verify before acting]');
152
+ expect(result.sources[0].metadata).toMatchObject({ entryId: 'ext-1', origin: 'external' });
153
+ });
154
+
155
+ it('非 external 条目不带来源标记,metadata 仍带 origin', () => {
156
+ saveEntry({ id: 'int-1', title: 'Internal', origin: 'agent' });
157
+ const result = injector.inject({ budget: 8000 });
158
+ expect(result.sources).toHaveLength(1);
159
+ expect(result.sources[0].content).not.toContain('[External Source — verify before acting]');
160
+ expect(result.sources[0].metadata).toMatchObject({ origin: 'agent' });
161
+ });
162
+
163
+ it('external 条目的摘要注入同样带来源标记与 origin metadata', () => {
164
+ saveEntry({ id: 'ext-2', title: 'Ext', origin: 'external' });
165
+ const result = injector.inject({ budget: 8000, exclude: ['ext-2'] });
166
+ const summary = result.sources.find(s => s.id === 'knowledge-summary-ext-2');
167
+ expect(summary).toBeDefined();
168
+ expect(summary!.content).toContain('[External Source — verify before acting]');
169
+ expect(summary!.metadata).toMatchObject({ origin: 'external' });
170
+ });
171
+ });
172
+
146
173
  describe('getQuery', () => {
147
174
  it('应该返回查询引擎', () => {
148
175
  expect(injector.getQuery()).toBe(query);
@@ -5,18 +5,25 @@
5
5
  import { SessionManager } from '../session-manager';
6
6
  import * as fs from 'fs';
7
7
 
8
- jest.mock('fs', () => ({
9
- mkdirSync: jest.fn(),
10
- appendFileSync: jest.fn(),
11
- existsSync: jest.fn().mockReturnValue(false),
12
- readFileSync: jest.fn().mockReturnValue(''),
13
- readdirSync: jest.fn().mockReturnValue([]),
14
- writeFileSync: jest.fn(),
15
- statSync: jest.fn().mockReturnValue({
16
- birthtime: new Date(),
17
- mtime: new Date(),
18
- }),
19
- }));
8
+ jest.mock('fs', () => {
9
+ // restoreSession 经有界 head 读 events.jsonl(ADR-0023 决策 3),需要倒读四件;
10
+ // 内容仍由下面按路径分派的 readFileSync 提供,两条读入口同源(夹具说明见其文件头)
11
+ const { jsonlBoundedReadChain } = require('../../test-setup/jsonl-fake-fs');
12
+ const readFileSync = jest.fn().mockReturnValue('');
13
+ return {
14
+ mkdirSync: jest.fn(),
15
+ appendFileSync: jest.fn(),
16
+ existsSync: jest.fn().mockReturnValue(false),
17
+ readFileSync,
18
+ readdirSync: jest.fn().mockReturnValue([]),
19
+ writeFileSync: jest.fn(),
20
+ statSync: jest.fn().mockReturnValue({
21
+ birthtime: new Date(),
22
+ mtime: new Date(),
23
+ }),
24
+ ...jsonlBoundedReadChain((p: string) => readFileSync(p, 'utf-8')),
25
+ };
26
+ });
20
27
 
21
28
  const mockFs = fs as jest.Mocked<typeof fs>;
22
29