@dommaker/harness 1.2.3 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (550) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +0 -2
  3. package/bin/harness.js +86 -32
  4. package/dist/cli/command-contract.d.ts +83 -0
  5. package/dist/cli/command-contract.d.ts.map +1 -0
  6. package/dist/cli/command-contract.js +66 -0
  7. package/dist/cli/command-contract.js.map +1 -0
  8. package/dist/cli/commands/acceptance.d.ts +3 -2
  9. package/dist/cli/commands/acceptance.d.ts.map +1 -1
  10. package/dist/cli/commands/acceptance.js +28 -24
  11. package/dist/cli/commands/acceptance.js.map +1 -1
  12. package/dist/cli/commands/analyze-sessions.d.ts +2 -1
  13. package/dist/cli/commands/analyze-sessions.d.ts.map +1 -1
  14. package/dist/cli/commands/analyze-sessions.js +25 -24
  15. package/dist/cli/commands/analyze-sessions.js.map +1 -1
  16. package/dist/cli/commands/check.d.ts +14 -2
  17. package/dist/cli/commands/check.d.ts.map +1 -1
  18. package/dist/cli/commands/check.js +76 -61
  19. package/dist/cli/commands/check.js.map +1 -1
  20. package/dist/cli/commands/command.d.ts +5 -1
  21. package/dist/cli/commands/command.d.ts.map +1 -1
  22. package/dist/cli/commands/command.js +26 -17
  23. package/dist/cli/commands/command.js.map +1 -1
  24. package/dist/cli/commands/constraints-report.d.ts +2 -1
  25. package/dist/cli/commands/constraints-report.d.ts.map +1 -1
  26. package/dist/cli/commands/constraints-report.js +37 -33
  27. package/dist/cli/commands/constraints-report.js.map +1 -1
  28. package/dist/cli/commands/constraints-retire.d.ts +6 -5
  29. package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
  30. package/dist/cli/commands/constraints-retire.js +50 -52
  31. package/dist/cli/commands/constraints-retire.js.map +1 -1
  32. package/dist/cli/commands/constraints.d.ts +2 -1
  33. package/dist/cli/commands/constraints.d.ts.map +1 -1
  34. package/dist/cli/commands/constraints.js +10 -27
  35. package/dist/cli/commands/constraints.js.map +1 -1
  36. package/dist/cli/commands/contract.d.ts +3 -2
  37. package/dist/cli/commands/contract.d.ts.map +1 -1
  38. package/dist/cli/commands/contract.js +45 -42
  39. package/dist/cli/commands/contract.js.map +1 -1
  40. package/dist/cli/commands/definitions.d.ts +11 -11
  41. package/dist/cli/commands/definitions.d.ts.map +1 -1
  42. package/dist/cli/commands/definitions.js +30 -215
  43. package/dist/cli/commands/definitions.js.map +1 -1
  44. package/dist/cli/commands/failure.d.ts +5 -4
  45. package/dist/cli/commands/failure.d.ts.map +1 -1
  46. package/dist/cli/commands/failure.js +33 -26
  47. package/dist/cli/commands/failure.js.map +1 -1
  48. package/dist/cli/commands/init.d.ts +7 -5
  49. package/dist/cli/commands/init.d.ts.map +1 -1
  50. package/dist/cli/commands/init.js +147 -211
  51. package/dist/cli/commands/init.js.map +1 -1
  52. package/dist/cli/commands/knowledge.d.ts +21 -15
  53. package/dist/cli/commands/knowledge.d.ts.map +1 -1
  54. package/dist/cli/commands/knowledge.js +237 -199
  55. package/dist/cli/commands/knowledge.js.map +1 -1
  56. package/dist/cli/commands/passes-gate.d.ts +8 -2
  57. package/dist/cli/commands/passes-gate.d.ts.map +1 -1
  58. package/dist/cli/commands/passes-gate.js +44 -66
  59. package/dist/cli/commands/passes-gate.js.map +1 -1
  60. package/dist/cli/commands/performance.d.ts +2 -1
  61. package/dist/cli/commands/performance.d.ts.map +1 -1
  62. package/dist/cli/commands/performance.js +20 -18
  63. package/dist/cli/commands/performance.js.map +1 -1
  64. package/dist/cli/commands/posteval-plan.d.ts +5 -1
  65. package/dist/cli/commands/posteval-plan.d.ts.map +1 -1
  66. package/dist/cli/commands/posteval-plan.js +26 -15
  67. package/dist/cli/commands/posteval-plan.js.map +1 -1
  68. package/dist/cli/commands/release.d.ts +6 -1
  69. package/dist/cli/commands/release.d.ts.map +1 -1
  70. package/dist/cli/commands/release.js +73 -77
  71. package/dist/cli/commands/release.js.map +1 -1
  72. package/dist/cli/commands/report.d.ts +2 -1
  73. package/dist/cli/commands/report.d.ts.map +1 -1
  74. package/dist/cli/commands/report.js +9 -5
  75. package/dist/cli/commands/report.js.map +1 -1
  76. package/dist/cli/commands/review.d.ts +3 -2
  77. package/dist/cli/commands/review.d.ts.map +1 -1
  78. package/dist/cli/commands/review.js +33 -29
  79. package/dist/cli/commands/review.js.map +1 -1
  80. package/dist/cli/commands/sdd.d.ts +2 -1
  81. package/dist/cli/commands/sdd.d.ts.map +1 -1
  82. package/dist/cli/commands/sdd.js +7 -5
  83. package/dist/cli/commands/sdd.js.map +1 -1
  84. package/dist/cli/commands/security.d.ts +3 -2
  85. package/dist/cli/commands/security.d.ts.map +1 -1
  86. package/dist/cli/commands/security.js +29 -26
  87. package/dist/cli/commands/security.js.map +1 -1
  88. package/dist/cli/commands/spec-baseline-check.d.ts +2 -1
  89. package/dist/cli/commands/spec-baseline-check.d.ts.map +1 -1
  90. package/dist/cli/commands/spec-baseline-check.js +15 -11
  91. package/dist/cli/commands/spec-baseline-check.js.map +1 -1
  92. package/dist/cli/commands/spec.d.ts +7 -2
  93. package/dist/cli/commands/spec.d.ts.map +1 -1
  94. package/dist/cli/commands/spec.js +61 -51
  95. package/dist/cli/commands/spec.js.map +1 -1
  96. package/dist/cli/commands/status.d.ts +2 -1
  97. package/dist/cli/commands/status.d.ts.map +1 -1
  98. package/dist/cli/commands/status.js +57 -53
  99. package/dist/cli/commands/status.js.map +1 -1
  100. package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
  101. package/dist/cli/commands/sync-docs/agents-syncer.js +31 -16
  102. package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
  103. package/dist/cli/commands/sync-docs/context-syncer.d.ts.map +1 -1
  104. package/dist/cli/commands/sync-docs/context-syncer.js +3 -2
  105. package/dist/cli/commands/sync-docs/context-syncer.js.map +1 -1
  106. package/dist/cli/commands/sync-docs/index.d.ts +2 -1
  107. package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
  108. package/dist/cli/commands/sync-docs/index.js +68 -88
  109. package/dist/cli/commands/sync-docs/index.js.map +1 -1
  110. package/dist/cli/commands/sync-docs/project-reader.d.ts +6 -1
  111. package/dist/cli/commands/sync-docs/project-reader.d.ts.map +1 -1
  112. package/dist/cli/commands/sync-docs/project-reader.js +14 -16
  113. package/dist/cli/commands/sync-docs/project-reader.js.map +1 -1
  114. package/dist/cli/commands/update-user-model.d.ts +2 -1
  115. package/dist/cli/commands/update-user-model.d.ts.map +1 -1
  116. package/dist/cli/commands/update-user-model.js +31 -45
  117. package/dist/cli/commands/update-user-model.js.map +1 -1
  118. package/dist/cli/commands/validate.d.ts +4 -3
  119. package/dist/cli/commands/validate.d.ts.map +1 -1
  120. package/dist/cli/commands/validate.js +28 -26
  121. package/dist/cli/commands/validate.js.map +1 -1
  122. package/dist/cli/session-mining/transcript.d.ts +7 -1
  123. package/dist/cli/session-mining/transcript.d.ts.map +1 -1
  124. package/dist/cli/session-mining/transcript.js +21 -14
  125. package/dist/cli/session-mining/transcript.js.map +1 -1
  126. package/dist/completion-checkers/index.d.ts +3 -3
  127. package/dist/completion-checkers/index.js +3 -3
  128. package/dist/completion-checkers/phase-format.d.ts +1 -1
  129. package/dist/completion-checkers/phase-format.js +2 -2
  130. package/dist/completion-checkers/phase-format.js.map +1 -1
  131. package/dist/completion-checkers/tdd-chain.d.ts +1 -1
  132. package/dist/completion-checkers/tdd-chain.js +2 -2
  133. package/dist/completion-checkers/tdd-chain.js.map +1 -1
  134. package/dist/completion-checkers/types.d.ts +5 -5
  135. package/dist/completion-checkers/types.d.ts.map +1 -1
  136. package/dist/completion-checkers/types.js +2 -2
  137. package/dist/context/session-manager.d.ts.map +1 -1
  138. package/dist/context/session-manager.js +19 -32
  139. package/dist/context/session-manager.js.map +1 -1
  140. package/dist/core/constraints/agent-prompt-renderer.d.ts.map +1 -1
  141. package/dist/core/constraints/agent-prompt-renderer.js +1 -4
  142. package/dist/core/constraints/agent-prompt-renderer.js.map +1 -1
  143. package/dist/core/constraints/capabilities-parser.d.ts +24 -7
  144. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  145. package/dist/core/constraints/capabilities-parser.js +32 -28
  146. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  147. package/dist/core/constraints/capabilities-reconcile.d.ts +67 -0
  148. package/dist/core/constraints/capabilities-reconcile.d.ts.map +1 -0
  149. package/dist/core/constraints/capabilities-reconcile.js +105 -0
  150. package/dist/core/constraints/capabilities-reconcile.js.map +1 -0
  151. package/dist/core/constraints/checker.d.ts +21 -43
  152. package/dist/core/constraints/checker.d.ts.map +1 -1
  153. package/dist/core/constraints/checker.js +64 -115
  154. package/dist/core/constraints/checker.js.map +1 -1
  155. package/dist/core/constraints/checkers/capability-sync.d.ts +4 -3
  156. package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
  157. package/dist/core/constraints/checkers/capability-sync.js +29 -69
  158. package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
  159. package/dist/core/constraints/checkers/context-doc-sync.d.ts +4 -2
  160. package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
  161. package/dist/core/constraints/checkers/context-doc-sync.js +13 -22
  162. package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
  163. package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
  164. package/dist/core/constraints/checkers/docs-freshness.js +37 -63
  165. package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
  166. package/dist/core/constraints/checkers/governance-presence.d.ts +2 -2
  167. package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
  168. package/dist/core/constraints/checkers/governance-presence.js +7 -28
  169. package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
  170. package/dist/core/constraints/checkers/no-test-simplification.d.ts.map +1 -1
  171. package/dist/core/constraints/checkers/no-test-simplification.js +4 -3
  172. package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
  173. package/dist/core/constraints/checkers/types.d.ts +2 -2
  174. package/dist/core/constraints/context-builder.d.ts +15 -14
  175. package/dist/core/constraints/context-builder.d.ts.map +1 -1
  176. package/dist/core/constraints/context-builder.js +32 -96
  177. package/dist/core/constraints/context-builder.js.map +1 -1
  178. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  179. package/dist/core/constraints/doc-freshness/runner.js +2 -1
  180. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  181. package/dist/core/constraints/git-evidence.d.ts +60 -0
  182. package/dist/core/constraints/git-evidence.d.ts.map +1 -0
  183. package/dist/core/constraints/git-evidence.js +88 -0
  184. package/dist/core/constraints/git-evidence.js.map +1 -0
  185. package/dist/core/constraints/injection-drift.d.ts +3 -20
  186. package/dist/core/constraints/injection-drift.d.ts.map +1 -1
  187. package/dist/core/constraints/injection-drift.js +10 -57
  188. package/dist/core/constraints/injection-drift.js.map +1 -1
  189. package/dist/core/constraints/injection-writer.d.ts +113 -0
  190. package/dist/core/constraints/injection-writer.d.ts.map +1 -0
  191. package/dist/core/constraints/injection-writer.js +228 -0
  192. package/dist/core/constraints/injection-writer.js.map +1 -0
  193. package/dist/core/constraints/usage-report.d.ts +22 -1
  194. package/dist/core/constraints/usage-report.d.ts.map +1 -1
  195. package/dist/core/constraints/usage-report.js +27 -20
  196. package/dist/core/constraints/usage-report.js.map +1 -1
  197. package/dist/core/index.d.ts +2 -2
  198. package/dist/core/index.d.ts.map +1 -1
  199. package/dist/core/index.js +4 -2
  200. package/dist/core/index.js.map +1 -1
  201. package/dist/core/project-config-loader.d.ts +32 -1
  202. package/dist/core/project-config-loader.d.ts.map +1 -1
  203. package/dist/core/project-config-loader.js +39 -9
  204. package/dist/core/project-config-loader.js.map +1 -1
  205. package/dist/core/validators/passes-gate.d.ts +15 -64
  206. package/dist/core/validators/passes-gate.d.ts.map +1 -1
  207. package/dist/core/validators/passes-gate.js +84 -222
  208. package/dist/core/validators/passes-gate.js.map +1 -1
  209. package/dist/core/validators/test-output.d.ts +58 -0
  210. package/dist/core/validators/test-output.d.ts.map +1 -0
  211. package/dist/core/validators/test-output.js +109 -0
  212. package/dist/core/validators/test-output.js.map +1 -0
  213. package/dist/failure/recorder.d.ts +7 -1
  214. package/dist/failure/recorder.d.ts.map +1 -1
  215. package/dist/failure/recorder.js +13 -9
  216. package/dist/failure/recorder.js.map +1 -1
  217. package/dist/gates/acceptance.d.ts +1 -48
  218. package/dist/gates/acceptance.d.ts.map +1 -1
  219. package/dist/gates/acceptance.js +6 -38
  220. package/dist/gates/acceptance.js.map +1 -1
  221. package/dist/gates/checker-gate.d.ts.map +1 -1
  222. package/dist/gates/checker-gate.js +6 -11
  223. package/dist/gates/checker-gate.js.map +1 -1
  224. package/dist/gates/command.d.ts.map +1 -1
  225. package/dist/gates/command.js +10 -22
  226. package/dist/gates/command.js.map +1 -1
  227. package/dist/gates/contract.d.ts.map +1 -1
  228. package/dist/gates/contract.js +22 -61
  229. package/dist/gates/contract.js.map +1 -1
  230. package/dist/gates/performance.d.ts.map +1 -1
  231. package/dist/gates/performance.js +13 -31
  232. package/dist/gates/performance.js.map +1 -1
  233. package/dist/gates/review.d.ts.map +1 -1
  234. package/dist/gates/review.js +21 -53
  235. package/dist/gates/review.js.map +1 -1
  236. package/dist/gates/security.d.ts.map +1 -1
  237. package/dist/gates/security.js +14 -32
  238. package/dist/gates/security.js.map +1 -1
  239. package/dist/gates/types.d.ts +28 -5
  240. package/dist/gates/types.d.ts.map +1 -1
  241. package/dist/gates/types.js +38 -1
  242. package/dist/gates/types.js.map +1 -1
  243. package/dist/hooks/bootstrap.d.ts +2 -0
  244. package/dist/hooks/bootstrap.d.ts.map +1 -1
  245. package/dist/hooks/bootstrap.js +5 -2
  246. package/dist/hooks/bootstrap.js.map +1 -1
  247. package/dist/index.d.ts +4 -3
  248. package/dist/index.d.ts.map +1 -1
  249. package/dist/index.js +13 -4
  250. package/dist/index.js.map +1 -1
  251. package/dist/knowledge/audit.d.ts +7 -2
  252. package/dist/knowledge/audit.d.ts.map +1 -1
  253. package/dist/knowledge/audit.js +59 -79
  254. package/dist/knowledge/audit.js.map +1 -1
  255. package/dist/knowledge/flywheel-metrics.d.ts +39 -0
  256. package/dist/knowledge/flywheel-metrics.d.ts.map +1 -0
  257. package/dist/knowledge/flywheel-metrics.js +47 -0
  258. package/dist/knowledge/flywheel-metrics.js.map +1 -0
  259. package/dist/knowledge/import.d.ts.map +1 -1
  260. package/dist/knowledge/import.js +4 -14
  261. package/dist/knowledge/import.js.map +1 -1
  262. package/dist/knowledge/index-generator.d.ts.map +1 -1
  263. package/dist/knowledge/index-generator.js +18 -15
  264. package/dist/knowledge/index-generator.js.map +1 -1
  265. package/dist/knowledge/ingest.d.ts.map +1 -1
  266. package/dist/knowledge/ingest.js +9 -4
  267. package/dist/knowledge/ingest.js.map +1 -1
  268. package/dist/knowledge/migration.d.ts.map +1 -1
  269. package/dist/knowledge/migration.js +11 -8
  270. package/dist/knowledge/migration.js.map +1 -1
  271. package/dist/knowledge/reference-tracker.d.ts.map +1 -1
  272. package/dist/knowledge/reference-tracker.js +12 -9
  273. package/dist/knowledge/reference-tracker.js.map +1 -1
  274. package/dist/knowledge/store.d.ts +15 -0
  275. package/dist/knowledge/store.d.ts.map +1 -1
  276. package/dist/knowledge/store.js +59 -10
  277. package/dist/knowledge/store.js.map +1 -1
  278. package/dist/monitoring/context-tracker.d.ts.map +1 -1
  279. package/dist/monitoring/context-tracker.js +11 -20
  280. package/dist/monitoring/context-tracker.js.map +1 -1
  281. package/dist/monitoring/index.d.ts +1 -1
  282. package/dist/monitoring/index.d.ts.map +1 -1
  283. package/dist/monitoring/index.js +1 -2
  284. package/dist/monitoring/index.js.map +1 -1
  285. package/dist/monitoring/trace-analyzer.d.ts +15 -0
  286. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  287. package/dist/monitoring/trace-analyzer.js +19 -2
  288. package/dist/monitoring/trace-analyzer.js.map +1 -1
  289. package/dist/monitoring/traces.d.ts +21 -2
  290. package/dist/monitoring/traces.d.ts.map +1 -1
  291. package/dist/monitoring/traces.js +38 -29
  292. package/dist/monitoring/traces.js.map +1 -1
  293. package/dist/release/index.d.ts +3 -0
  294. package/dist/release/index.d.ts.map +1 -0
  295. package/dist/release/index.js +7 -0
  296. package/dist/release/index.js.map +1 -0
  297. package/dist/release/integrity.d.ts +65 -0
  298. package/dist/release/integrity.d.ts.map +1 -0
  299. package/dist/release/integrity.js +151 -0
  300. package/dist/release/integrity.js.map +1 -0
  301. package/dist/sdd/index-generator.js +10 -11
  302. package/dist/sdd/index-generator.js.map +1 -1
  303. package/dist/test-setup/project-fixture.d.ts +48 -0
  304. package/dist/test-setup/project-fixture.d.ts.map +1 -0
  305. package/dist/test-setup/project-fixture.js +106 -0
  306. package/dist/test-setup/project-fixture.js.map +1 -0
  307. package/dist/types/constraint.d.ts +0 -18
  308. package/dist/types/constraint.d.ts.map +1 -1
  309. package/dist/types/constraint.js.map +1 -1
  310. package/dist/types/failure.d.ts +7 -0
  311. package/dist/types/failure.d.ts.map +1 -1
  312. package/dist/types/failure.js +8 -1
  313. package/dist/types/failure.js.map +1 -1
  314. package/dist/types/passes-gate.d.ts +10 -25
  315. package/dist/types/passes-gate.d.ts.map +1 -1
  316. package/dist/types/project-config.d.ts +1 -1
  317. package/dist/types/session.d.ts +6 -5
  318. package/dist/types/session.d.ts.map +1 -1
  319. package/dist/utils/exec.d.ts +10 -0
  320. package/dist/utils/exec.d.ts.map +1 -1
  321. package/dist/utils/exec.js +12 -0
  322. package/dist/utils/exec.js.map +1 -1
  323. package/dist/utils/file-walk.d.ts +9 -1
  324. package/dist/utils/file-walk.d.ts.map +1 -1
  325. package/dist/utils/file-walk.js +10 -1
  326. package/dist/utils/file-walk.js.map +1 -1
  327. package/dist/utils/frontmatter.d.ts +73 -0
  328. package/dist/utils/frontmatter.d.ts.map +1 -0
  329. package/dist/utils/frontmatter.js +141 -0
  330. package/dist/utils/frontmatter.js.map +1 -0
  331. package/dist/utils/jsonl.d.ts +92 -0
  332. package/dist/utils/jsonl.d.ts.map +1 -0
  333. package/dist/utils/jsonl.js +166 -0
  334. package/dist/utils/jsonl.js.map +1 -0
  335. package/dist/utils/package-version.d.ts +16 -0
  336. package/dist/utils/package-version.d.ts.map +1 -0
  337. package/dist/utils/package-version.js +63 -0
  338. package/dist/utils/package-version.js.map +1 -0
  339. package/package.json +1 -1
  340. package/src/CONTEXT.md +7 -2
  341. package/src/__tests__/acceptance-gate.test.ts +3 -1
  342. package/src/__tests__/checker-extra.test.ts +7 -1218
  343. package/src/__tests__/context-files-resolution.test.ts +116 -0
  344. package/src/__tests__/effective-constraints.test.ts +10 -13
  345. package/src/__tests__/exec.test.ts +35 -1
  346. package/src/__tests__/failure-recorder.test.ts +9 -0
  347. package/src/__tests__/layering.test.ts +152 -0
  348. package/src/__tests__/passes-gate-extra.test.ts +0 -145
  349. package/src/__tests__/passes-gate.test.ts +142 -106
  350. package/src/__tests__/public-exports.test.ts +3 -0
  351. package/src/__tests__/skip-semantics.test.ts +5 -9
  352. package/src/cli/__tests__/command-contract.test.ts +170 -0
  353. package/src/cli/command-contract.ts +108 -0
  354. package/src/cli/commands/CONTEXT.md +22 -7
  355. package/src/cli/commands/__tests__/acceptance.test.ts +40 -33
  356. package/src/cli/commands/__tests__/analyze-sessions.test.ts +35 -29
  357. package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +101 -0
  358. package/src/cli/commands/__tests__/check-drift.test.ts +27 -34
  359. package/src/cli/commands/__tests__/check-skip-output.test.ts +21 -24
  360. package/src/cli/commands/__tests__/check.test.ts +378 -382
  361. package/src/cli/commands/__tests__/command.test.ts +85 -70
  362. package/src/cli/commands/__tests__/constraints-report.test.ts +100 -71
  363. package/src/cli/commands/__tests__/constraints-retire.test.ts +152 -95
  364. package/src/cli/commands/__tests__/contract.test.ts +27 -34
  365. package/src/cli/commands/__tests__/failure.test.ts +31 -34
  366. package/src/cli/commands/__tests__/init-injection.test.ts +58 -28
  367. package/src/cli/commands/__tests__/init.test.ts +77 -48
  368. package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +108 -0
  369. package/src/cli/commands/__tests__/knowledge.test.ts +96 -38
  370. package/src/cli/commands/__tests__/passes-gate.test.ts +43 -45
  371. package/src/cli/commands/__tests__/performance.test.ts +22 -23
  372. package/src/cli/commands/__tests__/posteval-plan.test.ts +39 -38
  373. package/src/cli/commands/__tests__/project-path-anchoring.test.ts +157 -0
  374. package/src/cli/commands/__tests__/project-path-convention.test.ts +169 -0
  375. package/src/cli/commands/__tests__/registry.test.ts +69 -35
  376. package/src/cli/commands/__tests__/release.test.ts +224 -74
  377. package/src/cli/commands/__tests__/report-extra.test.ts +16 -16
  378. package/src/cli/commands/__tests__/report.test.ts +25 -25
  379. package/src/cli/commands/__tests__/review.test.ts +33 -31
  380. package/src/cli/commands/__tests__/security.test.ts +23 -27
  381. package/src/cli/commands/__tests__/spec-baseline-check.test.ts +20 -20
  382. package/src/cli/commands/__tests__/spec.test.ts +41 -29
  383. package/src/cli/commands/__tests__/status-extra.test.ts +18 -18
  384. package/src/cli/commands/__tests__/status-skipped-lines.test.ts +72 -0
  385. package/src/cli/commands/__tests__/status.test.ts +34 -34
  386. package/src/cli/commands/__tests__/sync-docs-agents.test.ts +108 -86
  387. package/src/cli/commands/__tests__/sync-docs.test.ts +72 -74
  388. package/src/cli/commands/__tests__/update-user-model.test.ts +62 -33
  389. package/src/cli/commands/__tests__/validate.test.ts +27 -29
  390. package/src/cli/commands/acceptance.ts +35 -24
  391. package/src/cli/commands/analyze-sessions.ts +25 -24
  392. package/src/cli/commands/check.ts +88 -65
  393. package/src/cli/commands/command.ts +30 -17
  394. package/src/cli/commands/constraints-report.ts +37 -34
  395. package/src/cli/commands/constraints-retire.ts +58 -55
  396. package/src/cli/commands/constraints.ts +10 -27
  397. package/src/cli/commands/contract.ts +51 -42
  398. package/src/cli/commands/definitions.ts +37 -223
  399. package/src/cli/commands/failure.ts +35 -27
  400. package/src/cli/commands/init.ts +166 -220
  401. package/src/cli/commands/knowledge.ts +250 -203
  402. package/src/cli/commands/passes-gate.ts +58 -68
  403. package/src/cli/commands/performance.ts +23 -18
  404. package/src/cli/commands/posteval-plan.ts +29 -15
  405. package/src/cli/commands/release.ts +75 -78
  406. package/src/cli/commands/report.ts +9 -5
  407. package/src/cli/commands/review.ts +39 -29
  408. package/src/cli/commands/sdd.ts +7 -5
  409. package/src/cli/commands/security.ts +35 -26
  410. package/src/cli/commands/spec-baseline-check.ts +16 -11
  411. package/src/cli/commands/spec.ts +66 -53
  412. package/src/cli/commands/status.ts +59 -53
  413. package/src/cli/commands/sync-docs/agents-syncer.ts +31 -15
  414. package/src/cli/commands/sync-docs/context-syncer.ts +3 -2
  415. package/src/cli/commands/sync-docs/index.ts +73 -100
  416. package/src/cli/commands/sync-docs/project-reader.ts +17 -18
  417. package/src/cli/commands/update-user-model.ts +33 -45
  418. package/src/cli/commands/validate.ts +31 -25
  419. package/src/cli/session-mining/__tests__/transcript.test.ts +118 -0
  420. package/src/cli/session-mining/transcript.ts +26 -8
  421. package/src/completion-checkers/CONTEXT.md +1 -1
  422. package/src/completion-checkers/__tests__/contract-presence.test.ts +1 -1
  423. package/src/completion-checkers/__tests__/phase-format.test.ts +1 -1
  424. package/src/completion-checkers/__tests__/tdd-chain.test.ts +2 -2
  425. package/src/completion-checkers/index.ts +3 -3
  426. package/src/completion-checkers/phase-format.ts +2 -2
  427. package/src/completion-checkers/tdd-chain.ts +2 -2
  428. package/src/completion-checkers/types.ts +5 -5
  429. package/src/context/session-manager.ts +19 -33
  430. package/src/core/CONTEXT.md +9 -6
  431. package/src/core/constraints/__tests__/capabilities-reconcile.test.ts +200 -0
  432. package/src/core/constraints/__tests__/capability-counts.test.ts +94 -0
  433. package/src/core/constraints/__tests__/git-evidence.test.ts +208 -0
  434. package/src/core/constraints/__tests__/injection-drift.test.ts +3 -10
  435. package/src/core/constraints/__tests__/injection-writer.test.ts +231 -0
  436. package/src/core/constraints/__tests__/trace-injection.test.ts +71 -0
  437. package/src/core/constraints/__tests__/usage-report.test.ts +112 -0
  438. package/src/core/constraints/agent-prompt-renderer.ts +2 -6
  439. package/src/core/constraints/capabilities-parser.ts +53 -35
  440. package/src/core/constraints/capabilities-reconcile.ts +166 -0
  441. package/src/core/constraints/checker.ts +75 -130
  442. package/src/core/constraints/checkers/__tests__/capability-sync.test.ts +214 -0
  443. package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +77 -0
  444. package/src/core/constraints/checkers/__tests__/docs-freshness.test.ts +117 -0
  445. package/src/{__tests__ → core/constraints/checkers/__tests__}/governance-presence.test.ts +83 -9
  446. package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +50 -0
  447. package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +76 -0
  448. package/src/{__tests__ → core/constraints/checkers/__tests__}/no-hardcoded-credentials.test.ts +4 -3
  449. package/src/core/constraints/checkers/__tests__/no-test-simplification.test.ts +58 -0
  450. package/src/core/constraints/checkers/__tests__/three-state-consistency.test.ts +145 -0
  451. package/src/core/constraints/checkers/capability-sync.ts +29 -88
  452. package/src/core/constraints/checkers/context-doc-sync.ts +15 -25
  453. package/src/core/constraints/checkers/docs-freshness.ts +40 -69
  454. package/src/core/constraints/checkers/governance-presence.ts +7 -25
  455. package/src/core/constraints/checkers/no-test-simplification.ts +4 -3
  456. package/src/core/constraints/checkers/types.ts +2 -2
  457. package/src/core/constraints/context-builder.ts +39 -91
  458. package/src/core/constraints/doc-freshness/runner.ts +2 -1
  459. package/src/core/constraints/git-evidence.ts +120 -0
  460. package/src/core/constraints/injection-drift.ts +13 -60
  461. package/src/core/constraints/injection-writer.ts +212 -0
  462. package/src/core/constraints/usage-report.ts +36 -17
  463. package/src/core/index.ts +4 -2
  464. package/src/core/project-config-loader.ts +49 -9
  465. package/src/core/validators/__tests__/passes-gate.test.ts +2 -56
  466. package/src/core/validators/__tests__/test-output.test.ts +372 -0
  467. package/src/core/validators/passes-gate.ts +86 -248
  468. package/src/core/validators/test-output.ts +128 -0
  469. package/src/failure/__tests__/recorder.test.ts +168 -0
  470. package/src/failure/recorder.ts +13 -11
  471. package/src/gates/CONTEXT.md +6 -3
  472. package/src/gates/__tests__/acceptance-types-single-source.test.ts +68 -0
  473. package/src/gates/__tests__/acceptance.test.ts +4 -86
  474. package/src/gates/__tests__/effective-gates.test.ts +3 -8
  475. package/src/gates/__tests__/test-verdict-consistency.test.ts +140 -0
  476. package/src/gates/acceptance.ts +19 -84
  477. package/src/gates/checker-gate.ts +8 -7
  478. package/src/gates/command.ts +10 -16
  479. package/src/gates/contract.ts +22 -61
  480. package/src/gates/performance.ts +10 -22
  481. package/src/gates/review.ts +21 -41
  482. package/src/gates/security.ts +10 -22
  483. package/src/gates/types.ts +70 -5
  484. package/src/hooks/CONTEXT.md +1 -1
  485. package/src/hooks/bootstrap.ts +5 -2
  486. package/src/index.ts +11 -6
  487. package/src/knowledge/CONTEXT.md +8 -3
  488. package/src/knowledge/__tests__/audit-labels.test.ts +44 -0
  489. package/src/knowledge/__tests__/flywheel-metrics.test.ts +135 -0
  490. package/src/knowledge/__tests__/import.test.ts +44 -0
  491. package/src/knowledge/__tests__/index-generator.test.ts +55 -2
  492. package/src/knowledge/__tests__/ingest.test.ts +85 -0
  493. package/src/knowledge/__tests__/migration.test.ts +47 -0
  494. package/src/knowledge/__tests__/reference-tracker.test.ts +16 -0
  495. package/src/knowledge/__tests__/store.test.ts +194 -0
  496. package/src/knowledge/audit.ts +67 -71
  497. package/src/knowledge/flywheel-metrics.ts +71 -0
  498. package/src/knowledge/import.ts +4 -18
  499. package/src/knowledge/index-generator.ts +20 -15
  500. package/src/knowledge/ingest.ts +9 -4
  501. package/src/knowledge/migration.ts +11 -8
  502. package/src/knowledge/reference-tracker.ts +12 -8
  503. package/src/knowledge/store.ts +64 -11
  504. package/src/monitoring/CONTEXT.md +8 -3
  505. package/src/monitoring/__tests__/skipped-lines-report.test.ts +178 -0
  506. package/src/monitoring/__tests__/traces.test.ts +57 -0
  507. package/src/monitoring/context-tracker.ts +11 -23
  508. package/src/monitoring/index.ts +1 -1
  509. package/src/monitoring/trace-analyzer.ts +21 -2
  510. package/src/monitoring/traces.ts +39 -36
  511. package/src/release/CONTEXT.md +24 -0
  512. package/src/release/__tests__/barrel.test.ts +60 -0
  513. package/src/release/__tests__/integrity.test.ts +157 -0
  514. package/src/release/index.ts +2 -0
  515. package/src/release/integrity.ts +141 -0
  516. package/src/sdd/__tests__/index-generator.test.ts +71 -0
  517. package/src/sdd/index-generator.ts +9 -9
  518. package/src/test-setup/__tests__/project-fixture.test.ts +178 -0
  519. package/src/test-setup/project-fixture.ts +87 -0
  520. package/src/tools/CONTEXT.md +6 -0
  521. package/src/tools/__tests__/registry.test.ts +221 -0
  522. package/src/types/__tests__/types.test.ts +4 -5
  523. package/src/types/constraint.ts +0 -25
  524. package/src/types/failure.ts +8 -0
  525. package/src/types/passes-gate.ts +10 -30
  526. package/src/types/project-config.ts +1 -1
  527. package/src/types/session.ts +5 -5
  528. package/src/utils/__tests__/frontmatter.test.ts +206 -0
  529. package/src/utils/__tests__/jsonl-skip-disposition.test.ts +175 -0
  530. package/src/utils/__tests__/jsonl.test.ts +187 -0
  531. package/src/utils/__tests__/package-version.test.ts +97 -0
  532. package/src/utils/exec.ts +15 -0
  533. package/src/utils/file-walk.ts +11 -2
  534. package/src/utils/frontmatter.ts +121 -0
  535. package/src/utils/jsonl.ts +167 -0
  536. package/src/utils/package-version.ts +27 -0
  537. package/dist/cli/commands/doc-freshness-check.d.ts +0 -53
  538. package/dist/cli/commands/doc-freshness-check.d.ts.map +0 -1
  539. package/dist/cli/commands/doc-freshness-check.js +0 -352
  540. package/dist/cli/commands/doc-freshness-check.js.map +0 -1
  541. package/dist/types/index.d.ts +0 -14
  542. package/dist/types/index.d.ts.map +0 -1
  543. package/dist/types/index.js +0 -39
  544. package/dist/types/index.js.map +0 -1
  545. package/src/__tests__/long-running.test.ts +0 -17
  546. package/src/__tests__/passes-gate-extension.test.ts +0 -151
  547. package/src/cli/commands/__tests__/doc-freshness-check.test.ts +0 -168
  548. package/src/cli/commands/doc-freshness-check.ts +0 -386
  549. package/src/spec/CONTEXT.md +0 -11
  550. package/src/types/index.ts +0 -47
