@dommaker/harness 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (532) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +0 -2
  3. package/bin/harness.js +86 -32
  4. package/dist/cli/command-contract.d.ts +83 -0
  5. package/dist/cli/command-contract.d.ts.map +1 -0
  6. package/dist/cli/command-contract.js +66 -0
  7. package/dist/cli/command-contract.js.map +1 -0
  8. package/dist/cli/commands/acceptance.d.ts +3 -2
  9. package/dist/cli/commands/acceptance.d.ts.map +1 -1
  10. package/dist/cli/commands/acceptance.js +28 -24
  11. package/dist/cli/commands/acceptance.js.map +1 -1
  12. package/dist/cli/commands/analyze-sessions.d.ts +2 -1
  13. package/dist/cli/commands/analyze-sessions.d.ts.map +1 -1
  14. package/dist/cli/commands/analyze-sessions.js +25 -24
  15. package/dist/cli/commands/analyze-sessions.js.map +1 -1
  16. package/dist/cli/commands/check.d.ts +14 -2
  17. package/dist/cli/commands/check.d.ts.map +1 -1
  18. package/dist/cli/commands/check.js +104 -61
  19. package/dist/cli/commands/check.js.map +1 -1
  20. package/dist/cli/commands/command.d.ts +5 -1
  21. package/dist/cli/commands/command.d.ts.map +1 -1
  22. package/dist/cli/commands/command.js +26 -17
  23. package/dist/cli/commands/command.js.map +1 -1
  24. package/dist/cli/commands/constraints-report.d.ts +2 -1
  25. package/dist/cli/commands/constraints-report.d.ts.map +1 -1
  26. package/dist/cli/commands/constraints-report.js +37 -33
  27. package/dist/cli/commands/constraints-report.js.map +1 -1
  28. package/dist/cli/commands/constraints-retire.d.ts +6 -5
  29. package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
  30. package/dist/cli/commands/constraints-retire.js +50 -52
  31. package/dist/cli/commands/constraints-retire.js.map +1 -1
  32. package/dist/cli/commands/constraints.d.ts +2 -1
  33. package/dist/cli/commands/constraints.d.ts.map +1 -1
  34. package/dist/cli/commands/constraints.js +10 -27
  35. package/dist/cli/commands/constraints.js.map +1 -1
  36. package/dist/cli/commands/contract.d.ts +3 -2
  37. package/dist/cli/commands/contract.d.ts.map +1 -1
  38. package/dist/cli/commands/contract.js +45 -42
  39. package/dist/cli/commands/contract.js.map +1 -1
  40. package/dist/cli/commands/definitions.d.ts +11 -11
  41. package/dist/cli/commands/definitions.d.ts.map +1 -1
  42. package/dist/cli/commands/definitions.js +27 -219
  43. package/dist/cli/commands/definitions.js.map +1 -1
  44. package/dist/cli/commands/failure.d.ts +5 -4
  45. package/dist/cli/commands/failure.d.ts.map +1 -1
  46. package/dist/cli/commands/failure.js +31 -25
  47. package/dist/cli/commands/failure.js.map +1 -1
  48. package/dist/cli/commands/init.d.ts +7 -5
  49. package/dist/cli/commands/init.d.ts.map +1 -1
  50. package/dist/cli/commands/init.js +147 -211
  51. package/dist/cli/commands/init.js.map +1 -1
  52. package/dist/cli/commands/knowledge.d.ts +17 -30
  53. package/dist/cli/commands/knowledge.d.ts.map +1 -1
  54. package/dist/cli/commands/knowledge.js +183 -273
  55. package/dist/cli/commands/knowledge.js.map +1 -1
  56. package/dist/cli/commands/passes-gate.d.ts +8 -2
  57. package/dist/cli/commands/passes-gate.d.ts.map +1 -1
  58. package/dist/cli/commands/passes-gate.js +44 -66
  59. package/dist/cli/commands/passes-gate.js.map +1 -1
  60. package/dist/cli/commands/performance.d.ts +2 -1
  61. package/dist/cli/commands/performance.d.ts.map +1 -1
  62. package/dist/cli/commands/performance.js +20 -18
  63. package/dist/cli/commands/performance.js.map +1 -1
  64. package/dist/cli/commands/posteval-plan.d.ts +5 -1
  65. package/dist/cli/commands/posteval-plan.d.ts.map +1 -1
  66. package/dist/cli/commands/posteval-plan.js +26 -15
  67. package/dist/cli/commands/posteval-plan.js.map +1 -1
  68. package/dist/cli/commands/release.d.ts +6 -1
  69. package/dist/cli/commands/release.d.ts.map +1 -1
  70. package/dist/cli/commands/release.js +69 -59
  71. package/dist/cli/commands/release.js.map +1 -1
  72. package/dist/cli/commands/report.d.ts +2 -1
  73. package/dist/cli/commands/report.d.ts.map +1 -1
  74. package/dist/cli/commands/report.js +9 -5
  75. package/dist/cli/commands/report.js.map +1 -1
  76. package/dist/cli/commands/review.d.ts +3 -2
  77. package/dist/cli/commands/review.d.ts.map +1 -1
  78. package/dist/cli/commands/review.js +33 -29
  79. package/dist/cli/commands/review.js.map +1 -1
  80. package/dist/cli/commands/sdd.d.ts +2 -1
  81. package/dist/cli/commands/sdd.d.ts.map +1 -1
  82. package/dist/cli/commands/sdd.js +7 -5
  83. package/dist/cli/commands/sdd.js.map +1 -1
  84. package/dist/cli/commands/security.d.ts +3 -2
  85. package/dist/cli/commands/security.d.ts.map +1 -1
  86. package/dist/cli/commands/security.js +29 -26
  87. package/dist/cli/commands/security.js.map +1 -1
  88. package/dist/cli/commands/spec-baseline-check.d.ts +2 -1
  89. package/dist/cli/commands/spec-baseline-check.d.ts.map +1 -1
  90. package/dist/cli/commands/spec-baseline-check.js +15 -11
  91. package/dist/cli/commands/spec-baseline-check.js.map +1 -1
  92. package/dist/cli/commands/spec.d.ts +7 -2
  93. package/dist/cli/commands/spec.d.ts.map +1 -1
  94. package/dist/cli/commands/spec.js +61 -51
  95. package/dist/cli/commands/spec.js.map +1 -1
  96. package/dist/cli/commands/status.d.ts +2 -1
  97. package/dist/cli/commands/status.d.ts.map +1 -1
  98. package/dist/cli/commands/status.js +57 -53
  99. package/dist/cli/commands/status.js.map +1 -1
  100. package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
  101. package/dist/cli/commands/sync-docs/agents-syncer.js +5 -2
  102. package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
  103. package/dist/cli/commands/sync-docs/context-syncer.d.ts.map +1 -1
  104. package/dist/cli/commands/sync-docs/context-syncer.js +3 -2
  105. package/dist/cli/commands/sync-docs/context-syncer.js.map +1 -1
  106. package/dist/cli/commands/sync-docs/index.d.ts +2 -1
  107. package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
  108. package/dist/cli/commands/sync-docs/index.js +68 -88
  109. package/dist/cli/commands/sync-docs/index.js.map +1 -1
  110. package/dist/cli/commands/sync-docs/project-reader.d.ts +6 -1
  111. package/dist/cli/commands/sync-docs/project-reader.d.ts.map +1 -1
  112. package/dist/cli/commands/sync-docs/project-reader.js +14 -16
  113. package/dist/cli/commands/sync-docs/project-reader.js.map +1 -1
  114. package/dist/cli/commands/update-user-model.d.ts +9 -3
  115. package/dist/cli/commands/update-user-model.d.ts.map +1 -1
  116. package/dist/cli/commands/update-user-model.js +56 -61
  117. package/dist/cli/commands/update-user-model.js.map +1 -1
  118. package/dist/cli/commands/validate.d.ts +4 -3
  119. package/dist/cli/commands/validate.d.ts.map +1 -1
  120. package/dist/cli/commands/validate.js +28 -26
  121. package/dist/cli/commands/validate.js.map +1 -1
  122. package/dist/cli/session-mining/transcript.d.ts +7 -1
  123. package/dist/cli/session-mining/transcript.d.ts.map +1 -1
  124. package/dist/cli/session-mining/transcript.js +21 -14
  125. package/dist/cli/session-mining/transcript.js.map +1 -1
  126. package/dist/context/session-manager.d.ts.map +1 -1
  127. package/dist/context/session-manager.js +19 -32
  128. package/dist/context/session-manager.js.map +1 -1
  129. package/dist/core/constraints/agent-prompt-renderer.d.ts.map +1 -1
  130. package/dist/core/constraints/agent-prompt-renderer.js +1 -4
  131. package/dist/core/constraints/agent-prompt-renderer.js.map +1 -1
  132. package/dist/core/constraints/capabilities-parser.d.ts +24 -7
  133. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  134. package/dist/core/constraints/capabilities-parser.js +32 -28
  135. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  136. package/dist/core/constraints/capabilities-reconcile.d.ts +67 -0
  137. package/dist/core/constraints/capabilities-reconcile.d.ts.map +1 -0
  138. package/dist/core/constraints/capabilities-reconcile.js +105 -0
  139. package/dist/core/constraints/capabilities-reconcile.js.map +1 -0
  140. package/dist/core/constraints/checker.d.ts +21 -43
  141. package/dist/core/constraints/checker.d.ts.map +1 -1
  142. package/dist/core/constraints/checker.js +68 -117
  143. package/dist/core/constraints/checker.js.map +1 -1
  144. package/dist/core/constraints/checkers/capability-sync.d.ts +20 -8
  145. package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
  146. package/dist/core/constraints/checkers/capability-sync.js +66 -77
  147. package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
  148. package/dist/core/constraints/checkers/context-doc-sync.d.ts +4 -2
  149. package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
  150. package/dist/core/constraints/checkers/context-doc-sync.js +13 -22
  151. package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
  152. package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
  153. package/dist/core/constraints/checkers/docs-freshness.js +49 -66
  154. package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
  155. package/dist/core/constraints/checkers/governance-presence.d.ts +2 -2
  156. package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
  157. package/dist/core/constraints/checkers/governance-presence.js +7 -28
  158. package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
  159. package/dist/core/constraints/checkers/index.d.ts +2 -2
  160. package/dist/core/constraints/checkers/index.d.ts.map +1 -1
  161. package/dist/core/constraints/checkers/index.js +2 -1
  162. package/dist/core/constraints/checkers/index.js.map +1 -1
  163. package/dist/core/constraints/checkers/no-test-simplification.d.ts.map +1 -1
  164. package/dist/core/constraints/checkers/no-test-simplification.js +4 -3
  165. package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
  166. package/dist/core/constraints/checkers/types.d.ts +43 -4
  167. package/dist/core/constraints/checkers/types.d.ts.map +1 -1
  168. package/dist/core/constraints/checkers/types.js +33 -0
  169. package/dist/core/constraints/checkers/types.js.map +1 -1
  170. package/dist/core/constraints/context-builder.d.ts +15 -14
  171. package/dist/core/constraints/context-builder.d.ts.map +1 -1
  172. package/dist/core/constraints/context-builder.js +32 -96
  173. package/dist/core/constraints/context-builder.js.map +1 -1
  174. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  175. package/dist/core/constraints/doc-freshness/runner.js +2 -1
  176. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  177. package/dist/core/constraints/git-evidence.d.ts +60 -0
  178. package/dist/core/constraints/git-evidence.d.ts.map +1 -0
  179. package/dist/core/constraints/git-evidence.js +88 -0
  180. package/dist/core/constraints/git-evidence.js.map +1 -0
  181. package/dist/core/constraints/injection-drift.d.ts +3 -20
  182. package/dist/core/constraints/injection-drift.d.ts.map +1 -1
  183. package/dist/core/constraints/injection-drift.js +10 -57
  184. package/dist/core/constraints/injection-drift.js.map +1 -1
  185. package/dist/core/constraints/injection-writer.d.ts +113 -0
  186. package/dist/core/constraints/injection-writer.d.ts.map +1 -0
  187. package/dist/core/constraints/injection-writer.js +228 -0
  188. package/dist/core/constraints/injection-writer.js.map +1 -0
  189. package/dist/core/constraints/usage-report.d.ts +22 -1
  190. package/dist/core/constraints/usage-report.d.ts.map +1 -1
  191. package/dist/core/constraints/usage-report.js +27 -20
  192. package/dist/core/constraints/usage-report.js.map +1 -1
  193. package/dist/core/index.d.ts +2 -2
  194. package/dist/core/index.d.ts.map +1 -1
  195. package/dist/core/index.js +4 -2
  196. package/dist/core/index.js.map +1 -1
  197. package/dist/core/project-config-loader.d.ts +32 -1
  198. package/dist/core/project-config-loader.d.ts.map +1 -1
  199. package/dist/core/project-config-loader.js +39 -9
  200. package/dist/core/project-config-loader.js.map +1 -1
  201. package/dist/core/validators/passes-gate.d.ts +15 -64
  202. package/dist/core/validators/passes-gate.d.ts.map +1 -1
  203. package/dist/core/validators/passes-gate.js +84 -222
  204. package/dist/core/validators/passes-gate.js.map +1 -1
  205. package/dist/core/validators/test-output.d.ts +58 -0
  206. package/dist/core/validators/test-output.d.ts.map +1 -0
  207. package/dist/core/validators/test-output.js +109 -0
  208. package/dist/core/validators/test-output.js.map +1 -0
  209. package/dist/failure/recorder.d.ts +6 -0
  210. package/dist/failure/recorder.d.ts.map +1 -1
  211. package/dist/failure/recorder.js +12 -8
  212. package/dist/failure/recorder.js.map +1 -1
  213. package/dist/gates/acceptance.d.ts +1 -48
  214. package/dist/gates/acceptance.d.ts.map +1 -1
  215. package/dist/gates/acceptance.js +6 -38
  216. package/dist/gates/acceptance.js.map +1 -1
  217. package/dist/gates/checker-gate.d.ts +2 -1
  218. package/dist/gates/checker-gate.d.ts.map +1 -1
  219. package/dist/gates/checker-gate.js +11 -13
  220. package/dist/gates/checker-gate.js.map +1 -1
  221. package/dist/gates/command.d.ts.map +1 -1
  222. package/dist/gates/command.js +10 -22
  223. package/dist/gates/command.js.map +1 -1
  224. package/dist/gates/contract.d.ts.map +1 -1
  225. package/dist/gates/contract.js +22 -61
  226. package/dist/gates/contract.js.map +1 -1
  227. package/dist/gates/performance.d.ts.map +1 -1
  228. package/dist/gates/performance.js +13 -31
  229. package/dist/gates/performance.js.map +1 -1
  230. package/dist/gates/review.d.ts.map +1 -1
  231. package/dist/gates/review.js +21 -53
  232. package/dist/gates/review.js.map +1 -1
  233. package/dist/gates/security.d.ts.map +1 -1
  234. package/dist/gates/security.js +14 -32
  235. package/dist/gates/security.js.map +1 -1
  236. package/dist/gates/types.d.ts +28 -5
  237. package/dist/gates/types.d.ts.map +1 -1
  238. package/dist/gates/types.js +38 -1
  239. package/dist/gates/types.js.map +1 -1
  240. package/dist/hooks/bootstrap.d.ts +2 -0
  241. package/dist/hooks/bootstrap.d.ts.map +1 -1
  242. package/dist/hooks/bootstrap.js +5 -2
  243. package/dist/hooks/bootstrap.js.map +1 -1
  244. package/dist/index.d.ts +1 -2
  245. package/dist/index.d.ts.map +1 -1
  246. package/dist/index.js.map +1 -1
  247. package/dist/knowledge/audit.d.ts +7 -2
  248. package/dist/knowledge/audit.d.ts.map +1 -1
  249. package/dist/knowledge/audit.js +59 -79
  250. package/dist/knowledge/audit.js.map +1 -1
  251. package/dist/knowledge/flywheel-metrics.d.ts +39 -0
  252. package/dist/knowledge/flywheel-metrics.d.ts.map +1 -0
  253. package/dist/knowledge/flywheel-metrics.js +47 -0
  254. package/dist/knowledge/flywheel-metrics.js.map +1 -0
  255. package/dist/knowledge/import.d.ts.map +1 -1
  256. package/dist/knowledge/import.js +4 -14
  257. package/dist/knowledge/import.js.map +1 -1
  258. package/dist/knowledge/index-generator.d.ts.map +1 -1
  259. package/dist/knowledge/index-generator.js +17 -14
  260. package/dist/knowledge/index-generator.js.map +1 -1
  261. package/dist/knowledge/ingest.d.ts.map +1 -1
  262. package/dist/knowledge/ingest.js +9 -4
  263. package/dist/knowledge/ingest.js.map +1 -1
  264. package/dist/knowledge/migration.d.ts.map +1 -1
  265. package/dist/knowledge/migration.js +11 -8
  266. package/dist/knowledge/migration.js.map +1 -1
  267. package/dist/knowledge/reference-tracker.d.ts.map +1 -1
  268. package/dist/knowledge/reference-tracker.js +12 -9
  269. package/dist/knowledge/reference-tracker.js.map +1 -1
  270. package/dist/knowledge/store.d.ts +15 -0
  271. package/dist/knowledge/store.d.ts.map +1 -1
  272. package/dist/knowledge/store.js +59 -10
  273. package/dist/knowledge/store.js.map +1 -1
  274. package/dist/monitoring/context-tracker.d.ts.map +1 -1
  275. package/dist/monitoring/context-tracker.js +11 -20
  276. package/dist/monitoring/context-tracker.js.map +1 -1
  277. package/dist/monitoring/index.d.ts +1 -1
  278. package/dist/monitoring/index.d.ts.map +1 -1
  279. package/dist/monitoring/index.js +1 -2
  280. package/dist/monitoring/index.js.map +1 -1
  281. package/dist/monitoring/trace-analyzer.d.ts +15 -0
  282. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  283. package/dist/monitoring/trace-analyzer.js +19 -2
  284. package/dist/monitoring/trace-analyzer.js.map +1 -1
  285. package/dist/monitoring/traces.d.ts +21 -2
  286. package/dist/monitoring/traces.d.ts.map +1 -1
  287. package/dist/monitoring/traces.js +38 -29
  288. package/dist/monitoring/traces.js.map +1 -1
  289. package/dist/release/index.d.ts +2 -2
  290. package/dist/release/index.d.ts.map +1 -1
  291. package/dist/release/index.js +1 -5
  292. package/dist/release/index.js.map +1 -1
  293. package/dist/sdd/index-generator.js +10 -11
  294. package/dist/sdd/index-generator.js.map +1 -1
  295. package/dist/test-setup/project-fixture.d.ts +48 -0
  296. package/dist/test-setup/project-fixture.d.ts.map +1 -0
  297. package/dist/test-setup/project-fixture.js +106 -0
  298. package/dist/test-setup/project-fixture.js.map +1 -0
  299. package/dist/types/constraint.d.ts +8 -18
  300. package/dist/types/constraint.d.ts.map +1 -1
  301. package/dist/types/constraint.js +6 -1
  302. package/dist/types/constraint.js.map +1 -1
  303. package/dist/types/passes-gate.d.ts +10 -25
  304. package/dist/types/passes-gate.d.ts.map +1 -1
  305. package/dist/types/project-config.d.ts +1 -1
  306. package/dist/types/session.d.ts +6 -5
  307. package/dist/types/session.d.ts.map +1 -1
  308. package/dist/types/trace.d.ts +9 -0
  309. package/dist/types/trace.d.ts.map +1 -1
  310. package/dist/utils/exec.d.ts +10 -0
  311. package/dist/utils/exec.d.ts.map +1 -1
  312. package/dist/utils/exec.js +12 -0
  313. package/dist/utils/exec.js.map +1 -1
  314. package/dist/utils/file-walk.d.ts +9 -1
  315. package/dist/utils/file-walk.d.ts.map +1 -1
  316. package/dist/utils/file-walk.js +10 -1
  317. package/dist/utils/file-walk.js.map +1 -1
  318. package/dist/utils/frontmatter.d.ts +73 -0
  319. package/dist/utils/frontmatter.d.ts.map +1 -0
  320. package/dist/utils/frontmatter.js +141 -0
  321. package/dist/utils/frontmatter.js.map +1 -0
  322. package/dist/utils/jsonl.d.ts +92 -0
  323. package/dist/utils/jsonl.d.ts.map +1 -0
  324. package/dist/utils/jsonl.js +166 -0
  325. package/dist/utils/jsonl.js.map +1 -0
  326. package/dist/utils/package-version.d.ts +16 -0
  327. package/dist/utils/package-version.d.ts.map +1 -0
  328. package/dist/utils/package-version.js +63 -0
  329. package/dist/utils/package-version.js.map +1 -0
  330. package/package.json +1 -1
  331. package/src/CONTEXT.md +8 -2
  332. package/src/__tests__/acceptance-gate.test.ts +3 -1
  333. package/src/__tests__/checker-extra.test.ts +7 -1218
  334. package/src/__tests__/context-files-resolution.test.ts +116 -0
  335. package/src/__tests__/effective-constraints.test.ts +10 -13
  336. package/src/__tests__/exec.test.ts +35 -1
  337. package/src/__tests__/layering.test.ts +152 -0
  338. package/src/__tests__/passes-gate-extra.test.ts +0 -145
  339. package/src/__tests__/passes-gate.test.ts +130 -101
  340. package/src/__tests__/skip-semantics.test.ts +5 -9
  341. package/src/cli/__tests__/command-contract.test.ts +170 -0
  342. package/src/cli/command-contract.ts +108 -0
  343. package/src/cli/commands/CONTEXT.md +23 -8
  344. package/src/cli/commands/__tests__/acceptance.test.ts +40 -33
  345. package/src/cli/commands/__tests__/analyze-sessions.test.ts +35 -29
  346. package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +107 -0
  347. package/src/cli/commands/__tests__/check-drift.test.ts +27 -34
  348. package/src/cli/commands/__tests__/check-skip-output.test.ts +21 -24
  349. package/src/cli/commands/__tests__/check.test.ts +448 -378
  350. package/src/cli/commands/__tests__/command.test.ts +85 -70
  351. package/src/cli/commands/__tests__/constraints-report.test.ts +100 -71
  352. package/src/cli/commands/__tests__/constraints-retire.test.ts +152 -95
  353. package/src/cli/commands/__tests__/contract.test.ts +27 -34
  354. package/src/cli/commands/__tests__/failure.test.ts +31 -34
  355. package/src/cli/commands/__tests__/init-injection.test.ts +58 -28
  356. package/src/cli/commands/__tests__/init.test.ts +77 -48
  357. package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +108 -0
  358. package/src/cli/commands/__tests__/knowledge.test.ts +69 -43
  359. package/src/cli/commands/__tests__/passes-gate.test.ts +43 -45
  360. package/src/cli/commands/__tests__/performance.test.ts +22 -23
  361. package/src/cli/commands/__tests__/posteval-plan.test.ts +39 -38
  362. package/src/cli/commands/__tests__/project-path-anchoring.test.ts +157 -0
  363. package/src/cli/commands/__tests__/project-path-convention.test.ts +231 -0
  364. package/src/cli/commands/__tests__/registry.test.ts +75 -35
  365. package/src/cli/commands/__tests__/release.test.ts +209 -80
  366. package/src/cli/commands/__tests__/report-extra.test.ts +16 -16
  367. package/src/cli/commands/__tests__/report.test.ts +25 -25
  368. package/src/cli/commands/__tests__/review.test.ts +33 -31
  369. package/src/cli/commands/__tests__/security.test.ts +23 -27
  370. package/src/cli/commands/__tests__/spec-baseline-check.test.ts +20 -20
  371. package/src/cli/commands/__tests__/spec.test.ts +41 -29
  372. package/src/cli/commands/__tests__/status-extra.test.ts +18 -18
  373. package/src/cli/commands/__tests__/status-skipped-lines.test.ts +72 -0
  374. package/src/cli/commands/__tests__/status.test.ts +34 -34
  375. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +77 -78
  376. package/src/cli/commands/__tests__/sync-docs.test.ts +72 -74
  377. package/src/cli/commands/__tests__/update-user-model.test.ts +97 -34
  378. package/src/cli/commands/__tests__/validate.test.ts +27 -29
  379. package/src/cli/commands/acceptance.ts +35 -24
  380. package/src/cli/commands/analyze-sessions.ts +25 -24
  381. package/src/cli/commands/check.ts +127 -66
  382. package/src/cli/commands/command.ts +30 -17
  383. package/src/cli/commands/constraints-report.ts +37 -34
  384. package/src/cli/commands/constraints-retire.ts +58 -55
  385. package/src/cli/commands/constraints.ts +10 -27
  386. package/src/cli/commands/contract.ts +51 -42
  387. package/src/cli/commands/definitions.ts +34 -227
  388. package/src/cli/commands/failure.ts +33 -26
  389. package/src/cli/commands/init.ts +166 -220
  390. package/src/cli/commands/knowledge.ts +192 -289
  391. package/src/cli/commands/passes-gate.ts +58 -68
  392. package/src/cli/commands/performance.ts +23 -18
  393. package/src/cli/commands/posteval-plan.ts +29 -15
  394. package/src/cli/commands/release.ts +70 -59
  395. package/src/cli/commands/report.ts +9 -5
  396. package/src/cli/commands/review.ts +39 -29
  397. package/src/cli/commands/sdd.ts +7 -5
  398. package/src/cli/commands/security.ts +35 -26
  399. package/src/cli/commands/spec-baseline-check.ts +16 -11
  400. package/src/cli/commands/spec.ts +66 -53
  401. package/src/cli/commands/status.ts +59 -53
  402. package/src/cli/commands/sync-docs/agents-syncer.ts +5 -2
  403. package/src/cli/commands/sync-docs/context-syncer.ts +3 -2
  404. package/src/cli/commands/sync-docs/index.ts +73 -100
  405. package/src/cli/commands/sync-docs/project-reader.ts +17 -18
  406. package/src/cli/commands/update-user-model.ts +64 -61
  407. package/src/cli/commands/validate.ts +31 -25
  408. package/src/cli/session-mining/__tests__/transcript.test.ts +118 -0
  409. package/src/cli/session-mining/transcript.ts +26 -8
  410. package/src/context/session-manager.ts +19 -33
  411. package/src/core/CONTEXT.md +10 -6
  412. package/src/core/constraints/__tests__/capabilities-reconcile.test.ts +200 -0
  413. package/src/core/constraints/__tests__/capability-counts.test.ts +94 -0
  414. package/src/core/constraints/__tests__/git-evidence.test.ts +208 -0
  415. package/src/core/constraints/__tests__/injection-drift.test.ts +3 -10
  416. package/src/core/constraints/__tests__/injection-writer.test.ts +231 -0
  417. package/src/core/constraints/__tests__/trace-injection.test.ts +71 -0
  418. package/src/core/constraints/__tests__/usage-report.test.ts +112 -0
  419. package/src/core/constraints/agent-prompt-renderer.ts +2 -6
  420. package/src/core/constraints/capabilities-parser.ts +53 -35
  421. package/src/core/constraints/capabilities-reconcile.ts +166 -0
  422. package/src/core/constraints/checker.ts +82 -133
  423. package/src/core/constraints/checkers/__tests__/capability-sync.test.ts +273 -0
  424. package/src/core/constraints/checkers/__tests__/check-outcome.test.ts +176 -0
  425. package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +77 -0
  426. package/src/core/constraints/checkers/__tests__/docs-freshness.test.ts +126 -0
  427. package/src/{__tests__ → core/constraints/checkers/__tests__}/governance-presence.test.ts +83 -9
  428. package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +50 -0
  429. package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +76 -0
  430. package/src/{__tests__ → core/constraints/checkers/__tests__}/no-hardcoded-credentials.test.ts +4 -3
  431. package/src/core/constraints/checkers/__tests__/no-test-simplification.test.ts +58 -0
  432. package/src/core/constraints/checkers/__tests__/three-state-consistency.test.ts +153 -0
  433. package/src/core/constraints/checkers/capability-sync.ts +75 -96
  434. package/src/core/constraints/checkers/context-doc-sync.ts +15 -25
  435. package/src/core/constraints/checkers/docs-freshness.ts +54 -70
  436. package/src/core/constraints/checkers/governance-presence.ts +7 -25
  437. package/src/core/constraints/checkers/index.ts +9 -2
  438. package/src/core/constraints/checkers/no-test-simplification.ts +4 -3
  439. package/src/core/constraints/checkers/types.ts +60 -4
  440. package/src/core/constraints/context-builder.ts +39 -91
  441. package/src/core/constraints/doc-freshness/runner.ts +2 -1
  442. package/src/core/constraints/git-evidence.ts +120 -0
  443. package/src/core/constraints/injection-drift.ts +13 -60
  444. package/src/core/constraints/injection-writer.ts +212 -0
  445. package/src/core/constraints/usage-report.ts +36 -17
  446. package/src/core/index.ts +4 -2
  447. package/src/core/project-config-loader.ts +49 -9
  448. package/src/core/validators/__tests__/passes-gate.test.ts +2 -56
  449. package/src/core/validators/__tests__/test-output.test.ts +372 -0
  450. package/src/core/validators/passes-gate.ts +86 -248
  451. package/src/core/validators/test-output.ts +128 -0
  452. package/src/failure/__tests__/recorder.test.ts +168 -0
  453. package/src/failure/recorder.ts +12 -10
  454. package/src/gates/CONTEXT.md +7 -4
  455. package/src/gates/__tests__/acceptance-types-single-source.test.ts +68 -0
  456. package/src/gates/__tests__/acceptance.test.ts +4 -86
  457. package/src/gates/__tests__/checker-gate.test.ts +24 -0
  458. package/src/gates/__tests__/effective-gates.test.ts +3 -8
  459. package/src/gates/__tests__/test-verdict-consistency.test.ts +140 -0
  460. package/src/gates/acceptance.ts +19 -84
  461. package/src/gates/checker-gate.ts +12 -15
  462. package/src/gates/command.ts +10 -16
  463. package/src/gates/contract.ts +22 -61
  464. package/src/gates/performance.ts +10 -22
  465. package/src/gates/review.ts +21 -41
  466. package/src/gates/security.ts +10 -22
  467. package/src/gates/types.ts +70 -5
  468. package/src/hooks/CONTEXT.md +1 -1
  469. package/src/hooks/bootstrap.ts +5 -2
  470. package/src/index.ts +0 -4
  471. package/src/knowledge/CONTEXT.md +8 -3
  472. package/src/knowledge/__tests__/audit-labels.test.ts +44 -0
  473. package/src/knowledge/__tests__/flywheel-metrics.test.ts +135 -0
  474. package/src/knowledge/__tests__/import.test.ts +44 -0
  475. package/src/knowledge/__tests__/index-generator.test.ts +53 -0
  476. package/src/knowledge/__tests__/ingest.test.ts +85 -0
  477. package/src/knowledge/__tests__/migration.test.ts +47 -0
  478. package/src/knowledge/__tests__/reference-tracker.test.ts +16 -0
  479. package/src/knowledge/__tests__/store.test.ts +194 -0
  480. package/src/knowledge/audit.ts +67 -71
  481. package/src/knowledge/flywheel-metrics.ts +71 -0
  482. package/src/knowledge/import.ts +4 -18
  483. package/src/knowledge/index-generator.ts +19 -14
  484. package/src/knowledge/ingest.ts +9 -4
  485. package/src/knowledge/migration.ts +11 -8
  486. package/src/knowledge/reference-tracker.ts +12 -8
  487. package/src/knowledge/store.ts +64 -11
  488. package/src/monitoring/CONTEXT.md +8 -3
  489. package/src/monitoring/__tests__/skipped-lines-report.test.ts +178 -0
  490. package/src/monitoring/__tests__/traces.test.ts +57 -0
  491. package/src/monitoring/context-tracker.ts +11 -23
  492. package/src/monitoring/index.ts +1 -1
  493. package/src/monitoring/trace-analyzer.ts +21 -2
  494. package/src/monitoring/traces.ts +39 -36
  495. package/src/release/CONTEXT.md +1 -0
  496. package/src/release/__tests__/barrel.test.ts +60 -0
  497. package/src/release/index.ts +2 -13
  498. package/src/sdd/__tests__/index-generator.test.ts +71 -0
  499. package/src/sdd/index-generator.ts +9 -9
  500. package/src/test-setup/__tests__/project-fixture.test.ts +178 -0
  501. package/src/test-setup/project-fixture.ts +87 -0
  502. package/src/tools/CONTEXT.md +6 -0
  503. package/src/tools/__tests__/registry.test.ts +221 -0
  504. package/src/types/__tests__/types.test.ts +4 -5
  505. package/src/types/constraint.ts +16 -27
  506. package/src/types/passes-gate.ts +10 -30
  507. package/src/types/project-config.ts +1 -1
  508. package/src/types/session.ts +5 -5
  509. package/src/types/trace.ts +10 -0
  510. package/src/utils/__tests__/frontmatter.test.ts +206 -0
  511. package/src/utils/__tests__/jsonl-skip-disposition.test.ts +175 -0
  512. package/src/utils/__tests__/jsonl.test.ts +187 -0
  513. package/src/utils/__tests__/package-version.test.ts +97 -0
  514. package/src/utils/exec.ts +15 -0
  515. package/src/utils/file-walk.ts +11 -2
  516. package/src/utils/frontmatter.ts +121 -0
  517. package/src/utils/jsonl.ts +167 -0
  518. package/src/utils/package-version.ts +27 -0
  519. package/dist/cli/commands/doc-freshness-check.d.ts +0 -53
  520. package/dist/cli/commands/doc-freshness-check.d.ts.map +0 -1
  521. package/dist/cli/commands/doc-freshness-check.js +0 -352
  522. package/dist/cli/commands/doc-freshness-check.js.map +0 -1
  523. package/dist/types/index.d.ts +0 -14
  524. package/dist/types/index.d.ts.map +0 -1
  525. package/dist/types/index.js +0 -39
  526. package/dist/types/index.js.map +0 -1
  527. package/src/__tests__/long-running.test.ts +0 -17
  528. package/src/__tests__/passes-gate-extension.test.ts +0 -151
  529. package/src/cli/commands/__tests__/doc-freshness-check.test.ts +0 -168
  530. package/src/cli/commands/doc-freshness-check.ts +0 -386
  531. package/src/spec/CONTEXT.md +0 -11
  532. package/src/types/index.ts +0 -47