@@ -2,7 +2,7 @@
2
2
  * 知识库质量审计引擎
3
3
  *
4
4
  * 纯代码检测,零 token 成本。
5
- * 6 维度覆盖:结构完整性 / 内容质量 / 去重有效性 / 成熟度健康 / 新鲜度 / 飞轮验证
5
+ * 7 维度覆盖:结构完整性 / 内容质量 / 去重有效性 / 成熟度健康 / 新鲜度 / 飞轮验证 / 增量存活
6
6
  *
7
7
  * 两种模式:
8
8
  * - validate(entry): 单条入库检查(ingest gate)
@@ -13,6 +13,7 @@ import * as fs from 'fs';
13
13
  import * as path from 'path';
14
14
  import { FileKnowledgeStore } from './store';
15
15
  import type { KnowledgeStore } from './store';
16
+ import { evaluateFlywheel, genuineRefs } from './flywheel-metrics';
16
17
  import type { KnowledgeEntry } from './types';
17
18
 
18
19
  // ── Types ─────────────────────────────────────────────────
@@ -99,22 +100,22 @@ const EVENT_NOISE_PATTERNS = [
99
100
  /^\[Session Feature\]\s/,
100
101
  ];
101
102
  const REQUIRED_FRONTMATTER = ['id', 'type', 'title', 'maturity'];
102
- const SYNTHETIC_REF_PATTERN = /^(search|test-agent|prompt-inject|monitor|analyst|auditor|triage|executor|session|trend|incident):\d{4}-\d{2}-\d{2}/;
103
-
104
- /** Filter out synthetic references (automated search/ops records, not genuine consumption) */
105
- function genuineRefs(refs: string[]): string[] {
106
- return refs.filter(r => !SYNTHETIC_REF_PATTERN.test(r));
107
- }
108
103
 
109
104
  // ── Per-Entry Rules ───────────────────────────────────────
110
105
 
111
106
  interface AuditRule {
112
107
  name: AuditRuleName;
108
+ /** 中文展示文案正本(#109):定义即注册,CLI 经 AUDIT_RULE_LABELS 消费,无 label 即编译期失败 */
109
+ label: string;
113
110
  severity: AuditIssue['severity'];
114
111
  action: AuditAction;
112
+ /** 规则适用人口:active = 跳过 archived 条目;all = 全人口(含已归档) */
113
+ scope: AuditRuleScope;
115
114
  detect: (entry: KnowledgeEntry, ctx: AuditContext) => string | null;
116
115
  }
117
116
 
117
+ type AuditRuleScope = 'active' | 'all';
118
+
118
119
  interface AuditContext {
119
120
  shortContentThreshold: number;
120
121
  staleDays: number;
@@ -126,8 +127,10 @@ const perEntryRules: AuditRule[] = [
126
127
  // D1: 结构完整性
127
128
  {
128
129
  name: 'frontmatter-missing',
130
+ label: 'frontmatter 缺失',
129
131
  severity: 'high',
130
132
  action: 'reject',
133
+ scope: 'all',
131
134
  detect: (entry) => {
132
135
  const missing = REQUIRED_FRONTMATTER.filter(f => {
133
136
  const val = (entry as any)[f];
@@ -143,10 +146,11 @@ const perEntryRules: AuditRule[] = [
143
146
  // D2: 内容质量
144
147
  {
145
148
  name: 'test-data-pollution',
149
+ label: '测试数据污染',
146
150
  severity: 'critical',
147
151
  action: 'archive',
152
+ scope: 'active',
148
153
  detect: (entry) => {
149
- if (entry.maturity === 'archived') return null;
150
154
  // Match test ID patterns
151
155
  if (/^(test-|inj-test)/.test(entry.id)) {
152
156
  return `测试 ID: "${entry.id}"`;
@@ -163,10 +167,11 @@ const perEntryRules: AuditRule[] = [
163
167
  },
164
168
  {
165
169
  name: 'daily-audit-noise',
170
+ label: '每日审计噪音',
166
171
  severity: 'high',
167
172
  action: 'archive',
173
+ scope: 'active',
168
174
  detect: (entry) => {
169
- if (entry.maturity === 'archived') return null;
170
175
  if (DAILY_AUDIT_PATTERN.test(entry.title)) {
171
176
  return `每日审计摘要: "${entry.title}"`;
172
177
  }
@@ -175,10 +180,11 @@ const perEntryRules: AuditRule[] = [
175
180
  },
176
181
  {
177
182
  name: 'event-noise',
183
+ label: '运维事件噪音',
178
184
  severity: 'critical',
179
185
  action: 'archive',
186
+ scope: 'active',
180
187
  detect: (entry) => {
181
- if (entry.maturity === 'archived') return null;
182
188
  for (const pattern of EVENT_NOISE_PATTERNS) {
183
189
  if (pattern.test(entry.title)) {
184
190
  return `运维事件标题: "${entry.title}"`;
@@ -189,8 +195,10 @@ const perEntryRules: AuditRule[] = [
189
195
  },
190
196
  {
191
197
  name: 'zero-content-proven',
198
+ label: '零内容 proven',
192
199
  severity: 'critical',
193
200
  action: 'demote',
201
+ scope: 'all',
194
202
  detect: (entry) => {
195
203
  if (entry.maturity === 'proven' && entry.content.trim().length < ZERO_CONTENT_THRESHOLD) {
196
204
  return `proven 条目内容仅 ${entry.content.trim().length} 字符`;
@@ -200,8 +208,10 @@ const perEntryRules: AuditRule[] = [
200
208
  },
201
209
  {
202
210
  name: 'maturity-inflation',
211
+ label: '成熟度虚高',
203
212
  severity: 'high',
204
213
  action: 'demote',
214
+ scope: 'all',
205
215
  detect: (entry) => {
206
216
  if (entry.maturity === 'verified' && entry.content.trim().length < ZERO_CONTENT_THRESHOLD) {
207
217
  return `verified 条目内容仅 ${entry.content.trim().length} 字符`;
@@ -211,10 +221,11 @@ const perEntryRules: AuditRule[] = [
211
221
  },
212
222
  {
213
223
  name: 'short-content',
224
+ label: '短内容',
214
225
  severity: 'medium',
215
226
  action: 'flag',
227
+ scope: 'active',
216
228
  detect: (entry, ctx) => {
217
- if (entry.maturity === 'archived') return null;
218
229
  const len = entry.content.trim().length;
219
230
  if (len < ctx.shortContentThreshold && len >= ZERO_CONTENT_THRESHOLD) {
220
231
  return `内容 ${len} 字符 (阈值 ${ctx.shortContentThreshold})`;
@@ -226,10 +237,11 @@ const perEntryRules: AuditRule[] = [
226
237
  // D3: 去重有效性
227
238
  {
228
239
  name: 'title-duplicate',
240
+ label: '标题重复',
229
241
  severity: 'medium',
230
242
  action: 'flag',
243
+ scope: 'active',
231
244
  detect: (entry, ctx) => {
232
- if (entry.maturity === 'archived') return null;
233
245
  if (!ctx.allEntries) return null;
234
246
  if (!entry.title) return null;
235
247
  const dupes = ctx.allEntries.filter(e =>
@@ -246,8 +258,10 @@ const perEntryRules: AuditRule[] = [
246
258
  },
247
259
  {
248
260
  name: 'source-refs-bloat',
261
+ label: 'sourceReferences 膨胀',
249
262
  severity: 'low',
250
263
  action: 'trim',
264
+ scope: 'all',
251
265
  detect: (entry) => {
252
266
  if ((entry.sourceReferences?.length || 0) > MAX_SOURCE_REFS) {
253
267
  return `sourceReferences ${entry.sourceReferences!.length} 条 (上限 ${MAX_SOURCE_REFS})`;
@@ -257,10 +271,11 @@ const perEntryRules: AuditRule[] = [
257
271
  },
258
272
  {
259
273
  name: 'fragment-cluster',
274
+ label: '碎片集群',
260
275
  severity: 'medium',
261
276
  action: 'flag',
277
+ scope: 'active',
262
278
  detect: (entry, ctx) => {
263
- if (entry.maturity === 'archived') return null;
264
279
  if (!ctx.allEntries || entry.content.trim().length >= 100) return null;
265
280
  if (!entry.tags?.length) return null;
266
281
 
@@ -284,8 +299,10 @@ const perEntryRules: AuditRule[] = [
284
299
  // D4: 成熟度健康
285
300
  {
286
301
  name: 'promotion-blocked',
302
+ label: 'promotion 受阻',
287
303
  severity: 'medium',
288
304
  action: 'flag',
305
+ scope: 'all',
289
306
  detect: (entry, ctx) => {
290
307
  if (entry.maturity !== 'draft') return null;
291
308
  const created = new Date(entry.created);
@@ -298,8 +315,10 @@ const perEntryRules: AuditRule[] = [
298
315
  },
299
316
  {
300
317
  name: 'orphan-draft',
318
+ label: '孤儿 draft',
301
319
  severity: 'low',
302
320
  action: 'flag',
321
+ scope: 'all',
303
322
  detect: (entry) => {
304
323
  if (entry.maturity !== 'draft') return null;
305
324
  if (entry.contributors.length === 0 && entry.projects.length === 0 && genuineRefs(entry.referencedBy).length === 0) {
@@ -312,10 +331,11 @@ const perEntryRules: AuditRule[] = [
312
331
  // D5: 新鲜度
313
332
  {
314
333
  name: 'stale-entry',
334
+ label: '过期条目',
315
335
  severity: 'medium',
316
336
  action: 'flag',
337
+ scope: 'active',
317
338
  detect: (entry, ctx) => {
318
- if (entry.maturity === 'archived') return null;
319
339
  const lastRef = entry.lastReferenced || entry.created;
320
340
  if (!lastRef) return null;
321
341
  const daysSinceRef = (Date.now() - new Date(lastRef).getTime()) / (1000 * 60 * 60 * 24);
@@ -329,10 +349,11 @@ const perEntryRules: AuditRule[] = [
329
349
  // D2b: 领域相关性 — 检测已废弃领域的残留条目
330
350
  {
331
351
  name: 'deprecated-domain',
352
+ label: '废弃领域残留',
332
353
  severity: 'high',
333
354
  action: 'archive',
355
+ scope: 'active',
334
356
  detect: (entry) => {
335
- if (entry.maturity === 'archived') return null;
336
357
  const tags = (entry.tags ?? []).map(t => t.toLowerCase());
337
358
  // Tag-level: "pipeline" tag is deprecated (Studio Pipeline superseded by Agent Network)
338
359
  if (tags.includes('pipeline')) {
@@ -348,8 +369,19 @@ const perEntryRules: AuditRule[] = [
348
369
  },
349
370
  ];
350
371
 
372
+ /** 规则 label 正本表(#109,ADR-0002 定义即注册):派生自 perEntryRules,与规则键集编译期闭环,CLI 展示层直接消费 */
373
+ export const AUDIT_RULE_LABELS: Record<AuditRuleName, string> = Object.fromEntries(
374
+ perEntryRules.map(r => [r.name, r.label])
375
+ ) as Record<AuditRuleName, string>;
376
+
351
377
  // ── Audit Engine ──────────────────────────────────────────
352
378
 
379
+ /** 按 scope 决定是否评估:active 规则对 archived 条目不判定(人口过滤唯一落点) */
380
+ function ruleDetail(rule: AuditRule, entry: KnowledgeEntry, ctx: AuditContext): string | null {
381
+ if (rule.scope === 'active' && entry.maturity === 'archived') return null;
382
+ return rule.detect(entry, ctx);
383
+ }
384
+
353
385
  export class KnowledgeAudit {
354
386
  private store: KnowledgeStore;
355
387
  private shortContentThreshold: number;
@@ -372,21 +404,7 @@ export class KnowledgeAudit {
372
404
  staleDays: this.staleDays,
373
405
  promotionBlockDays: this.promotionBlockDays,
374
406
  };
375
- const issues: AuditIssue[] = [];
376
- for (const rule of perEntryRules) {
377
- const detail = rule.detect(entry, ctx);
378
- if (detail) {
379
- issues.push({
380
- rule: rule.name,
381
- entryId: entry.id,
382
- title: entry.title,
383
- severity: rule.severity,
384
- action: rule.action,
385
- detail,
386
- });
387
- }
388
- }
389
- return issues;
407
+ return this.scanEntries([entry], ctx);
390
408
  }
391
409
 
392
410
  /**
@@ -402,22 +420,7 @@ export class KnowledgeAudit {
402
420
  };
403
421
 
404
422
  // Per-entry issues
405
- const allIssues: AuditIssue[] = [];
406
- for (const entry of entries) {
407
- for (const rule of perEntryRules) {
408
- const detail = rule.detect(entry, ctx);
409
- if (detail) {
410
- allIssues.push({
411
- rule: rule.name,
412
- entryId: entry.id,
413
- title: entry.title,
414
- severity: rule.severity,
415
- action: rule.action,
416
- detail,
417
- });
418
- }
419
- }
420
- }
423
+ const allIssues = this.scanEntries(entries, ctx);
421
424
 
422
425
  // Summary
423
426
  const summary = {} as Record<AuditRuleName, number>;
@@ -438,7 +441,7 @@ export class KnowledgeAudit {
438
441
 
439
442
  // Health score (after)
440
443
  const entriesAfter = options?.autoFix ? this.store.list({ excludeArchived: false }) : entries;
441
- const issuesAfter = options?.autoFix ? this.runScan(entriesAfter, ctx) : allIssues;
444
+ const issuesAfter = options?.autoFix ? this.scanEntries(entriesAfter, ctx) : allIssues;
442
445
  const healthAfter = this.calculateHealthScore(entriesAfter, issuesAfter);
443
446
 
444
447
  return {
@@ -496,33 +499,23 @@ export class KnowledgeAudit {
496
499
  const staleRatio = active.length > 0 ? d5Issues.length / active.length : 0;
497
500
  const d5Score = active.length === 0 ? 100 : Math.max(0, 100 - (staleRatio * 100));
498
501
 
499
- // D6: 飞轮验证
500
- const withRefs = active.filter(e => genuineRefs(e.referencedBy).length > 0).length;
501
- const refCoverage = active.length > 0 ? withRefs / active.length : 0;
502
- const avgRefs = active.length > 0
503
- ? active.reduce((sum, e) => sum + genuineRefs(e.referencedBy).length, 0) / active.length
504
- : 0;
505
-
506
- // Consumption hit rate from aggregated stats file (written by MonitorAgent)
507
- let consumptionHitRate = 0;
502
+ // D6: 飞轮验证 —— 指标计算唯一实现在 flywheel-metrics,此处只做 fs 读取与报告层映射
508
503
  let dailyConsumptionEvents = 0;
509
504
  try {
510
505
  const statsPath = path.join(this.store.getBaseDir(), '.consumption-stats.json');
511
506
  if (fs.existsSync(statsPath)) {
512
507
  const stats = JSON.parse(fs.readFileSync(statsPath, 'utf-8'));
513
508
  dailyConsumptionEvents = stats.dailyEvents || 0;
514
- // hitRate: daily events / active entries, capped at 1
515
- consumptionHitRate = active.length > 0
516
- ? Math.min(dailyConsumptionEvents / active.length, 1)
517
- : 0;
518
509
  }
519
510
  } catch { /* best-effort */ }
520
511
 
512
+ const metrics = evaluateFlywheel({ entries: active, dailyConsumptionEvents });
513
+
521
514
  // Score: refCoverage * 50 + avgRefs * 20 + consumptionHitRate * 30
522
515
  const d6Score = Math.min(100, Math.round(
523
- refCoverage * 50 +
524
- Math.min(avgRefs / 5, 1) * 20 +
525
- consumptionHitRate * 30
516
+ metrics.refCoverage * 50 +
517
+ Math.min(metrics.avgRefs / 5, 1) * 20 +
518
+ metrics.consumptionHitRate * 30
526
519
  ));
527
520
 
528
521
  return {
@@ -576,12 +569,12 @@ export class KnowledgeAudit {
576
569
  score: d6Score,
577
570
  issues: 0,
578
571
  details: {
579
- activeEntries: active.length,
580
- entriesWithRefs: withRefs,
581
- refCoverage: Math.round(refCoverage * 100),
582
- avgRefCount: Math.round(avgRefs * 10) / 10,
583
- dailyConsumptionEvents,
584
- consumptionHitRate: Math.round(consumptionHitRate * 100),
572
+ activeEntries: metrics.activeEntries,
573
+ entriesWithRefs: metrics.entriesWithRefs,
574
+ refCoverage: Math.round(metrics.refCoverage * 100),
575
+ avgRefCount: Math.round(metrics.avgRefs * 10) / 10,
576
+ dailyConsumptionEvents: metrics.dailyConsumptionEvents,
577
+ consumptionHitRate: Math.round(metrics.consumptionHitRate * 100),
585
578
  },
586
579
  },
587
580
  incremental: this.computeIncremental(),
@@ -608,11 +601,14 @@ export class KnowledgeAudit {
608
601
 
609
602
  // ── Internal ────────────────────────────────────────────
610
603
 
611
- private runScan(entries: KnowledgeEntry[], ctx: AuditContext): AuditIssue[] {
604
+ /**
605
+ * 逐条目跑 perEntryRules(#111 收口):validate(单条目)、run(全量)与 autoFix 重扫共用
606
+ */
607
+ private scanEntries(entries: KnowledgeEntry[], ctx: AuditContext): AuditIssue[] {
612
608
  const issues: AuditIssue[] = [];
613
609
  for (const entry of entries) {
614
610
  for (const rule of perEntryRules) {
615
- const detail = rule.detect(entry, ctx);
611
+ const detail = ruleDetail(rule, entry, ctx);
616
612
  if (detail) {
617
613
  issues.push({
618
614
  rule: rule.name,
@@ -0,0 +1,71 @@
1
+ /**
2
+ * 知识飞轮指标(harness#81)
3
+ *
4
+ * 纯判定 module:吃 active 条目数组(+ 可选当日消费事件数),一次产出 canonical 飞轮指标。
5
+ * 零 IO——人口筛选(哪些条目算 active)与 `.consumption-stats.json` 读取都留在调用方。
6
+ *
7
+ * 此前同一组分子计算在 audit.ts D6、knowledge stats、knowledge health 三处独立实现,
8
+ * 后两处直接数 `referencedBy.length`,把 search / prompt-inject / test-agent 等自动化
9
+ * 触碰算成真实消费——同一知识库报出两套飞轮健康度(实测 refCoverage 26% vs 28%、
10
+ * avgRefs 0.4 vs 0.6)。飞轮指标是退役/降级判定的输入,口径必须唯一:
11
+ * **分子一律为过滤 synthetic 后的 genuine refs**(ADR-0013)。
12
+ *
13
+ * 单位约定:本 module 只出比例(0..1),百分比取整/一位小数等是展示层的事。
14
+ */
15
+
16
+ import type { KnowledgeEntry } from './types';
17
+
18
+ /**
19
+ * 自动化写入方形成的引用键:`<contributor>:<YYYY-MM-DD>`(contributor 见 recordReference)。
20
+ * 这些是检索/运维动作的按天记账,不代表条目被真实消费,不计入飞轮分子。
21
+ * `unknown:`(query() 注入路径)不在过滤面内——注入即消费,维持既有语义。
22
+ */
23
+ const SYNTHETIC_REF_PATTERN = /^(search|test-agent|prompt-inject|monitor|analyst|auditor|triage|executor|session|trend|incident):\d{4}-\d{2}-\d{2}/;
24
+
25
+ /** 剔除 synthetic refs,返回真实消费引用(唯一口径) */
26
+ export function genuineRefs(refs: string[]): string[] {
27
+ return refs.filter(r => !SYNTHETIC_REF_PATTERN.test(r));
28
+ }
29
+
30
+ export interface FlywheelEnv {
31
+ /** 参与统计的条目人口(调用方筛选,本 module 不再过滤 archived/deprecated) */
32
+ entries: KnowledgeEntry[];
33
+ /** 当日消费事件数(`.consumption-stats.json` 的 dailyEvents,MonitorAgent 汇总写入);未提供视为 0 */
34
+ dailyConsumptionEvents?: number;
35
+ }
36
+
37
+ export interface FlywheelMetrics {
38
+ /** 人口大小 */
39
+ activeEntries: number;
40
+ /** 分子:有 genuine 引用(含 1 条即算)的条目数 */
41
+ entriesWithRefs: number;
42
+ /** entriesWithRefs / activeEntries,0..1;空人口为 0 */
43
+ refCoverage: number;
44
+ /** genuine 引用总数 / activeEntries,未取整比例 */
45
+ avgRefs: number;
46
+ /** 透传输入,供报告层复用 */
47
+ dailyConsumptionEvents: number;
48
+ /** dailyConsumptionEvents / activeEntries,capped at 1;无消费事件为 0 */
49
+ consumptionHitRate: number;
50
+ }
51
+
52
+ export function evaluateFlywheel(env: FlywheelEnv): FlywheelMetrics {
53
+ const entries = env.entries;
54
+ const dailyConsumptionEvents = env.dailyConsumptionEvents ?? 0;
55
+ const activeEntries = entries.length;
56
+
57
+ const refCounts = entries.map(e => genuineRefs(e.referencedBy).length);
58
+ const entriesWithRefs = refCounts.filter(n => n > 0).length;
59
+ const totalRefs = refCounts.reduce((sum, n) => sum + n, 0);
60
+
61
+ return {
62
+ activeEntries,
63
+ entriesWithRefs,
64
+ refCoverage: activeEntries > 0 ? entriesWithRefs / activeEntries : 0,
65
+ avgRefs: activeEntries > 0 ? totalRefs / activeEntries : 0,
66
+ dailyConsumptionEvents,
67
+ consumptionHitRate: activeEntries > 0
68
+ ? Math.min(dailyConsumptionEvents / activeEntries, 1)
69
+ : 0,
70
+ };
71
+ }
@@ -183,9 +183,7 @@ export class ColdStartImporter {
183
183
  }
184
184
 
185
185
  // 写入知识库
186
- for (const entry of entries) {
187
- this.config.store.save(entry);
188
- }
186
+ this.config.store.saveAll(entries);
189
187
 
190
188
  return { source, entries, errors };
191
189
  }
@@ -202,12 +200,6 @@ export class ColdStartImporter {
202
200
  const { execSync } = require('child_process');
203
201
  const cwd = this.config.projectRoot;
204
202
 
205
- // 分析大型重构提交
206
- execSync(
207
- 'git log --oneline --diff-filter=M --numstat --since="6 months ago" | head -100',
208
- { cwd, encoding: 'utf-8', timeout: 10000 },
209
- );
210
-
211
203
  // 查找 fix/hotfix 相关提交
212
204
  const fixCommits = execSync(
213
205
  'git log --oneline --grep="fix\\|hotfix\\|bug" --since="6 months ago" | head -20',
@@ -247,9 +239,7 @@ export class ColdStartImporter {
247
239
  });
248
240
  }
249
241
 
250
- for (const entry of entries) {
251
- this.config.store.save(entry);
252
- }
242
+ this.config.store.saveAll(entries);
253
243
 
254
244
  return { source, entries, errors };
255
245
  }
@@ -317,9 +307,7 @@ export class ColdStartImporter {
317
307
  }
318
308
  }
319
309
 
320
- for (const entry of entries) {
321
- this.config.store.save(entry);
322
- }
310
+ this.config.store.saveAll(entries);
323
311
 
324
312
  return { source, entries, errors };
325
313
  }
@@ -354,9 +342,7 @@ export class ColdStartImporter {
354
342
  }
355
343
  }
356
344
 
357
- for (const entry of entries) {
358
- this.config.store.save(entry);
359
- }
345
+ this.config.store.saveAll(entries);
360
346
 
361
347
  return { source, entries, errors };
362
348
  }
@@ -9,7 +9,7 @@
9
9
 
10
10
  import * as fs from 'fs';
11
11
  import * as path from 'path';
12
- import * as yaml from 'js-yaml';
12
+ import { splitFrontmatter } from '../utils/frontmatter';
13
13
 
14
14
  const EXCLUDED_DIRS = ['.archive', 'archived', '.snapshots', 'resolutions'];
15
15
  const INDEX_FILENAME = '_index.md';
@@ -59,7 +59,7 @@ export class KnowledgeIndexGenerator {
59
59
  `# Types: ${Object.entries(byType).map(([t, n]) => `${t}:${n}`).join(' ')}`,
60
60
  `#`,
61
61
  `# Usage:`,
62
- `# grep "<keyword>" ~/.studio/knowledge/_index.md`,
62
+ `# grep "<keyword>" ~/.harness/knowledge/_index.md`,
63
63
  `# Then Read the matching file for full content.`,
64
64
  `#`,
65
65
  `# filename|id|type|title|maturity|tags|terms`,
@@ -131,32 +131,37 @@ export class KnowledgeIndexGenerator {
131
131
  // 跳过 ghost 文件(文件名为 .md)
132
132
  if (path.basename(filename) === '.md') return null;
133
133
 
134
- // 尝试解析 YAML frontmatter
135
- const fmMatch = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
136
- if (fmMatch) {
137
- const meta = yaml.load(fmMatch[1]) as Record<string, unknown>;
138
- const body = fmMatch[2];
139
- const headings = this.extractHeadings(body);
134
+ // 尝试解析 YAML frontmatter(语法与失败走法正本:utils/frontmatter)
135
+ const fm = splitFrontmatter(raw);
136
+ if (fm.state === 'malformed') {
137
+ // 统一口径(harness#89):损坏必须显式上报,恢复动作是 best-effort 索引原文
138
+ console.error(
139
+ `[harness] 知识索引 frontmatter ${fm.reason}(${fm.detail}),按无 frontmatter 处理 ${filename}`
140
+ );
141
+ }
142
+ if (fm.state === 'ok') {
143
+ const meta = fm.meta;
144
+ const headings = this.extractHeadings(fm.body);
140
145
 
141
146
  // 检测 skill schema(name/description 而非 id/type)
142
- const type = meta?.type
147
+ const type = meta.type
143
148
  ? String(meta.type)
144
- : meta?.name
149
+ : meta.name
145
150
  ? 'skill'
146
151
  : this.inferType(filename);
147
152
 
148
153
  return {
149
154
  filename,
150
- id: String(meta?.id ?? meta?.name ?? path.basename(filePath, '.md')),
155
+ id: String(meta.id ?? meta.name ?? path.basename(filePath, '.md')),
151
156
  type,
152
- title: String(meta?.title ?? meta?.description ?? ''),
153
- maturity: String(meta?.maturity ?? 'unknown'),
154
- tags: Array.isArray(meta?.tags) ? (meta.tags as unknown[]).map(String) : [],
157
+ title: String(meta.title ?? meta.description ?? ''),
158
+ maturity: String(meta.maturity ?? 'unknown'),
159
+ tags: Array.isArray(meta.tags) ? (meta.tags as unknown[]).map(String) : [],
155
160
  headings,
156
161
  };
157
162
  }
158
163
 
159
- // frontmatter — best effort
164
+ // absent(含空 meta)或已上报的 malformed — best effort
160
165
  const headings = this.extractHeadings(raw);
161
166
  const h1Match = raw.match(/^#\s+(.+)$/m);
162
167
 
@@ -13,6 +13,7 @@ import type {
13
13
  } from './types';
14
14
  import type { KnowledgeStore } from './store';
15
15
  import { KnowledgeAudit } from './audit';
16
+ import { splitFrontmatter } from '../utils/frontmatter';
16
17
 
17
18
  const MAX_SOURCE_REFS = 20;
18
19
  const MAX_EXTERNAL_CONTENT_LENGTH = 5000;
@@ -178,7 +179,10 @@ export class KnowledgeIngest {
178
179
 
179
180
  private generateId(type: KnowledgeSubsystem): string {
180
181
  const prefix = type.toUpperCase().slice(0, 3);
181
- const existing = this.store.list({ types: [type] });
182
+ // 序号取自索引计数(harness#107):不再走 list() 逐条读条目文件;
183
+ // 与 list() 默认过滤口径一致,archived/deprecated 不计入序号
184
+ const existing = this.store.readIndex()
185
+ .filter(e => e.type === type && e.maturity !== 'archived' && e.maturity !== 'deprecated');
182
186
  const seq = String(existing.length + 1).padStart(3, '0');
183
187
  return `${prefix}-${seq}`;
184
188
  }
@@ -242,9 +246,10 @@ export class KnowledgeIngest {
242
246
 
243
247
  /** Extract first 50 chars of content body (after frontmatter), stripped of whitespace */
244
248
  private getContentPrefix(content: string): string {
245
- // Strip YAML frontmatter (may be nested if content includes raw markdown)
246
- const body = content.replace(/^---[\s\S]*?---\n?/, '').trim();
247
- return body.slice(0, 50).replace(/\s+/g, '');
249
+ // 正本判定(harness#89):只有合法 frontmatter 块才剥头,正文里的 --- 分隔线不再被误吞
250
+ const fm = splitFrontmatter(content);
251
+ const body = fm.state === 'ok' ? fm.body : content;
252
+ return body.trim().slice(0, 50).replace(/\s+/g, '');
248
253
  }
249
254
 
250
255
  /** Calculate keyword overlap ratio between two normalized titles */
@@ -7,7 +7,7 @@
7
7
 
8
8
  import * as fs from 'fs';
9
9
  import * as path from 'path';
10
- import * as yaml from 'js-yaml';
10
+ import { splitFrontmatter, joinFrontmatter } from '../utils/frontmatter';
11
11
 
12
12
  interface MigrationResult {
13
13
  total: number;
@@ -40,14 +40,19 @@ export function migrateKnowledgeEntries(baseDir: string): MigrationResult {
40
40
  const filePath = path.join(baseDir, file);
41
41
  try {
42
42
  const raw = fs.readFileSync(filePath, 'utf-8');
43
- const match = raw.match(/^---\n([\s\S]*?)\n---\n\n?([\s\S]*)$/);
44
- if (!match) {
43
+ const fm = splitFrontmatter(raw);
44
+ if (fm.state === 'absent') {
45
45
  result.errors.push(`${file}: no frontmatter found`);
46
46
  continue;
47
47
  }
48
+ if (fm.state === 'malformed') {
49
+ // 统一口径(harness#89):损坏的 frontmatter 落 errors,不静默丢
50
+ result.errors.push(`${file}: frontmatter ${fm.reason} (${fm.detail})`);
51
+ continue;
52
+ }
48
53
 
49
- const meta = yaml.load(match[1]) as Record<string, unknown>;
50
- const content = match[2];
54
+ const meta = fm.meta;
55
+ const content = fm.body;
51
56
 
52
57
  // Check if migration needed
53
58
  if (meta.consumptionMode && meta.origin) {
@@ -60,9 +65,7 @@ export function migrateKnowledgeEntries(baseDir: string): MigrationResult {
60
65
  if (!meta.origin) meta.origin = 'agent';
61
66
 
62
67
  // Write back
63
- const newFrontmatter = yaml.dump(meta, { lineWidth: 120 });
64
- const newContent = `---\n${newFrontmatter}---\n\n${content}`;
65
- fs.writeFileSync(filePath, newContent, 'utf-8');
68
+ fs.writeFileSync(filePath, joinFrontmatter(meta, content), 'utf-8');
66
69
  result.migrated++;
67
70
  } catch (err) {
68
71
  result.errors.push(`${file}: ${err instanceof Error ? err.message : String(err)}`);
@@ -5,8 +5,8 @@
5
5
  * Storage: append-only JSONL at .harness/knowledge/references.jsonl
6
6
  */
7
7
 
8
- import * as fs from 'fs';
9
8
  import * as path from 'path';
9
+ import { appendJsonl, readJsonl } from '../utils/jsonl';
10
10
  import type { ReferenceRecord } from './types';
11
11
  import type { KnowledgeStore } from './store';
12
12
 
@@ -33,7 +33,8 @@ export class ReferenceTracker {
33
33
  entryIds,
34
34
  timestamp: new Date().toISOString(),
35
35
  };
36
- fs.appendFileSync(this.filePath, JSON.stringify(record) + '\n', 'utf-8');
36
+ // 写链收口:ensureDir + append(harness#82;原实现不 ensureDir,依赖 store 建目录)
37
+ appendJsonl(this.filePath, record);
37
38
  }
38
39
 
39
40
  /**
@@ -85,13 +86,16 @@ export class ReferenceTracker {
85
86
  // ── Internal ───────────────────────────────────────────────
86
87
 
87
88
  private readAll(): ReferenceRecord[] {
88
- if (!fs.existsSync(this.filePath)) return [];
89
89
  try {
90
- const raw = fs.readFileSync(this.filePath, 'utf-8');
91
- return raw
92
- .split('\n')
93
- .filter(line => line.trim())
94
- .map(line => JSON.parse(line) as ReferenceRecord);
90
+ // 坏行策略:skip(harness#82:原「逐行 map 会抛 + 外层 catch 吞成整文件 []」
91
+ // ——坏一行丢全部记录——不可接受,改为单行损坏只丢该行);
92
+ // 外层 catch 仅兜 IO 读取错误,沿用原吞掉语义
93
+ // 计数去向:豁免(harness#100)——三个消费点 getReferencesForEntry /
94
+ // getReferencesForDecision / updateReferencedBy 只按合法记录取引用关系。
95
+ // 代价如实记录:checkOrphans(knowledge/lint.ts)以 refs.length>0 判孤儿,
96
+ // 坏行会让它给出假孤儿判定,不只是少报;透传需要给 LintIssue / `knowledge health`
97
+ // 报告加计数位,属 knowledge 域输出形状变更,另票收口
98
+ return readJsonl<ReferenceRecord>(this.filePath, 'skip').records;
95
99
  } catch {
96
100
  return [];
97
101
  }