@@ -0,0 +1,175 @@
1
+ /**
2
+ * skip 读点的「计数去向」守护(harness#100)
3
+ *
4
+ * 契约原则(正本见 `src/monitoring/CONTEXT.md` 与 `src/cli/commands/CONTEXT.md`「约定」段):
5
+ * **凡以 `skip` 策略读 JSONL,坏行计数必须到达该消费面的用户可见输出,或在该调用点
6
+ * 显式记名豁免并写明理由。** 静默吞掉「数据不全」不是一种可选项。
7
+ *
8
+ * 本文件把这句原则变成机器可检面,三道闸:
9
+ * 1. 每个 `readJsonl` 调用点都判得出坏行策略——判不出(策略走变量、参数形状超出识别窗口)
10
+ * 即形状逃逸,失败并交人裁决,不给静默漏判留口子
11
+ * 2. skip 读点集合冻结(`EXPECTED_SKIP_READ_SITES`):新增读点即失败;
12
+ * 读点消失却不删条目也失败(豁免不烂成化石,与 #95 同一手法)
13
+ * 3. 每个读点上方 6 行内必须有 `计数去向:` 声明(透传到哪儿 / 为何豁免),且声明与本读点
14
+ * 之间不得夹着另一个读点——邻居的声明不顶替,缺失即失败
15
+ *
16
+ * `throw` 策略不产计数,故不要求去向声明;但新增 throw 调用点必须在下文那条断言里逐个点名
17
+ * 裁决——否则「改成 throw 就免掉告知」成了原则的绕行通道。`src/utils/jsonl.ts` 本身是策略
18
+ * 定义处,排除。
19
+ * `readJsonlEnds`(首尾读取)同样是 skip 读点,纳入同一识别正则——否则它成为本契约的
20
+ * 绕行通道(harness#114)。
21
+ * `countJsonlLines` 的纯行数站点(如 `check` 的提示计数)不 parse、不产坏行计数,也不在本表内
22
+ * ——它把损坏行算进条数的问题属口径变更,#100 明确不做。
23
+ */
24
+
25
+ import * as fs from 'fs';
26
+ import * as path from 'path';
27
+
28
+ const SRC_ROOT = path.join(__dirname, '..', '..');
29
+
30
+ /** 读点上方声明计数去向的标记(写在坏行策略注释里,与调用点同一处) */
31
+ const DISPOSITION_MARKER = /计数去向[::]/;
32
+
33
+ /** 声明与读点的最大距离(行数):只认紧贴读点的那段注释 */
34
+ const DECLARATION_LOOKBACK_LINES = 6;
35
+
36
+ /** 判定策略时从读点行向下拼几行(覆盖参数换行的调用形状) */
37
+ const POLICY_WINDOW_LINES = 6;
38
+
39
+ /**
40
+ * 冻结的 skip 读点集合:文件(仓库相对路径)→ 读点数。
41
+ * 每个读点的去向理由在调用点注释里,此表只钉「有哪些点、各几处」。
42
+ */
43
+ const EXPECTED_SKIP_READ_SITES: Record<string, number> = {
44
+ 'src/cli/commands/status.ts': 1,
45
+ 'src/cli/session-mining/transcript.ts': 1,
46
+ 'src/context/session-manager.ts': 2,
47
+ 'src/core/constraints/context-builder.ts': 2,
48
+ 'src/core/constraints/usage-report.ts': 1,
49
+ 'src/failure/recorder.ts': 1,
50
+ 'src/knowledge/reference-tracker.ts': 1,
51
+ 'src/monitoring/context-tracker.ts': 1,
52
+ 'src/monitoring/traces.ts': 2,
53
+ };
54
+
55
+ function listTsFiles(dir: string): string[] {
56
+ const found: string[] = [];
57
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
58
+ const full = path.join(dir, entry.name);
59
+ if (entry.isDirectory()) {
60
+ if (entry.name === '__tests__') continue;
61
+ found.push(...listTsFiles(full));
62
+ } else if (entry.name.endsWith('.ts') && !entry.name.endsWith('.d.ts')) {
63
+ found.push(full);
64
+ }
65
+ }
66
+ return found.sort();
67
+ }
68
+
69
+ /** 仓库相对路径(跨机器稳定) */
70
+ function repoPath(abs: string): string {
71
+ return path.relative(path.join(SRC_ROOT, '..'), abs).split(path.sep).join('/');
72
+ }
73
+
74
+ interface CallSite {
75
+ /** 1-based 行号 */
76
+ line: number;
77
+ snippet: string;
78
+ /** 策略判不出 = unknown(形状超出本守卫的识别能力,必须由人裁决而非静默漏判) */
79
+ policy: 'skip' | 'throw' | 'unknown';
80
+ }
81
+
82
+ /**
83
+ * 找出一个文件里所有 readJsonl 调用点并判出坏行策略。
84
+ * 调用参数可能换行,故从 `readJsonl` 起拼若干行再判:先遇到 `'skip'` 记 skip、
85
+ * 先遇到 `'throw'` 记 throw,两者都不出现记 unknown。
86
+ */
87
+ function callSites(file: string): CallSite[] {
88
+ const raw = fs.readFileSync(file, 'utf-8').split('\n');
89
+ const sites: CallSite[] = [];
90
+
91
+ for (let i = 0; i < raw.length; i++) {
92
+ const trimmed = raw[i].trim();
93
+ if (!/readJsonl(?:Ends)?\s*[<(]/.test(trimmed)) continue;
94
+ // 注释行里的示例不是站点(策略定义处 src/utils/jsonl.ts 已整体排除)
95
+ if (trimmed.startsWith('//') || trimmed.startsWith('*') || trimmed.startsWith('/*')) continue;
96
+
97
+ const joined = raw.slice(i, i + POLICY_WINDOW_LINES).join(' ');
98
+ const skipIdx = joined.indexOf("'skip'");
99
+ const throwIdx = joined.indexOf("'throw'");
100
+ const policy: CallSite['policy'] =
101
+ skipIdx === -1 && throwIdx === -1
102
+ ? 'unknown'
103
+ : throwIdx !== -1 && (skipIdx === -1 || throwIdx < skipIdx)
104
+ ? 'throw'
105
+ : 'skip';
106
+
107
+ sites.push({ line: i + 1, snippet: trimmed, policy });
108
+ }
109
+ return sites;
110
+ }
111
+
112
+ describe('skip 读点的计数去向契约(harness#100)', () => {
113
+ const sourceFiles = listTsFiles(SRC_ROOT).filter(
114
+ f => repoPath(f) !== 'src/utils/jsonl.ts'
115
+ );
116
+ const sitesByFile = new Map<string, CallSite[]>();
117
+ for (const file of sourceFiles) {
118
+ const sites = callSites(file);
119
+ if (sites.length > 0) sitesByFile.set(repoPath(file), sites);
120
+ }
121
+
122
+ it('每个 readJsonl 调用点都判得出策略(判不出即形状逃逸,需人来补识别规则)', () => {
123
+ const unknown = [...sitesByFile.entries()].flatMap(([rel, sites]) =>
124
+ sites.filter(s => s.policy === 'unknown').map(s => `${rel}:${s.line} ${s.snippet}`)
125
+ );
126
+
127
+ expect(unknown).toEqual([]);
128
+ });
129
+
130
+ it('skip 读点集合 = 冻结表(新增即失败;站点消失却不删条目也失败)', () => {
131
+ const expected = Object.entries(EXPECTED_SKIP_READ_SITES)
132
+ .filter(([, count]) => count > 0)
133
+ .sort(([a], [b]) => a.localeCompare(b));
134
+ const observed = [...sitesByFile.entries()]
135
+ .map(([rel, sites]) => [rel, sites.filter(s => s.policy === 'skip').length] as [string, number])
136
+ .filter(([, count]) => count > 0)
137
+ .sort(([a], [b]) => a.localeCompare(b));
138
+
139
+ expect(observed).toEqual(expected);
140
+ });
141
+
142
+ it('每个 skip 读点都记名声明计数去向(透传目标或豁免理由)', () => {
143
+ const undeclared: string[] = [];
144
+
145
+ for (const [rel, sites] of sitesByFile) {
146
+ const raw = fs.readFileSync(path.join(SRC_ROOT, '..', rel), 'utf-8').split('\n');
147
+ for (const site of sites.filter(s => s.policy === 'skip')) {
148
+ const windowStart = Math.max(0, site.line - 1 - DECLARATION_LOOKBACK_LINES);
149
+ // 邻居读点的声明不得顶替:声明之后到本读点之间不能再有其它 readJsonl 调用点
150
+ const prevSiteLine = sites
151
+ .filter(s => s.line < site.line)
152
+ .map(s => s.line)
153
+ .reduce((max, line) => Math.max(max, line), 0);
154
+ const searchFrom = Math.max(windowStart, prevSiteLine);
155
+ const above = raw.slice(searchFrom, site.line - 1).join('\n');
156
+
157
+ if (!DISPOSITION_MARKER.test(above)) {
158
+ undeclared.push(`${rel}:${site.line} ${site.snippet}`);
159
+ }
160
+ }
161
+ }
162
+
163
+ expect(undeclared).toEqual([]);
164
+ });
165
+
166
+ it('策略为 throw 的调用点不产计数,目前为零(出现即需逐个裁决)', () => {
167
+ const throwSites = [...sitesByFile.entries()].flatMap(([rel, sites]) =>
168
+ sites.filter(s => s.policy === 'throw').map(s => `${rel}:${s.line}`)
169
+ );
170
+
171
+ // 'throw' 是 #82 裁决 1 保留的显式选项,但全仓当前无调用点;
172
+ // 新增时必须在此逐个点名裁决,不允许悄悄多出一条「不需要告知」的策略
173
+ expect(throwSites).toEqual([]);
174
+ });
175
+ });
@@ -0,0 +1,187 @@
1
+ /**
2
+ * JSONL 读写正本测试(harness#82)
3
+ */
4
+
5
+ import * as fs from 'fs';
6
+ import * as os from 'os';
7
+ import * as path from 'path';
8
+ import { readJsonl, countJsonlLines, readJsonlEnds, appendJsonl } from '../jsonl';
9
+
10
+ interface Row {
11
+ n: number;
12
+ }
13
+
14
+ const CORRUPT = '{"n":2,"broken';
15
+
16
+ describe('readJsonl', () => {
17
+ let dir: string;
18
+
19
+ beforeEach(() => {
20
+ dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-jsonl-'));
21
+ });
22
+
23
+ afterEach(() => {
24
+ fs.rmSync(dir, { recursive: true, force: true });
25
+ });
26
+
27
+ function write(name: string, lines: string[]): string {
28
+ const filePath = path.join(dir, name);
29
+ fs.writeFileSync(filePath, lines.join('\n') + '\n', 'utf-8');
30
+ return filePath;
31
+ }
32
+
33
+ it('按文件顺序解析全部合法行', () => {
34
+ const filePath = write('ok.jsonl', ['{"n":1}', '{"n":2}', '{"n":3}']);
35
+ const { records, skippedLines } = readJsonl<Row>(filePath, 'skip');
36
+ expect(records.map(r => r.n)).toEqual([1, 2, 3]);
37
+ expect(skippedLines).toBe(0);
38
+ });
39
+
40
+ it('缺文件返回空结果,不抛', () => {
41
+ expect(readJsonl<Row>(path.join(dir, 'missing.jsonl'), 'skip')).toEqual({
42
+ records: [],
43
+ skippedLines: 0,
44
+ });
45
+ });
46
+
47
+ it('空文件与纯空白行返回空结果', () => {
48
+ const empty = write('empty.jsonl', []);
49
+ const blank = write('blank.jsonl', ['', ' ', '\t']);
50
+ expect(readJsonl<Row>(empty, 'skip').records).toEqual([]);
51
+ expect(readJsonl<Row>(blank, 'skip').records).toEqual([]);
52
+ });
53
+
54
+ it("policy 'skip':坏行计数、合法行照常返回", () => {
55
+ const filePath = write('dirty.jsonl', ['{"n":1}', CORRUPT, '', '{"n":3}']);
56
+ const { records, skippedLines } = readJsonl<Row>(filePath, 'skip');
57
+ expect(records.map(r => r.n)).toEqual([1, 3]);
58
+ expect(skippedLines).toBe(1);
59
+ });
60
+
61
+ it("policy 'throw':坏行上抛", () => {
62
+ const filePath = write('dirty.jsonl', ['{"n":1}', CORRUPT]);
63
+ expect(() => readJsonl<Row>(filePath, 'throw')).toThrow(SyntaxError);
64
+ });
65
+
66
+ it('读入口无策略缺省值:不传 policy 编译期即失败(裁决 1,@ts-expect-error 固定)', () => {
67
+ // @ts-expect-error policy 为显式必填参数,缺省必须在编译期失败
68
+ const result = readJsonl<Row>(path.join(dir, 'unused.jsonl'));
69
+ expect(result.records).toEqual([]);
70
+ });
71
+
72
+ it('tail:只解析最后 N 个非空行', () => {
73
+ const filePath = write('tail.jsonl', ['{"n":1}', '{"n":2}', CORRUPT, '{"n":4}']);
74
+ const { records, skippedLines } = readJsonl<Row>(filePath, 'skip', { tail: 2 });
75
+ expect(records.map(r => r.n)).toEqual([4]);
76
+ expect(skippedLines).toBe(1);
77
+ });
78
+
79
+ it('head:只解析前 N 个非空行', () => {
80
+ const filePath = write('head.jsonl', ['{"n":1}', CORRUPT, '{"n":3}', '{"n":4}']);
81
+ const { records, skippedLines } = readJsonl<Row>(filePath, 'skip', { head: 2 });
82
+ expect(records.map(r => r.n)).toEqual([1]);
83
+ expect(skippedLines).toBe(1);
84
+ });
85
+
86
+ it('tail/head 超过总行数时取全部', () => {
87
+ const filePath = write('small.jsonl', ['{"n":1}']);
88
+ expect(readJsonl<Row>(filePath, 'skip', { tail: 10 }).records).toHaveLength(1);
89
+ expect(readJsonl<Row>(filePath, 'skip', { head: 10 }).records).toHaveLength(1);
90
+ });
91
+ });
92
+
93
+ describe('countJsonlLines', () => {
94
+ let dir: string;
95
+
96
+ beforeEach(() => {
97
+ dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-jsonl-'));
98
+ });
99
+
100
+ afterEach(() => {
101
+ fs.rmSync(dir, { recursive: true, force: true });
102
+ });
103
+
104
+ it('计非空行数(含坏行),缺文件为 0', () => {
105
+ const filePath = path.join(dir, 'c.jsonl');
106
+ fs.writeFileSync(filePath, ['{"n":1}', CORRUPT, '', ' '].join('\n') + '\n', 'utf-8');
107
+ expect(countJsonlLines(filePath)).toBe(2);
108
+ expect(countJsonlLines(path.join(dir, 'missing.jsonl'))).toBe(0);
109
+ });
110
+ });
111
+
112
+ describe('readJsonlEnds', () => {
113
+ let dir: string;
114
+
115
+ beforeEach(() => {
116
+ dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-jsonl-'));
117
+ });
118
+
119
+ afterEach(() => {
120
+ fs.rmSync(dir, { recursive: true, force: true });
121
+ });
122
+
123
+ function write(name: string, lines: string[]): string {
124
+ const filePath = path.join(dir, name);
125
+ fs.writeFileSync(filePath, lines.join('\n') + '\n', 'utf-8');
126
+ return filePath;
127
+ }
128
+
129
+ it('取首/末条合法记录 + 原始行数(含坏行)', () => {
130
+ const filePath = write('ends.jsonl', ['{"n":1}', CORRUPT, '{"n":3}']);
131
+ expect(readJsonlEnds<Row>(filePath, 'skip')).toEqual({
132
+ totalLines: 3,
133
+ first: { n: 1 },
134
+ last: { n: 3 },
135
+ });
136
+ });
137
+
138
+ it('首/末行坏时跳过坏行取最近的合法记录', () => {
139
+ const filePath = write('dirty-ends.jsonl', [CORRUPT, '{"n":2}', '{"n":3}', CORRUPT]);
140
+ const result = readJsonlEnds<Row>(filePath, 'skip');
141
+ expect(result.first).toEqual({ n: 2 });
142
+ expect(result.last).toEqual({ n: 3 });
143
+ expect(result.totalLines).toBe(4);
144
+ });
145
+
146
+ it("policy 'throw':首端遇坏行上抛", () => {
147
+ const filePath = write('throw.jsonl', [CORRUPT, '{"n":2}']);
148
+ expect(() => readJsonlEnds<Row>(filePath, 'throw')).toThrow();
149
+ });
150
+
151
+ it('缺文件 / 全坏行返回无首末记录', () => {
152
+ expect(readJsonlEnds<Row>(path.join(dir, 'missing.jsonl'), 'skip')).toEqual({ totalLines: 0 });
153
+
154
+ const filePath = write('all-bad.jsonl', [CORRUPT, CORRUPT]);
155
+ expect(readJsonlEnds<Row>(filePath, 'skip')).toEqual({ totalLines: 2 });
156
+ });
157
+ });
158
+
159
+ describe('appendJsonl', () => {
160
+ let dir: string;
161
+
162
+ beforeEach(() => {
163
+ dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-jsonl-'));
164
+ });
165
+
166
+ afterEach(() => {
167
+ fs.rmSync(dir, { recursive: true, force: true });
168
+ });
169
+
170
+ it('目录不存在时递归创建并追加单行 JSON', () => {
171
+ const filePath = path.join(dir, 'a', 'b', 'log.jsonl');
172
+ appendJsonl(filePath, { n: 1 });
173
+ appendJsonl(filePath, { n: 2 });
174
+
175
+ expect(countJsonlLines(filePath)).toBe(2);
176
+ const lines = fs.readFileSync(filePath, 'utf-8').split('\n');
177
+ expect(lines[0]).toBe('{"n":1}');
178
+ expect(lines[1]).toBe('{"n":2}');
179
+ expect(lines[2]).toBe('');
180
+ });
181
+
182
+ it('与 readJsonl 构成读写闭环', () => {
183
+ const filePath = path.join(dir, 'roundtrip.jsonl');
184
+ appendJsonl(filePath, { n: 7 });
185
+ expect(readJsonl<Row>(filePath, 'skip').records).toEqual([{ n: 7 }]);
186
+ });
187
+ });
@@ -0,0 +1,97 @@
1
+ /**
2
+ * getHarnessPackageVersion 正本测试(#102,架构评审 A3)
3
+ *
4
+ * 钉住三件事:
5
+ * 1. 读的是 harness 包自身的 package.json(与真实版本一致)
6
+ * 2. cwd 兜底已删除:cwd 是装着 harness 的消费者项目时,绝不误读消费者版本
7
+ * 3. 仓内不再有第二份「harness 自身 package.json」版本读取(防回潮扫描)
8
+ */
9
+
10
+ import * as fs from 'fs';
11
+ import * as path from 'path';
12
+ import { getHarnessPackageVersion } from '../package-version';
13
+
14
+ const REAL_VERSION = JSON.parse(
15
+ fs.readFileSync(path.join(__dirname, '..', '..', '..', 'package.json'), 'utf-8')
16
+ ).version as string;
17
+
18
+ describe('getHarnessPackageVersion', () => {
19
+ it('返回 harness 包真实版本(与包根 package.json 一致)', () => {
20
+ expect(getHarnessPackageVersion()).toBe(REAL_VERSION);
21
+ });
22
+
23
+ it('cwd 是消费者项目时仍读 harness 版本,不误读消费者 package.json', () => {
24
+ const consumerDir = path.join(process.cwd(), 'temp-test-pkg-version-consumer');
25
+ fs.mkdirSync(consumerDir, { recursive: true });
26
+ fs.writeFileSync(
27
+ path.join(consumerDir, 'package.json'),
28
+ JSON.stringify({ name: 'consumer-app', version: '9.9.9' })
29
+ );
30
+ const prevCwd = process.cwd();
31
+ try {
32
+ process.chdir(consumerDir);
33
+ expect(getHarnessPackageVersion()).toBe(REAL_VERSION);
34
+ } finally {
35
+ process.chdir(prevCwd);
36
+ fs.rmSync(consumerDir, { recursive: true, force: true });
37
+ }
38
+ });
39
+
40
+ it('package.json 读取失败(缺失/损坏)时返回 unknown', () => {
41
+ jest.isolateModules(() => {
42
+ jest.doMock('fs', () => ({
43
+ ...jest.requireActual('fs'),
44
+ readFileSync: () => { throw new Error('ENOENT'); },
45
+ }));
46
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
47
+ const fresh = require('../package-version') as typeof import('../package-version');
48
+ expect(fresh.getHarnessPackageVersion()).toBe('unknown');
49
+ jest.dontMock('fs');
50
+ });
51
+ });
52
+
53
+ it('package.json 无 version 字段时返回 unknown', () => {
54
+ jest.isolateModules(() => {
55
+ jest.doMock('fs', () => ({
56
+ ...jest.requireActual('fs'),
57
+ readFileSync: () => JSON.stringify({ name: '@dommaker/harness' }),
58
+ }));
59
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
60
+ const fresh = require('../package-version') as typeof import('../package-version');
61
+ expect(fresh.getHarnessPackageVersion()).toBe('unknown');
62
+ jest.dontMock('fs');
63
+ });
64
+ });
65
+ });
66
+
67
+ describe('版本读取正本收口(防回潮扫描)', () => {
68
+ function collectTsFiles(dir: string): string[] {
69
+ const out: string[] = [];
70
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
71
+ const full = path.join(dir, entry.name);
72
+ if (entry.isDirectory()) {
73
+ if (entry.name === '__tests__') continue;
74
+ out.push(...collectTsFiles(full));
75
+ } else if (entry.name.endsWith('.ts')) {
76
+ out.push(full);
77
+ }
78
+ }
79
+ return out;
80
+ }
81
+
82
+ it('src/ 内只有 utils/package-version.ts 读取 harness 自身 package.json', () => {
83
+ const srcRoot = path.join(__dirname, '..', '..');
84
+ const canonical = path.join(srcRoot, 'utils', 'package-version.ts');
85
+ // harness 自身清单的三类历史读法:__dirname 相对 / require / cwd 兜底
86
+ const selfPkgReadRe = /(__dirname[^\n]*package\.json|require\(['"][^'"]*package\.json['"]\)|process\.cwd\(\)[^\n]*package\.json)/;
87
+ const offenders: string[] = [];
88
+ for (const file of collectTsFiles(srcRoot)) {
89
+ if (file === canonical) continue;
90
+ const hits = fs.readFileSync(file, 'utf-8')
91
+ .split('\n')
92
+ .filter(line => selfPkgReadRe.test(line));
93
+ if (hits.length > 0) offenders.push(`${file}: ${hits.join(' | ')}`);
94
+ }
95
+ expect(offenders).toEqual([]);
96
+ });
97
+ });
package/src/utils/exec.ts CHANGED
@@ -53,6 +53,21 @@ export function normalizeTriggers<T>(
53
53
  return Array.isArray(value) ? value : [value];
54
54
  }
55
55
 
56
+ /**
57
+ * 判断约束是否匹配当前操作集合(#105:trigger 匹配语义单一来源)
58
+ *
59
+ * 语义 = normalizeTriggers(undefined → [] 不匹配任何操作)+ 交集判定,
60
+ * 任一 operation 命中任一 trigger 即匹配。checker 与 prompt 渲染共用本函数,
61
+ * 禁止就地重写。
62
+ */
63
+ export function matchesTrigger<T>(
64
+ constraint: { trigger: T | T[] | undefined },
65
+ operations: T[]
66
+ ): boolean {
67
+ const triggers = normalizeTriggers(constraint.trigger);
68
+ return operations.some(op => triggers.includes(op));
69
+ }
70
+
56
71
  /**
57
72
  * 异步延迟
58
73
  */
@@ -50,12 +50,21 @@ export function walkFiles(root: string, options: FileWalkOptions = {}): string[]
50
50
  return results;
51
51
  }
52
52
 
53
+ /**
54
+ * 目录遍历默认跳过名单:依赖 / 旁测目录 / 构建产物。
55
+ *
56
+ * 遍历跳过口径的正本——chain 式判断(sync-docs 的 readdir 循环)与
57
+ * findTsSourceFiles 的 skipDirs 默认值同源,禁止再各处手抄三段 `===`。
58
+ * 各调用方自有的更广名单(如 AGENTS.md 目录表跳过 build/out/coverage)不并入。
59
+ */
60
+ export const DEFAULT_SKIP_DIRS = ['node_modules', '__tests__', 'dist'];
61
+
53
62
  export interface TsSourceWalkOptions {
54
63
  /** 是否跳过 barrel 文件 index.ts,默认 false */
55
64
  skipIndex?: boolean;
56
65
  /** 是否同时收集 .tsx 文件,默认 false(只收集 .ts) */
57
66
  includeTsx?: boolean;
58
- /** 跳过的目录名,默认 node_modules/__tests__/dist */
67
+ /** 跳过的目录名,默认 DEFAULT_SKIP_DIRS */
59
68
  skipDirs?: string[];
60
69
  /** 是否跳过 `.` 开头的条目,默认 false */
61
70
  skipHidden?: boolean;
@@ -86,7 +95,7 @@ export function findTsSourceFiles(root: string, options: TsSourceWalkOptions = {
86
95
  const {
87
96
  skipIndex = false,
88
97
  includeTsx = false,
89
- skipDirs = ['node_modules', '__tests__', 'dist'],
98
+ skipDirs = DEFAULT_SKIP_DIRS,
90
99
  skipHidden = false,
91
100
  } = options;
92
101
 
@@ -0,0 +1,121 @@
1
+ /**
2
+ * markdown frontmatter 解析/序列化正本(harness#89,架构评审 2026-09-02 候选10)
3
+ *
4
+ * `---\n<yaml>\n---\n<body>` 这一种语法此前在 knowledge 域 5 个文件里手写了 7 遍,
5
+ * 失败语义三派并存(store 静默丢条目 / migration 记 errors / index-generator 走
6
+ * best-effort / sdd 不要求闭合行完整 / ingest 只 strip 会把正文 `---` 误吞)。
7
+ * 语法与失败走法在此一处定义,仓内不允许存在第二份副本。
8
+ *
9
+ * 语法(唯一解释):
10
+ * - 开界:偏移 0 处必须正好是一行 `---`(前面有空白行则不算 frontmatter)
11
+ * - 闭界:其后第一个**独立成行**的 `---`(`---x`、`----`、缩进 `---` 均不作闭合)
12
+ * - body:闭合行之后全部内容,紧随的一个空行被吃掉(与 join 的 `---\n\n` 包裹严格对称,
13
+ * 保证已落盘条目 load→save 字节稳定)
14
+ * - meta:闭合前的行经 YAML 解析,必须是 mapping
15
+ * - CRLF 文件不按 frontmatter 处理(与收口前各消费方一致,本 module 不新增解释)
16
+ *
17
+ * 三种畸形输入的走法(裁决 2,用例固定在 utils/__tests__/frontmatter.test.ts):
18
+ * - 缺 frontmatter(无开界)→ 'absent'
19
+ * - 空 meta(闭合前只有空白,含零宽块)→ 'absent'——零宽元数据块不承载信息,
20
+ * 与「没有 frontmatter」同走一支,消费方按无元数据路径处理(store 视为非条目、
21
+ * index-generator 走 best-effort),无需上报
22
+ * - 未闭合(有开无闭 / 闭合行不完整)→ 'malformed' + reason 'unterminated'
23
+ * - meta 不是合法 YAML mapping(语法错、显式 null、标量或数组)→ 'malformed' + reason 'invalid-yaml'
24
+ *
25
+ * 消费方统一口径(裁决 2:不允许两派并存):'malformed' 两支必须**显式上报**,
26
+ * 禁止静默丢数据;恢复动作按消费方语义选择并在代码里写明——
27
+ * 有结构化报告面的写报告(migration → result.errors),
28
+ * 纯扫描型的打一行 stderr(前缀 `[harness]`,与 failure/recorder.ts 的坏行告警同构)
29
+ * 后跳过或走 best-effort。'absent' 是合法输入,不上报。
30
+ *
31
+ * 序列化(裁决 3:收格式不收字段排序策略):join 只负责包裹与 YAML dump 参数
32
+ * (lineWidth 120,与收口前 store/migration 逐字一致);canonical 字段序是 store 的
33
+ * 私有策略,留在 store.toFrontmatter。
34
+ *
35
+ * 机械核查(期望命中数,均已实测):
36
+ * grep -rnE '/\^---' src --include='*.ts' | grep -v __tests__
37
+ * → 期望 0 处:手写开界正则归零
38
+ * grep -rn "'---'" src --include='*.ts' | grep -v __tests__
39
+ * → 期望 1 处:本文件 DELIMITER
40
+ * grep -rn "yaml.load(metaText)\|yaml.dump(meta," src --include='*.ts' | grep -v __tests__
41
+ * → 期望 2 处:本文件 split 的 load + join 的 dump
42
+ * 豁免清单(不属 frontmatter,不改):markdown 生成物里的 `---` 分隔线与表格分隔行
43
+ * (init / sync-docs / report / constraints-report 模板)、doc-freshness 的章节定位正则
44
+ * (`\n---` 是章节边界)、config.yml 与 checkpoint/tasks/progress 的 yaml.dump(单 YAML/JSON 文档)。
45
+ */
46
+
47
+ import * as yaml from 'js-yaml';
48
+
49
+ /** frontmatter 界符(整行) */
50
+ const DELIMITER = '---';
51
+
52
+ /** 与收口前 store/migration 逐字一致的 YAML dump 行宽 */
53
+ const DUMP_LINE_WIDTH = 120;
54
+
55
+ /** 'malformed' 的两支:消费方必须逐支显式处理(口径见文件头) */
56
+ export type FrontmatterMalformedReason = 'unterminated' | 'invalid-yaml';
57
+
58
+ /** frontmatter 元数据(已由 split 保证是 YAML mapping) */
59
+ export type FrontmatterMeta = Record<string, unknown>;
60
+
61
+ export type FrontmatterSplit =
62
+ | { state: 'ok'; meta: FrontmatterMeta; body: string }
63
+ | { state: 'absent' }
64
+ | { state: 'malformed'; reason: FrontmatterMalformedReason; detail: string };
65
+
66
+ /**
67
+ * 解析 markdown 的 YAML frontmatter。
68
+ *
69
+ * 不抛:三种畸形输入与 YAML 非法都体现在返回值 state/reason 上,由消费方按口径处置。
70
+ */
71
+ export function splitFrontmatter(raw: string): FrontmatterSplit {
72
+ const lines = raw.split('\n');
73
+ if (lines[0] !== DELIMITER) return { state: 'absent' };
74
+
75
+ const closeIndex = lines.indexOf(DELIMITER, 1);
76
+ if (closeIndex === -1) {
77
+ return {
78
+ state: 'malformed',
79
+ reason: 'unterminated',
80
+ detail: `开界之后缺少独立成行的 ${DELIMITER} 闭合行`,
81
+ };
82
+ }
83
+
84
+ const metaLines = lines.slice(1, closeIndex);
85
+ if (metaLines.every(line => line.trim().length === 0)) return { state: 'absent' };
86
+
87
+ const metaText = metaLines.join('\n');
88
+ let parsed: unknown;
89
+ try {
90
+ parsed = yaml.load(metaText);
91
+ } catch (error) {
92
+ return {
93
+ state: 'malformed',
94
+ reason: 'invalid-yaml',
95
+ // js-yaml 的 message 带多行代码片段,detail 只取首行描述——消费方要嵌进单行报告/stderr
96
+ detail: (error instanceof Error ? error.message : String(error)).split('\n')[0],
97
+ };
98
+ }
99
+
100
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
101
+ return {
102
+ state: 'malformed',
103
+ reason: 'invalid-yaml',
104
+ detail: `frontmatter 必须是 YAML mapping,实际是 ${Array.isArray(parsed) ? 'array' : typeof parsed}`,
105
+ };
106
+ }
107
+
108
+ let bodyStart = closeIndex + 1;
109
+ if (lines[bodyStart] === '') bodyStart++;
110
+
111
+ return { state: 'ok', meta: parsed as FrontmatterMeta, body: lines.slice(bodyStart).join('\n') };
112
+ }
113
+
114
+ /**
115
+ * 序列化为 `---\n<yaml>---\n\n<body>`(包裹格式与收口前 store/migration 逐字一致)。
116
+ *
117
+ * meta 的键序由调用方决定(store 保 canonical 序);本函数只做包裹与 dump 参数。
118
+ */
119
+ export function joinFrontmatter(meta: FrontmatterMeta, body: string): string {
120
+ return `${DELIMITER}\n${yaml.dump(meta, { lineWidth: DUMP_LINE_WIDTH })}${DELIMITER}\n\n${body}`;
121
+ }