akm-cli 0.9.17-alpha.1 → 0.9.17-alpha.10

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 (399) hide show
  1. package/CHANGELOG.md +1623 -0
  2. package/STABILITY.md +11 -10
  3. package/dist/akm +124 -193
  4. package/dist/akm-migrate +38 -19
  5. package/dist/assets/hints/cli-hints-full.md +6 -7
  6. package/dist/assets/improve-strategies/catchup.json +0 -3
  7. package/dist/assets/improve-strategies/consolidate.json +0 -1
  8. package/dist/assets/improve-strategies/default.json +1 -2
  9. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -2
  10. package/dist/assets/improve-strategies/quick.json +1 -2
  11. package/dist/assets/improve-strategies/reflect-distill.json +1 -2
  12. package/dist/assets/improve-strategies/thorough.json +0 -3
  13. package/dist/assets/prompts/consolidate-pair.md +20 -0
  14. package/dist/assets/prompts/retrieval-relevance-judge.md +6 -0
  15. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +20 -20
  16. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -2
  17. package/dist/assets/templates/html/health.html +3 -5
  18. package/dist/cli/retired-commands.js +1 -1
  19. package/dist/cli/shared.js +6 -2
  20. package/dist/cli/unknown-flags.js +24 -1
  21. package/dist/cli.js +68 -10
  22. package/dist/commands/agent/agent-dispatch.js +1 -1
  23. package/dist/commands/command/command-execution.js +24 -62
  24. package/dist/commands/feedback-cli.js +0 -1
  25. package/dist/commands/health/accept-rate.js +2 -2
  26. package/dist/commands/health/archive-usage.js +92 -0
  27. package/dist/commands/health/checks.js +30 -75
  28. package/dist/commands/health/config-skew.js +38 -0
  29. package/dist/commands/health/data-dir-usage.js +25 -13
  30. package/dist/commands/health/egress.js +54 -0
  31. package/dist/commands/health/html-report.js +1 -42
  32. package/dist/commands/health/improve-metrics.js +136 -587
  33. package/dist/commands/health/md-report.js +1 -6
  34. package/dist/commands/health/plugin-staleness.js +53 -3
  35. package/dist/commands/health/renderers.js +12 -4
  36. package/dist/commands/health/report-view-model.js +14 -119
  37. package/dist/commands/health/types-improve.js +4 -19
  38. package/dist/commands/health/windows.js +64 -74
  39. package/dist/commands/health.js +135 -143
  40. package/dist/commands/improve/consolidate/chunking.js +25 -100
  41. package/dist/commands/improve/consolidate/continuity-check.js +137 -0
  42. package/dist/commands/improve/consolidate/pair-pass.js +791 -0
  43. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  44. package/dist/commands/improve/consolidate.js +573 -1124
  45. package/dist/commands/improve/content-hash.js +16 -24
  46. package/dist/commands/improve/distill/content-repair.js +18 -100
  47. package/dist/commands/improve/distill-guards.js +20 -81
  48. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  49. package/dist/commands/improve/distill.js +608 -1075
  50. package/dist/commands/improve/eligibility.js +126 -400
  51. package/dist/commands/improve/execution.js +8 -10
  52. package/dist/commands/improve/extract-prompt.js +1 -2
  53. package/dist/commands/improve/extract.js +487 -1046
  54. package/dist/commands/improve/feedback-valence.js +0 -25
  55. package/dist/commands/improve/improve-cli.js +54 -171
  56. package/dist/commands/improve/improve-result-file.js +10 -66
  57. package/dist/commands/improve/improve-strategies.js +35 -9
  58. package/dist/commands/improve/improve-usage-report.js +18 -64
  59. package/dist/commands/improve/improve.js +451 -1082
  60. package/dist/commands/improve/ledger.js +119 -0
  61. package/dist/commands/improve/locks.js +2 -8
  62. package/dist/commands/improve/loop-stages.js +401 -1192
  63. package/dist/commands/improve/memory/derived-ref.js +12 -77
  64. package/dist/commands/improve/memory/memory-belief.js +16 -118
  65. package/dist/commands/improve/memory/memory-improve.js +266 -14
  66. package/dist/commands/improve/outcome-loop.js +28 -156
  67. package/dist/commands/improve/planner.js +5 -15
  68. package/dist/commands/improve/preparation.js +776 -2349
  69. package/dist/commands/improve/proactive-maintenance.js +34 -101
  70. package/dist/commands/improve/reflect-noise.js +104 -280
  71. package/dist/commands/improve/reflect.js +642 -1364
  72. package/dist/commands/improve/retrieval-gate.js +127 -0
  73. package/dist/commands/improve/retrieval-scope.js +92 -0
  74. package/dist/commands/improve/salience.js +41 -240
  75. package/dist/commands/improve/session-asset.js +19 -100
  76. package/dist/commands/improve/stage.js +322 -0
  77. package/dist/commands/lint/base-linter.js +37 -15
  78. package/dist/commands/proposal/drain.js +258 -644
  79. package/dist/commands/proposal/proposal-cli.js +19 -20
  80. package/dist/commands/proposal/proposal-types.js +27 -41
  81. package/dist/commands/proposal/proposal.js +38 -8
  82. package/dist/commands/proposal/propose.js +134 -160
  83. package/dist/commands/proposal/repository.js +1099 -1475
  84. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  85. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  86. package/dist/commands/proposal/validators/proposals.js +22 -89
  87. package/dist/commands/read/curate.js +105 -462
  88. package/dist/commands/read/knowledge.js +3 -2
  89. package/dist/commands/read/search-cli.js +16 -33
  90. package/dist/commands/read/search.js +17 -23
  91. package/dist/commands/read/show.js +57 -108
  92. package/dist/commands/sources/bundle-cli.js +25 -2
  93. package/dist/commands/sources/bundle-config-ops.js +4 -0
  94. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  95. package/dist/commands/sources/info.js +127 -29
  96. package/dist/commands/sources/installed-stashes.js +197 -746
  97. package/dist/commands/sources/schema-repair.js +98 -129
  98. package/dist/commands/sources/source-add.js +62 -12
  99. package/dist/commands/sources/source-manage.js +9 -2
  100. package/dist/commands/sources/stash-cli.js +24 -4
  101. package/dist/commands/tasks/explain.js +10 -13
  102. package/dist/commands/tasks/tasks-cli.js +12 -13
  103. package/dist/commands/tasks/tasks.js +350 -936
  104. package/dist/commands/tasks/validate.js +26 -24
  105. package/dist/commands/workflow/plan.js +22 -29
  106. package/dist/commands/workflow-cli.js +4 -4
  107. package/dist/core/adapter/adapters/akm-adapter.js +2 -1
  108. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  109. package/dist/core/adapter/adapters/akm-metadata.js +42 -12
  110. package/dist/core/adapter/adapters/akm-task-adapter.js +29 -8
  111. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  112. package/dist/core/adapter/execution-source.js +17 -29
  113. package/dist/core/asset/asset-placement.js +4 -13
  114. package/dist/core/asset/resolve-ref.js +1 -1
  115. package/dist/core/bundle-id.js +42 -5
  116. package/dist/core/bundle-rename.js +285 -0
  117. package/dist/core/config/config-io.js +1 -2
  118. package/dist/core/config/config-schema.js +9 -34
  119. package/dist/core/config/config-walker.js +1 -1
  120. package/dist/core/config/config.js +184 -111
  121. package/dist/core/config/engine-semantics.js +0 -2
  122. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  123. package/dist/core/config/schema/embedding.js +20 -5
  124. package/dist/core/config/schema/engines.js +5 -0
  125. package/dist/core/config/schema/execution.js +1 -1
  126. package/dist/core/config/schema/experimental.js +1 -1
  127. package/dist/core/config/schema/improve-processes.js +37 -135
  128. package/dist/core/config/schema/improve.js +4 -42
  129. package/dist/core/config/schema/index-config.js +9 -48
  130. package/dist/core/config/schema/scheduler.js +12 -12
  131. package/dist/core/config/schema/search.js +6 -22
  132. package/dist/core/env-secret-ref.js +0 -1
  133. package/dist/core/errors.js +8 -9
  134. package/dist/core/file-change.js +13 -5
  135. package/dist/core/file-lock.js +76 -173
  136. package/dist/core/improve-result.js +26 -7
  137. package/dist/core/improve-types.js +0 -1
  138. package/dist/core/logs-db.js +2 -2
  139. package/dist/core/loopback.js +7 -12
  140. package/dist/core/non-task-input.js +20 -0
  141. package/dist/core/parse.js +13 -16
  142. package/dist/core/paths.js +0 -31
  143. package/dist/core/redaction.js +109 -2
  144. package/dist/core/run-lock.js +2 -5
  145. package/dist/core/spawn-env.js +1 -1
  146. package/dist/core/state/migrations.js +123 -61
  147. package/dist/core/state-db-scope.js +2 -4
  148. package/dist/core/state-db.js +126 -692
  149. package/dist/core/time.js +0 -20
  150. package/dist/core/type-presentation.js +1 -9
  151. package/dist/core/write-source.js +294 -1005
  152. package/dist/execution/input-contract.js +1 -1
  153. package/dist/execution/resolved-request.js +135 -689
  154. package/dist/execution/source.js +63 -257
  155. package/dist/execution/target-ref.js +1 -1
  156. package/dist/indexer/bundle-identity-guard.js +2 -2
  157. package/dist/indexer/db/llm-cache.js +2 -2
  158. package/dist/indexer/ensure-index.js +46 -87
  159. package/dist/indexer/index-rebuild-lock.js +3 -11
  160. package/dist/indexer/index-writer-lock.js +8 -17
  161. package/dist/indexer/index-written-assets.js +141 -154
  162. package/dist/indexer/indexer.js +400 -1124
  163. package/dist/indexer/links/declared-links.js +90 -0
  164. package/dist/indexer/materialize-embeddings.js +60 -397
  165. package/dist/indexer/passes/memory-inference.js +81 -90
  166. package/dist/indexer/passes/metadata.js +132 -219
  167. package/dist/indexer/read-preflight.js +0 -7
  168. package/dist/indexer/scan/doc-to-entry.js +2 -3
  169. package/dist/indexer/scan/drain-dir.js +1 -1
  170. package/dist/indexer/search/db-search.js +190 -590
  171. package/dist/indexer/search/fts-query.js +30 -41
  172. package/dist/indexer/search/ranking.js +28 -154
  173. package/dist/indexer/search/search-attribution.js +12 -32
  174. package/dist/indexer/search/search-fields.js +11 -15
  175. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  176. package/dist/indexer/search/search-source.js +1 -4
  177. package/dist/indexer/usage/usage-events.js +36 -7
  178. package/dist/indexer/walk/walker.js +3 -4
  179. package/dist/integrations/agent/engine-fallback.js +23 -40
  180. package/dist/integrations/agent/engine-resolution.js +93 -183
  181. package/dist/integrations/agent/execution.js +507 -0
  182. package/dist/integrations/agent/model-map.js +28 -156
  183. package/dist/integrations/agent/request-lowering.js +66 -141
  184. package/dist/integrations/agent/runner-dispatch.js +143 -321
  185. package/dist/integrations/agent/runner.js +54 -14
  186. package/dist/integrations/lockfile.js +53 -101
  187. package/dist/llm/client.js +8 -10
  188. package/dist/llm/embedders/deterministic.js +2 -3
  189. package/dist/llm/embedders/profile.js +71 -0
  190. package/dist/llm/embedders/remote.js +11 -17
  191. package/dist/llm/feature-gate.js +0 -8
  192. package/dist/llm/index-passes.js +3 -5
  193. package/dist/llm/memory-infer.js +1 -2
  194. package/dist/llm/structured-call.js +5 -24
  195. package/dist/output/generic-render.js +23 -11
  196. package/dist/output/html-render.js +13 -10
  197. package/dist/output/render-registry.js +3 -32
  198. package/dist/output/shapes/helpers.js +25 -38
  199. package/dist/output/shapes/passthrough.js +1 -9
  200. package/dist/{indexer/graph/graph-types.js → output/text/bundle-rename.js} +4 -1
  201. package/dist/output/text/command-format.js +69 -31
  202. package/dist/output/text/helpers.js +1 -1
  203. package/dist/output/text/migrate.js +5 -14
  204. package/dist/output/text/proposal-format.js +48 -3
  205. package/dist/output/text/show-format.js +13 -17
  206. package/dist/output/text/workflow-format.js +0 -32
  207. package/dist/output/text.js +2 -0
  208. package/dist/registry/factory.js +4 -19
  209. package/dist/registry/network.js +66 -220
  210. package/dist/registry/providers/index.js +0 -2
  211. package/dist/registry/providers/skills-sh.js +3 -14
  212. package/dist/registry/providers/static-index.js +24 -26
  213. package/dist/registry/resolve.js +55 -131
  214. package/dist/scripts/akm-migrate-node.js +42938 -92375
  215. package/dist/scripts/akm-migrate.js +42930 -92365
  216. package/dist/setup/registry-stash-loader.js +4 -13
  217. package/dist/setup/semantic-assets.js +3 -44
  218. package/dist/setup/setup.js +1 -1
  219. package/dist/setup/steps/connection.js +5 -6
  220. package/dist/setup/steps/platforms.js +2 -2
  221. package/dist/setup/steps/tasks.js +25 -15
  222. package/dist/sources/provider-factory.js +17 -18
  223. package/dist/sources/providers/filesystem.js +2 -3
  224. package/dist/sources/providers/git-install.js +7 -1
  225. package/dist/sources/providers/git-provider.js +0 -3
  226. package/dist/sources/providers/git-stash.js +83 -21
  227. package/dist/sources/providers/npm.js +2 -4
  228. package/dist/sources/providers/provider-utils.js +5 -10
  229. package/dist/sources/providers/website.js +0 -2
  230. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  231. package/dist/sources/website-url.js +2 -2
  232. package/dist/storage/database.js +9 -35
  233. package/dist/storage/repositories/improve-ledger-repository.js +209 -0
  234. package/dist/storage/repositories/index-connection.js +39 -72
  235. package/dist/storage/repositories/index-entries-repository.js +88 -129
  236. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  237. package/dist/storage/repositories/index-entry-schema.js +101 -268
  238. package/dist/storage/repositories/index-fts-repository.js +86 -256
  239. package/dist/storage/repositories/index-links-repository.js +143 -0
  240. package/dist/storage/repositories/index-llm-cache-repository.js +7 -9
  241. package/dist/storage/repositories/index-meta-repository.js +6 -4
  242. package/dist/storage/repositories/index-schema.js +257 -325
  243. package/dist/storage/repositories/index-utility-repository.js +8 -29
  244. package/dist/storage/repositories/index-vec-repository.js +133 -414
  245. package/dist/storage/repositories/outcome-repository.js +2 -1
  246. package/dist/storage/repositories/proposals-repository.js +100 -0
  247. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  248. package/dist/storage/repositories/salience-repository.js +1 -19
  249. package/dist/storage/repositories/task-history-repository.js +26 -4
  250. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  251. package/dist/storage/sqlite-migrations.js +136 -0
  252. package/dist/storage/sqlite-pragmas.js +11 -9
  253. package/dist/storage/sqlite-transaction.js +170 -0
  254. package/dist/storage/state-db-integrity.js +34 -27
  255. package/dist/tasks/activation-config.js +134 -62
  256. package/dist/tasks/backends/cron.js +191 -302
  257. package/dist/tasks/backends/exec-utils.js +2 -5
  258. package/dist/tasks/backends/launchd.js +141 -748
  259. package/dist/tasks/backends/schtasks.js +119 -623
  260. package/dist/tasks/prepare/prepare-support.js +5 -15
  261. package/dist/tasks/prepare/prepare.js +0 -2
  262. package/dist/tasks/resolve-akm-bin.js +20 -79
  263. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  264. package/dist/tasks/run/load-task.js +1 -1
  265. package/dist/tasks/scheduler-binding.js +20 -238
  266. package/dist/tasks/scheduler-invocation.js +136 -244
  267. package/dist/tasks/scheduler-lock.js +53 -0
  268. package/dist/tasks/scheduler-sync.js +368 -679
  269. package/dist/tasks/source/parse-task-source.js +55 -9
  270. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  271. package/dist/tasks/source/task-to-v4.js +464 -88
  272. package/dist/workflows/authoring/authoring.js +3 -12
  273. package/dist/workflows/compile.js +211 -0
  274. package/dist/workflows/concurrency-policy.js +13 -74
  275. package/dist/workflows/exec/child-invocation.js +3 -17
  276. package/dist/workflows/exec/child-workflow.js +32 -141
  277. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  278. package/dist/workflows/exec/environment.js +98 -0
  279. package/dist/workflows/exec/exec-unit.js +33 -140
  280. package/dist/workflows/exec/frozen-judge.js +7 -59
  281. package/dist/workflows/exec/native-executor.js +82 -341
  282. package/dist/workflows/exec/param-secrets.js +29 -47
  283. package/dist/workflows/exec/run-workflow.js +154 -387
  284. package/dist/workflows/exec/scheduler.js +9 -36
  285. package/dist/workflows/exec/step-work.js +127 -430
  286. package/dist/workflows/exec/unit-dispatch.js +11 -63
  287. package/dist/workflows/exec/unit-writer.js +8 -52
  288. package/dist/workflows/exec/worktree.js +39 -273
  289. package/dist/workflows/freeze/child-output-references.js +4 -15
  290. package/dist/workflows/freeze/environment.js +99 -92
  291. package/dist/workflows/freeze/freeze.js +172 -0
  292. package/dist/workflows/freeze/step-values.js +19 -21
  293. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  294. package/dist/workflows/freeze/targets/command.js +10 -33
  295. package/dist/workflows/freeze/targets/script.js +5 -12
  296. package/dist/workflows/freeze/targets/shell.js +3 -6
  297. package/dist/workflows/freeze/targets/task.js +25 -80
  298. package/dist/workflows/freeze/task-bindings.js +20 -67
  299. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  300. package/dist/workflows/ir/params.js +6 -51
  301. package/dist/workflows/ir/plan-hash.js +2 -34
  302. package/dist/workflows/parser.js +140 -43
  303. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  304. package/dist/workflows/renderer.js +36 -69
  305. package/dist/workflows/resource-limits.js +12 -120
  306. package/dist/workflows/runtime/agent-identity.js +8 -40
  307. package/dist/workflows/runtime/run-outputs.js +3 -6
  308. package/dist/workflows/runtime/run-plan.js +316 -0
  309. package/dist/workflows/runtime/runs.js +48 -200
  310. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  311. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  312. package/dist/workflows/validate-summary.js +2 -7
  313. package/docs/integration/bundling-akm.md +49 -42
  314. package/docs/migration/README.md +1 -0
  315. package/docs/migration/release-notes/0.9.17.md +43 -0
  316. package/docs/migration/v0.9.1-to-v0.9.2.md +23 -7
  317. package/docs/reference/cli.md +229 -133
  318. package/docs/reference/configuration.md +71 -57
  319. package/docs/reference/data-and-telemetry.md +19 -21
  320. package/docs/reference/tasks.md +105 -39
  321. package/docs/reference/workflow-schema.md +14 -18
  322. package/docs/reference/workflows.md +6 -9
  323. package/package.json +1 -1
  324. package/schemas/akm-config.json +87 -754
  325. package/dist/assets/improve-strategies/graph-refresh.json +0 -15
  326. package/dist/assets/prompts/contradiction-judge.md +0 -33
  327. package/dist/assets/prompts/graph-extract-system.md +0 -1
  328. package/dist/assets/prompts/graph-extract-user-prompt.md +0 -35
  329. package/dist/assets/prompts/metadata-enhance-system.md +0 -1
  330. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +0 -4
  331. package/dist/commands/health/advisories.js +0 -150
  332. package/dist/commands/health/metrics.js +0 -329
  333. package/dist/commands/health/surfaces.js +0 -102
  334. package/dist/commands/improve/anti-collapse.js +0 -83
  335. package/dist/commands/improve/collapse-detector.js +0 -432
  336. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  337. package/dist/commands/improve/consolidate/merge.js +0 -146
  338. package/dist/commands/improve/distill/promote-memory.js +0 -329
  339. package/dist/commands/improve/distill/quality-gate.js +0 -500
  340. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  341. package/dist/commands/improve/proposal-envelope.js +0 -31
  342. package/dist/commands/improve/run-context.js +0 -123
  343. package/dist/commands/improve/shared.js +0 -21
  344. package/dist/commands/improve/source-identity.js +0 -28
  345. package/dist/commands/improve/triage.js +0 -96
  346. package/dist/commands/proposal/drain-policies.js +0 -151
  347. package/dist/commands/sources/update-transaction.js +0 -220
  348. package/dist/core/action-contributors.js +0 -28
  349. package/dist/core/config/config-version-shim.js +0 -101
  350. package/dist/core/fs-txn.js +0 -405
  351. package/dist/core/lexical-score.js +0 -25
  352. package/dist/core/maintenance-barrier.js +0 -167
  353. package/dist/execution/executable-identity.js +0 -105
  354. package/dist/execution/guarded-source.js +0 -427
  355. package/dist/indexer/db/graph-db.js +0 -444
  356. package/dist/indexer/graph/graph-boost.js +0 -427
  357. package/dist/indexer/graph/graph-dedup.js +0 -95
  358. package/dist/indexer/graph/graph-extraction.js +0 -1182
  359. package/dist/indexer/search/name-match.js +0 -35
  360. package/dist/indexer/search/ranking-contributors.js +0 -515
  361. package/dist/indexer/search/ranking-types.js +0 -4
  362. package/dist/indexer/walk/project-context.js +0 -192
  363. package/dist/integrations/agent/execution-cascade.js +0 -566
  364. package/dist/integrations/agent/execution-definitions.js +0 -202
  365. package/dist/integrations/agent/execution-lowering.js +0 -841
  366. package/dist/integrations/agent/execution-preparation.js +0 -98
  367. package/dist/integrations/agent/inline-execution.js +0 -74
  368. package/dist/llm/graph-extract.js +0 -872
  369. package/dist/llm/metadata-enhance.js +0 -96
  370. package/dist/registry/create-provider-registry.js +0 -29
  371. package/dist/registry/pinned-request-helper.js +0 -247
  372. package/dist/registry/pinned-transport.js +0 -717
  373. package/dist/sources/providers/index.js +0 -14
  374. package/dist/storage/engines/sqlite-migrations.js +0 -271
  375. package/dist/storage/repositories/canaries-repository.js +0 -107
  376. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  377. package/dist/storage/repositories/registry-cache.js +0 -113
  378. package/dist/tasks/scheduler-sync-preview.js +0 -52
  379. package/dist/tasks/source/task-to-v3.js +0 -507
  380. package/dist/workflows/freeze/resolve-steps.js +0 -86
  381. package/dist/workflows/freeze/source-freeze.js +0 -64
  382. package/dist/workflows/ir/compile.js +0 -321
  383. package/dist/workflows/ir/environment-v4.js +0 -330
  384. package/dist/workflows/ir/freeze-v4.js +0 -153
  385. package/dist/workflows/ir/schema-v4.js +0 -745
  386. package/dist/workflows/ir/schema.js +0 -354
  387. package/dist/workflows/program/schema.js +0 -78
  388. package/dist/workflows/runtime/checkin.js +0 -57
  389. package/dist/workflows/runtime/plan-classifier.js +0 -196
  390. package/dist/workflows/runtime/unit-checkin.js +0 -45
  391. package/dist/workflows/runtime/unit-phases.js +0 -20
  392. package/dist/workflows/schema.js +0 -4
  393. package/dist/workflows/source-ir/compile.js +0 -200
  394. package/dist/workflows/source-ir/program.js +0 -50
  395. package/dist/workflows/source-ir/result.js +0 -26
  396. package/dist/workflows/source-ir/schema.js +0 -786
  397. package/dist/workflows/source-ir/triggers.js +0 -79
  398. package/dist/workflows/source-ir/uses.js +0 -40
  399. package/dist/workflows/validator.js +0 -60
@@ -7,24 +7,17 @@ import { decodeTaskHistoryMetadata } from "../../storage/repositories/task-histo
7
7
  export function roundRate(value) {
8
8
  return Number(value.toFixed(4));
9
9
  }
10
- export function parseTaskMetadata(row) {
11
- const metadata = decodeTaskHistoryMetadata(row.metadata_json);
12
- return {
13
- ...(metadata.durationMs !== undefined ? { durationMs: metadata.durationMs } : {}),
14
- ...(metadata.detail ? { detail: metadata.detail } : {}),
15
- ...(metadata.engine !== undefined ? { engine: metadata.engine } : {}),
16
- };
17
- }
18
10
  /**
19
- * `parseTaskMetadata`, but per-row skip-and-warn instead of throwing (mirrors
20
- * `listStateProposals`). `decodeTaskHistoryMetadata` already tolerates
21
- * legacy/additive shapes; only genuine corruption reaches this catch, and a
22
- * corrupt row must degrade the metric (excluded, not fatal) rather than abort
23
- * the whole `akm health` computation.
11
+ * `task_history.metadata_json`'s `detail` field, decoded and skip-and-warn on
12
+ * corruption instead of throwing (mirrors `listStateProposals`).
13
+ * `decodeTaskHistoryMetadata` already tolerates legacy/additive shapes; only
14
+ * genuine corruption reaches this catch, and a corrupt row must degrade the
15
+ * metric (excluded, not fatal) rather than abort the whole `akm health`
16
+ * computation.
24
17
  */
25
18
  export function taskFailureDetail(row) {
26
19
  try {
27
- return parseTaskMetadata(row).detail;
20
+ return decodeTaskHistoryMetadata(row.metadata_json).detail ?? undefined;
28
21
  }
29
22
  catch (error) {
30
23
  const message = error instanceof Error ? error.message : String(error);
@@ -37,9 +30,8 @@ export function taskFailureDetail(row) {
37
30
  * that represents a prepared command (agent/LLM) result. `target_kind` is
38
31
  * read in the current (post-D8) vocabulary — the
39
32
  * `025-task-history-vocabulary-backfill` state migration rewrites every
40
- * legacy-vocabulary row (which stored the agent/LLM arm as `"prompt"` and
41
- * the native shell/script arm as `"command"`) before this ever runs against
42
- * it, so a `"command"` row here is unambiguously the agent/LLM arm.
33
+ * legacy-vocabulary row before this ever runs against it, so a `"command"`
34
+ * row here is unambiguously the agent/LLM arm.
43
35
  */
44
36
  export function isAgentTaskHistoryRow(row) {
45
37
  return row.target_kind === "command";
@@ -48,8 +40,7 @@ export function isAgentTaskHistoryRow(row) {
48
40
  * #943: reason-value breakdown for a set of agent (command-kind) task
49
41
  * failure rows — how much of the observed failures are `timeout` vs
50
42
  * `non_zero_exit` vs `spawn_failed` etc., so `akm health`'s `task-fail-rate`
51
- * advisory can say "timeout-dominant" from data rather than log grep. Keeps
52
- * the existing `AgentFailureReason` vocabulary verbatim (spawn.ts) — a
43
+ * advisory can say "timeout-dominant" from data rather than log grep. A
53
44
  * reason-per-row read failure (already warned by {@link taskFailureDetail})
54
45
  * still counts under `"unknown"` rather than being dropped, so the total
55
46
  * always equals `agentFailures.length`.
@@ -62,17 +53,35 @@ export function countAgentFailureReasons(agentFailures) {
62
53
  }
63
54
  return counts;
64
55
  }
65
- function createUnknownImproveMetrics() {
56
+ /**
57
+ * Decode one `improve_runs.result_json` envelope, warning once per row on
58
+ * failure (mirrors {@link taskFailureDetail}'s handling of the analogous
59
+ * `task_history` case) and returning `undefined` instead of throwing.
60
+ * Callers count the `undefined` case themselves (`resultRows.skipped.invalid`
61
+ * / `resultStatus: "invalid"`) so a decode failure is never silent — the
62
+ * warning names *why* (corrupt data, or a decoder too strict for a shape an
63
+ * older release legitimately wrote), the counters say *how many*.
64
+ */
65
+ function decodeImproveResultRow(row) {
66
+ try {
67
+ return decodeImproveResult(row.result_json).envelope;
68
+ }
69
+ catch (error) {
70
+ const message = error instanceof Error ? error.message : String(error);
71
+ console.warn(`[akm] Skipping unparseable improve_runs row in health metrics (id=${row.id}, started_at=${row.started_at}): ${message}`);
72
+ return undefined;
73
+ }
74
+ }
75
+ /** A zeroed accumulator — also what health reports when it could not read state.db at all (#791). */
76
+ export function emptyImproveMetrics() {
66
77
  return {
67
78
  invoked: 0,
68
79
  completed: 0,
69
80
  skipped: 0,
70
81
  skipReasons: {},
71
82
  resultRows: { total: 0, included: 0, skipped: { invalid: 0 } },
72
- plannedRefs: 0,
73
- strategyFilteredRefs: 0,
74
83
  actions: {
75
- reflect: { ok: 0, failed: 0, cooldown: 0, skipped: 0, guardRejected: 0, skippedByReason: {} },
84
+ reflect: { ok: 0, failed: 0, cooldown: 0, skipped: 0 },
76
85
  distill: {
77
86
  queued: 0,
78
87
  llmFailed: 0,
@@ -81,119 +90,30 @@ function createUnknownImproveMetrics() {
81
90
  configDisabled: 0,
82
91
  skipped: 0,
83
92
  skippedByReason: {},
84
- deferred: 0,
85
- deferredByReason: {},
86
93
  },
87
94
  memoryPrune: 0,
88
95
  memoryInference: 0,
89
- graphExtraction: 0,
90
96
  error: 0,
91
97
  },
92
98
  autoAccept: { promoted: 0, validationFailed: 0 },
93
- reflectsWithErrorContext: 0,
94
- coverageGapCount: 0,
95
- deadUrlCount: 0,
96
- deadUrlsChecked: 0,
97
- deadUrlsTotal: 0,
98
- deadUrlsSkipped: 0,
99
99
  memorySummary: { eligible: 0, derived: 0 },
100
- memoryCleanup: {
101
- pruneCandidates: 0,
102
- contradictionCandidates: 0,
103
- beliefStateTransitions: 0,
104
- consolidationCandidates: 0,
105
- archived: 0,
106
- warnings: 0,
107
- },
108
100
  consolidation: {
109
- ran: false,
110
101
  processed: 0,
111
102
  promoted: 0,
112
103
  merged: 0,
113
104
  deleted: 0,
114
105
  contradicted: 0,
115
106
  judgedNoAction: 0,
116
- mergedSecondaries: 0,
117
- failedChunkMemories: 0,
118
- skipReasons: {},
119
107
  failedChunks: 0,
120
108
  totalChunks: 0,
121
109
  durationMs: 0,
122
110
  },
123
- memoryInference: {
124
- ran: false,
125
- considered: 0,
126
- cacheHits: 0,
127
- retryAttempts: 0,
128
- freshAttempts: 0,
129
- splitParents: 0,
130
- written: 0,
131
- skippedNoFacts: 0,
132
- skippedChildExists: 0,
133
- skippedAborted: 0,
134
- unaccounted: 0,
135
- htmlErrorCount: 0,
136
- yieldRate: 0,
137
- durationMs: 0,
138
- },
139
- graphExtraction: {
140
- ran: false,
141
- extractedFiles: 0,
142
- consideredFiles: 0,
143
- entities: 0,
144
- relations: 0,
145
- extractionCoverage: 0,
146
- cacheHits: 0,
147
- cacheMisses: 0,
148
- cacheHitRate: 0,
149
- truncations: 0,
150
- failures: 0,
151
- htmlErrors: 0,
152
- retryAttempts: 0,
153
- nonArrayBatchFailures: 0,
154
- durationMs: 0,
155
- },
156
- sessionExtraction: {
157
- ran: false,
158
- sessionsScanned: 0,
159
- sessionsExtracted: 0,
160
- sessionsSkipped: 0,
161
- proposalsCreated: 0,
162
- warnings: 0,
163
- durationMs: 0,
164
- },
165
- wallTime: {
166
- count: 0,
167
- medianMs: 0,
168
- p95Ms: 0,
169
- minMs: 0,
170
- maxMs: 0,
171
- byPhase: {
172
- consolidation: { count: 0, totalMs: 0, medianMs: 0, p95Ms: 0 },
173
- memoryInference: { count: 0, totalMs: 0, medianMs: 0, p95Ms: 0 },
174
- graphExtraction: { count: 0, totalMs: 0, medianMs: 0, p95Ms: 0 },
175
- },
176
- },
177
- perfTelemetry: {
178
- dedupPoolSize: 0,
179
- llmPoolSize: 0,
180
- embedMs: 0,
181
- embedCacheHits: 0,
182
- embedCacheMisses: 0,
183
- overBudgetRuns: 0,
184
- runsWithTelemetry: 0,
185
- },
186
- coverage: {
187
- rate: Number.NaN,
188
- eligibleFraction: Number.NaN,
189
- acceptedProposals: 0,
190
- distinctRefs: 0,
191
- churnRatio: Number.NaN,
192
- totalAssets: 0,
193
- },
111
+ memoryInference: { considered: 0, freshAttempts: 0, written: 0, skippedNoFacts: 0, yieldRate: 0, durationMs: 0 },
112
+ wallTime: { medianMs: 0, p95Ms: 0 },
113
+ coverage: { acceptedProposals: 0, distinctRefs: 0 },
194
114
  };
195
115
  }
196
- export function toFiniteNumber(value) {
116
+ function toFiniteNumber(value) {
197
117
  if (typeof value === "number" && Number.isFinite(value))
198
118
  return value;
199
119
  if (typeof value === "string" && value.trim()) {
@@ -203,47 +123,9 @@ export function toFiniteNumber(value) {
203
123
  }
204
124
  return 0;
205
125
  }
206
- /**
207
- * Event-derived metrics. Only `completed` and skipReasons/invoked are sourced
208
- * from events in v2 — the richer fields come from {@link summarizeImproveRuns}.
209
- * The function still receives `improve_completed` events so that the completed
210
- * count reflects the canonical event stream (it lines up 1:1 with improve_runs
211
- * rows in practice, but the events table remains the system-of-record for the
212
- * existence of a run).
213
- */
214
- export function summarizeImproveCompleted(events) {
215
- const metrics = createUnknownImproveMetrics();
216
- metrics.completed = events.length;
217
- return metrics;
218
- }
219
- /**
220
- * Read the machine-readable reason from a distill `outcome: "skipped"` result.
221
- */
222
- function classifyDistillSkipReason(r) {
223
- const explicitReason = typeof r?.skipReason === "string" ? r.skipReason : undefined;
224
- return explicitReason || "unknown";
225
- }
226
- // ── projectRunMetrics phase helpers (chunk-9 WI-9.5c; file-level decompose of
227
- // the single-envelope projection — each helper mutates its own disjoint
228
- // subtree of the accumulator from the raw envelope, so call order does not
229
- // matter; kept in envelope-field order for readability) ──────────────────────
230
- /** plannedRefs / strategyFilteredRefs ref-count accounting. */
231
- function applyPlannedRefs(metrics, result) {
232
- // plannedRefs (array of {ref, reason})
233
- const plannedRefs = result.plannedRefs;
234
- if (Array.isArray(plannedRefs))
235
- metrics.plannedRefs += plannedRefs.length;
236
- // strategyFilteredRefs (array of {ref, reason}) — 2026-05-27: pre-filter
237
- // bucket from `collectEligibleRefs` so the metric reflects work the
238
- // planner dropped before signal-delta / per-pass dispatch.
239
- const strategyFilteredRefs = result.strategyFilteredRefs;
240
- if (Array.isArray(strategyFilteredRefs))
241
- metrics.strategyFilteredRefs += strategyFilteredRefs.length;
242
- }
243
- /** One `actions[]` entry → the matching counter bucket (see ImproveHealthMetrics.actions jsdoc for the taxonomy). */
126
+ /** One `actions[]` entry → its counter bucket; unknown modes/outcomes are not counted. */
244
127
  function applyAction(metrics, action) {
245
- const mode = typeof action.mode === "string" ? action.mode : "";
246
- switch (mode) {
128
+ switch (typeof action.mode === "string" ? action.mode : "") {
247
129
  case "reflect":
248
130
  metrics.actions.reflect.ok += 1;
249
131
  break;
@@ -253,20 +135,12 @@ function applyAction(metrics, action) {
253
135
  case "reflect-cooldown":
254
136
  metrics.actions.reflect.cooldown += 1;
255
137
  break;
256
- case "reflect-skipped": {
138
+ case "reflect-skipped":
257
139
  metrics.actions.reflect.skipped += 1;
258
- const r = action.result;
259
- const reason = typeof r?.reason === "string" && r.reason.trim() ? r.reason : "unknown";
260
- metrics.actions.reflect.skippedByReason[reason] = (metrics.actions.reflect.skippedByReason[reason] ?? 0) + 1;
261
- break;
262
- }
263
- case "reflect-guard-rejected":
264
- metrics.actions.reflect.guardRejected += 1;
265
140
  break;
266
141
  case "distill": {
267
- const r = action.result;
268
- const outcome = typeof r?.outcome === "string" ? r.outcome : "";
269
- switch (outcome) {
142
+ const result = action.result;
143
+ switch (typeof result?.outcome === "string" ? result.outcome : "") {
270
144
  case "queued":
271
145
  metrics.actions.distill.queued += 1;
272
146
  break;
@@ -283,21 +157,6 @@ function applyAction(metrics, action) {
283
157
  case "config_disabled":
284
158
  metrics.actions.distill.configDisabled += 1;
285
159
  break;
286
- case "skipped": {
287
- // Previously dropped on the floor. The four sub-paths that emit
288
- // `outcome: "skipped"` (see distill.ts:893, 1024, 1120, 1576):
289
- // - recursive_lesson_input (type guard refused a lesson input)
290
- // - conflict_noop (LLM resolved destination conflict as NOOP)
291
- // - proposal-skipped cooldown / dedup at persistence
292
- // 465 events/7d in the user's live stack. The result message
293
- // typically encodes the reason; we also accept an explicit
294
- // `skipReason` field when downstream code sets it.
295
- metrics.actions.distill.deferred += 1;
296
- const reason = classifyDistillSkipReason(r);
297
- metrics.actions.distill.deferredByReason[reason] =
298
- (metrics.actions.distill.deferredByReason[reason] ?? 0) + 1;
299
- break;
300
- }
301
160
  default:
302
161
  break;
303
162
  }
@@ -309,267 +168,79 @@ function applyAction(metrics, action) {
309
168
  case "memory-inference":
310
169
  metrics.actions.memoryInference += 1;
311
170
  break;
312
- case "graph-extraction":
313
- metrics.actions.graphExtraction += 1;
314
- break;
315
171
  case "error":
316
172
  metrics.actions.error += 1;
317
173
  break;
174
+ default:
175
+ break;
318
176
  }
319
177
  }
320
- /** actions: split reflect / distill by outcome, count others. */
321
- function applyActionsList(metrics, result) {
322
- const actions = result.actions;
323
- if (!Array.isArray(actions))
324
- return;
325
- for (const action of actions) {
326
- applyAction(metrics, action);
178
+ /** Project one `improve_runs.result_json` envelope into a single-run accumulator. */
179
+ function projectRunMetrics(result) {
180
+ const metrics = emptyImproveMetrics();
181
+ if (Array.isArray(result.actions)) {
182
+ for (const action of result.actions)
183
+ applyAction(metrics, action);
327
184
  }
328
- }
329
- /**
330
- * Read the bounded `distillSkipped` aggregate into the corresponding total and
331
- * per-reason health metrics.
332
- */
333
- function applyDistillSkippedAggregate(metrics, result) {
334
185
  const distillSkipped = result.distillSkipped;
335
- if (!distillSkipped || typeof distillSkipped !== "object")
336
- return;
337
- metrics.actions.distill.skipped += toFiniteNumber(distillSkipped.total);
338
- const byReason = distillSkipped.byReason;
339
- if (byReason && typeof byReason === "object") {
340
- for (const [reason, count] of Object.entries(byReason)) {
186
+ if (distillSkipped && typeof distillSkipped === "object") {
187
+ metrics.actions.distill.skipped += toFiniteNumber(distillSkipped.total);
188
+ for (const [reason, count] of Object.entries(distillSkipped.byReason ?? {})) {
341
189
  metrics.actions.distill.skippedByReason[reason] =
342
190
  (metrics.actions.distill.skippedByReason[reason] ?? 0) + toFiniteNumber(count);
343
191
  }
344
192
  }
345
- }
346
- /** autoAccept + the small envelope-level counters (reflectsWithErrorContext, coverageGaps, deadUrls). */
347
- function applyMiscCounters(metrics, result) {
348
193
  metrics.autoAccept.promoted += toFiniteNumber(result.gateAutoAcceptedCount);
349
194
  metrics.autoAccept.validationFailed += toFiniteNumber(result.gateAutoAcceptFailedCount);
350
- metrics.reflectsWithErrorContext += toFiniteNumber(result.reflectsWithErrorContext);
351
- if (Array.isArray(result.coverageGaps))
352
- metrics.coverageGapCount += result.coverageGaps.length;
353
- if (Array.isArray(result.deadUrls))
354
- metrics.deadUrlCount += result.deadUrls.length;
355
- const deadUrlCoverage = result.deadUrlCoverage;
356
- if (deadUrlCoverage && typeof deadUrlCoverage === "object") {
357
- metrics.deadUrlsChecked += toFiniteNumber(deadUrlCoverage.checked);
358
- metrics.deadUrlsTotal += toFiniteNumber(deadUrlCoverage.total);
359
- metrics.deadUrlsSkipped += toFiniteNumber(deadUrlCoverage.skipped);
360
- }
361
- }
362
- function applyMemorySummary(metrics, result) {
363
195
  const memorySummary = result.memorySummary;
364
- if (!memorySummary)
365
- return;
366
- metrics.memorySummary.eligible += toFiniteNumber(memorySummary.eligible);
367
- metrics.memorySummary.derived += toFiniteNumber(memorySummary.derived);
368
- }
369
- function applyMemoryCleanup(metrics, result) {
370
- const memoryCleanup = result.memoryCleanup;
371
- if (!memoryCleanup)
372
- return;
373
- if (Array.isArray(memoryCleanup.pruneCandidates))
374
- metrics.memoryCleanup.pruneCandidates += memoryCleanup.pruneCandidates.length;
375
- if (Array.isArray(memoryCleanup.contradictionCandidates))
376
- metrics.memoryCleanup.contradictionCandidates += memoryCleanup.contradictionCandidates.length;
377
- if (Array.isArray(memoryCleanup.beliefStateTransitions))
378
- metrics.memoryCleanup.beliefStateTransitions += memoryCleanup.beliefStateTransitions.length;
379
- if (Array.isArray(memoryCleanup.consolidationCandidates))
380
- metrics.memoryCleanup.consolidationCandidates += memoryCleanup.consolidationCandidates.length;
381
- if (Array.isArray(memoryCleanup.archived))
382
- metrics.memoryCleanup.archived += memoryCleanup.archived.length;
383
- if (Array.isArray(memoryCleanup.warnings))
384
- metrics.memoryCleanup.warnings += memoryCleanup.warnings.length;
385
- }
386
- function applyConsolidation(metrics, result) {
387
- const consolidation = result.consolidation;
388
- if (!consolidation)
389
- return;
390
- metrics.consolidation.processed += toFiniteNumber(consolidation.processed);
391
- metrics.consolidation.merged += toFiniteNumber(consolidation.merged);
392
- metrics.consolidation.deleted += toFiniteNumber(consolidation.deleted);
393
- metrics.consolidation.contradicted += toFiniteNumber(consolidation.contradicted);
394
- if (Array.isArray(consolidation.promoted))
395
- metrics.consolidation.promoted += consolidation.promoted.length;
396
- metrics.consolidation.failedChunks += toFiniteNumber(consolidation.failedChunks);
397
- metrics.consolidation.totalChunks += toFiniteNumber(consolidation.totalChunks);
398
- metrics.consolidation.durationMs += toFiniteNumber(consolidation.durationMs);
399
- metrics.consolidation.judgedNoAction += toFiniteNumber(consolidation.judgedNoAction);
400
- metrics.consolidation.mergedSecondaries += toFiniteNumber(consolidation.mergedSecondaries);
401
- metrics.consolidation.failedChunkMemories += toFiniteNumber(consolidation.failedChunkMemories);
402
- // Structured emitter (new on this branch): consolidate.ts now pushes
403
- // per-ref grouped `{ref, skips: [{op, reason}]}` entries to `skipReasons`
404
- // for every deterministic post-LLM rejection. Each ref appears once but
405
- // may carry multiple skips; aggregate every reason. Pre-fix envelopes have
406
- // neither field, so be defensive.
407
- const skipReasons = consolidation.skipReasons;
408
- if (Array.isArray(skipReasons)) {
409
- for (const entry of skipReasons) {
410
- if (!entry || typeof entry !== "object")
411
- continue;
412
- const skips = entry.skips;
413
- if (!Array.isArray(skips))
414
- continue;
415
- for (const skip of skips) {
416
- if (!skip || typeof skip !== "object")
417
- continue;
418
- const reason = skip.reason;
419
- if (typeof reason !== "string" || !reason.trim())
420
- continue;
421
- metrics.consolidation.skipReasons[reason] = (metrics.consolidation.skipReasons[reason] ?? 0) + 1;
422
- }
423
- }
196
+ if (memorySummary) {
197
+ metrics.memorySummary.eligible += toFiniteNumber(memorySummary.eligible);
198
+ metrics.memorySummary.derived += toFiniteNumber(memorySummary.derived);
424
199
  }
425
- // WS-5: extract perf telemetry from the consolidation envelope.
426
- // Pre-WS-5 envelopes lack `perfTelemetry`; be defensive.
427
- const perf = consolidation.perfTelemetry;
428
- if (perf) {
429
- metrics.perfTelemetry.runsWithTelemetry += 1;
430
- metrics.perfTelemetry.dedupPoolSize += toFiniteNumber(perf.dedupPoolSize);
431
- metrics.perfTelemetry.llmPoolSize += toFiniteNumber(perf.llmPoolSize);
432
- metrics.perfTelemetry.embedMs += toFiniteNumber(perf.embedMs);
433
- metrics.perfTelemetry.embedCacheHits += toFiniteNumber(perf.embedCacheHits);
434
- metrics.perfTelemetry.embedCacheMisses += toFiniteNumber(perf.embedCacheMisses);
435
- const budgetFrac = toFiniteNumber(perf.estimatedBudgetFractionUsed);
436
- if (budgetFrac > 1.0)
437
- metrics.perfTelemetry.overBudgetRuns += 1;
200
+ const consolidation = result.consolidation;
201
+ if (consolidation) {
202
+ const cons = metrics.consolidation;
203
+ cons.processed += toFiniteNumber(consolidation.processed);
204
+ if (Array.isArray(consolidation.promoted))
205
+ cons.promoted += consolidation.promoted.length;
206
+ cons.merged += toFiniteNumber(consolidation.merged);
207
+ cons.deleted += toFiniteNumber(consolidation.deleted);
208
+ cons.contradicted += toFiniteNumber(consolidation.contradicted);
209
+ cons.judgedNoAction += toFiniteNumber(consolidation.judgedNoAction);
210
+ cons.failedChunks += toFiniteNumber(consolidation.failedChunks);
211
+ cons.totalChunks += toFiniteNumber(consolidation.totalChunks);
212
+ cons.durationMs += toFiniteNumber(consolidation.durationMs);
438
213
  }
439
- }
440
- function applyMemoryInference(metrics, result) {
441
214
  const memoryInference = result.memoryInference;
442
215
  if (memoryInference) {
443
216
  const considered = toFiniteNumber(memoryInference.considered);
444
- const writtenFacts = toFiniteNumber(memoryInference.writtenFacts);
445
- metrics.memoryInference.considered += considered;
446
- metrics.memoryInference.cacheHits += toFiniteNumber(memoryInference.cacheHits);
447
- metrics.memoryInference.retryAttempts += toFiniteNumber(memoryInference.retryAttempts);
448
- metrics.memoryInference.splitParents += toFiniteNumber(memoryInference.splitParents);
449
- metrics.memoryInference.written += writtenFacts;
450
- metrics.memoryInference.skippedNoFacts += toFiniteNumber(memoryInference.skippedNoFacts);
451
- metrics.memoryInference.skippedChildExists += toFiniteNumber(memoryInference.skippedChildExists);
452
- metrics.memoryInference.skippedAborted += toFiniteNumber(memoryInference.skippedAborted);
453
- metrics.memoryInference.unaccounted += toFiniteNumber(memoryInference.unaccounted);
454
- metrics.memoryInference.htmlErrorCount += toFiniteNumber(memoryInference.htmlErrorCount);
217
+ const mi = metrics.memoryInference;
218
+ mi.considered += considered;
219
+ // Cache hits and budget-aborted records never reached the LLM; excluding
220
+ // them keeps the yield rate a statement about real inference attempts.
221
+ mi.freshAttempts += Math.max(0, considered - toFiniteNumber(memoryInference.cacheHits) - toFiniteNumber(memoryInference.skippedAborted));
222
+ mi.written += toFiniteNumber(memoryInference.writtenFacts);
223
+ mi.skippedNoFacts += toFiniteNumber(memoryInference.skippedNoFacts);
455
224
  }
456
225
  metrics.memoryInference.durationMs += toFiniteNumber(result.memoryInferenceDurationMs);
457
- }
458
- function applyGraphExtraction(metrics, result) {
459
- const graphExtraction = result.graphExtraction;
460
- if (graphExtraction) {
461
- const quality = graphExtraction.quality;
462
- if (quality) {
463
- metrics.graphExtraction.extractedFiles += toFiniteNumber(quality.extractedFiles);
464
- metrics.graphExtraction.consideredFiles += toFiniteNumber(quality.consideredFiles);
465
- }
466
- metrics.graphExtraction.entities += toFiniteNumber(graphExtraction.totalEntities);
467
- metrics.graphExtraction.relations += toFiniteNumber(graphExtraction.totalRelations);
468
- const telemetry = graphExtraction.telemetry;
469
- if (telemetry) {
470
- metrics.graphExtraction.cacheHits += toFiniteNumber(telemetry.cacheHits);
471
- metrics.graphExtraction.cacheMisses += toFiniteNumber(telemetry.cacheMisses);
472
- metrics.graphExtraction.truncations += toFiniteNumber(telemetry.truncationCount);
473
- metrics.graphExtraction.failures += toFiniteNumber(telemetry.failureCount);
474
- metrics.graphExtraction.htmlErrors += toFiniteNumber(telemetry.htmlErrorCount);
475
- metrics.graphExtraction.retryAttempts += toFiniteNumber(telemetry.retryAttempts);
476
- metrics.graphExtraction.nonArrayBatchFailures += toFiniteNumber(telemetry.nonArrayBatchFailures);
477
- }
478
- }
479
- metrics.graphExtraction.durationMs += toFiniteNumber(result.graphExtractionDurationMs);
480
- }
481
- function applySessionExtraction(metrics, result) {
482
- if (!Array.isArray(result.extract))
483
- return;
484
- for (const e of result.extract) {
485
- metrics.sessionExtraction.sessionsScanned += toFiniteNumber(e.sessionsProcessed);
486
- metrics.sessionExtraction.sessionsSkipped += toFiniteNumber(e.sessionsSkipped);
487
- if (Array.isArray(e.sessions)) {
488
- metrics.sessionExtraction.sessionsExtracted += e.sessions.filter((s) => Array.isArray(s.proposalIds) && s.proposalIds.length > 0).length;
489
- }
490
- metrics.sessionExtraction.proposalsCreated += Array.isArray(e.proposals) ? e.proposals.length : 0;
491
- metrics.sessionExtraction.warnings += Array.isArray(e.warnings) ? e.warnings.length : 0;
492
- metrics.sessionExtraction.durationMs += toFiniteNumber(e.durationMs);
493
- }
494
- }
495
- /**
496
- * Project a single `improve_runs.result_json` envelope into an accumulator-shaped
497
- * ImproveHealthMetrics. The aggregator merges these per-row metrics into one
498
- * window-level metric. File-level decomposed (chunk-9 WI-9.5c) into one
499
- * `apply*` helper per envelope section above; each mutates its own disjoint
500
- * subtree of the accumulator so the call order below is not load-bearing.
501
- */
502
- function projectRunMetrics(result) {
503
- const metrics = createUnknownImproveMetrics();
504
- applyPlannedRefs(metrics, result);
505
- applyActionsList(metrics, result);
506
- applyDistillSkippedAggregate(metrics, result);
507
- applyMiscCounters(metrics, result);
508
- applyMemorySummary(metrics, result);
509
- applyMemoryCleanup(metrics, result);
510
- applyConsolidation(metrics, result);
511
- applyMemoryInference(metrics, result);
512
- applyGraphExtraction(metrics, result);
513
- applySessionExtraction(metrics, result);
514
226
  return metrics;
515
227
  }
516
- /**
517
- * Finalize derived flags and rates on an accumulator. Used both for the
518
- * window-level aggregate and for each per-run row in --detail per-run mode
519
- * so the single-row metrics still expose `ran` / `yieldRate` / `cacheHitRate`.
520
- */
228
+ /** Derived rates on an accumulator (window aggregate or single run). */
521
229
  function finalizeImproveMetrics(metrics) {
522
- metrics.consolidation.ran =
523
- metrics.consolidation.processed > 0 ||
524
- metrics.consolidation.durationMs > 0 ||
525
- metrics.consolidation.promoted > 0 ||
526
- metrics.consolidation.merged > 0 ||
527
- metrics.consolidation.deleted > 0 ||
528
- metrics.consolidation.contradicted > 0 ||
529
- metrics.consolidation.totalChunks > 0;
530
- metrics.memoryInference.ran =
531
- metrics.memoryInference.considered > 0 ||
532
- metrics.memoryInference.written > 0 ||
533
- metrics.memoryInference.durationMs > 0;
534
- // Yield denominator excludes cache hits and aborted records.
535
- metrics.memoryInference.freshAttempts = Math.max(0, metrics.memoryInference.considered - metrics.memoryInference.cacheHits - metrics.memoryInference.skippedAborted);
536
- metrics.memoryInference.yieldRate =
537
- metrics.memoryInference.freshAttempts > 0
538
- ? roundRate(metrics.memoryInference.written / metrics.memoryInference.freshAttempts)
539
- : 0;
540
- metrics.graphExtraction.ran =
541
- metrics.graphExtraction.extractedFiles > 0 ||
542
- metrics.graphExtraction.entities > 0 ||
543
- metrics.graphExtraction.durationMs > 0;
544
- const cacheTotal = metrics.graphExtraction.cacheHits + metrics.graphExtraction.cacheMisses;
545
- metrics.graphExtraction.cacheHitRate = cacheTotal > 0 ? roundRate(metrics.graphExtraction.cacheHits / cacheTotal) : 0;
546
- metrics.graphExtraction.extractionCoverage =
547
- metrics.graphExtraction.consideredFiles > 0
548
- ? roundRate(metrics.graphExtraction.extractedFiles / metrics.graphExtraction.consideredFiles)
549
- : 0;
550
- metrics.sessionExtraction.ran =
551
- metrics.sessionExtraction.sessionsScanned > 0 ||
552
- metrics.sessionExtraction.proposalsCreated > 0 ||
553
- metrics.sessionExtraction.durationMs > 0;
230
+ const mi = metrics.memoryInference;
231
+ mi.yieldRate = mi.freshAttempts > 0 ? roundRate(mi.written / mi.freshAttempts) : 0;
554
232
  }
555
233
  /**
556
- * Merge per-row metrics from `src` into accumulator `dst`. All numeric fields
557
- * are additive; cumulative rates are recomputed by finalizeImproveMetrics.
234
+ * Merge per-run metrics from `src` into accumulator `dst`. Every counter is
235
+ * additive; `memorySummary` is a whole-stash snapshot and is deliberately NOT
236
+ * merged (summing it across runs inflated it ~N× — the 1.2M-eligible bug), and
237
+ * the derived rate is recomputed by {@link finalizeImproveMetrics}.
558
238
  */
559
239
  function mergeImproveMetrics(dst, src) {
560
- dst.plannedRefs += src.plannedRefs;
561
- // strategyFilteredRefs is the count of refs the planner drops up-front for the
562
- // active strategy — recomputed against the (stable) stash every run, so it is a
563
- // snapshot, NOT a per-run increment. Summing it re-counts the same refs each
564
- // run (the ~2.4M bug). Set from the most recent run in summarizeImproveRuns.
565
240
  dst.actions.reflect.ok += src.actions.reflect.ok;
566
241
  dst.actions.reflect.failed += src.actions.reflect.failed;
567
242
  dst.actions.reflect.cooldown += src.actions.reflect.cooldown;
568
243
  dst.actions.reflect.skipped += src.actions.reflect.skipped;
569
- dst.actions.reflect.guardRejected += src.actions.reflect.guardRejected;
570
- for (const [reason, count] of Object.entries(src.actions.reflect.skippedByReason)) {
571
- dst.actions.reflect.skippedByReason[reason] = (dst.actions.reflect.skippedByReason[reason] ?? 0) + count;
572
- }
573
244
  dst.actions.distill.queued += src.actions.distill.queued;
574
245
  dst.actions.distill.llmFailed += src.actions.distill.llmFailed;
575
246
  dst.actions.distill.judgeRejected += src.actions.distill.judgeRejected;
@@ -579,86 +250,25 @@ function mergeImproveMetrics(dst, src) {
579
250
  for (const [reason, count] of Object.entries(src.actions.distill.skippedByReason)) {
580
251
  dst.actions.distill.skippedByReason[reason] = (dst.actions.distill.skippedByReason[reason] ?? 0) + count;
581
252
  }
582
- dst.actions.distill.deferred += src.actions.distill.deferred;
583
- for (const [reason, count] of Object.entries(src.actions.distill.deferredByReason)) {
584
- dst.actions.distill.deferredByReason[reason] = (dst.actions.distill.deferredByReason[reason] ?? 0) + count;
585
- }
586
253
  dst.actions.memoryPrune += src.actions.memoryPrune;
587
254
  dst.actions.memoryInference += src.actions.memoryInference;
588
- dst.actions.graphExtraction += src.actions.graphExtraction;
589
255
  dst.actions.error += src.actions.error;
590
256
  dst.autoAccept.promoted += src.autoAccept.promoted;
591
257
  dst.autoAccept.validationFailed += src.autoAccept.validationFailed;
592
- dst.reflectsWithErrorContext += src.reflectsWithErrorContext;
593
- dst.coverageGapCount += src.coverageGapCount;
594
- dst.deadUrlCount += src.deadUrlCount;
595
- dst.deadUrlsChecked += src.deadUrlsChecked;
596
- dst.deadUrlsTotal += src.deadUrlsTotal;
597
- dst.deadUrlsSkipped += src.deadUrlsSkipped;
598
- // NOTE: memorySummary (derived/eligible) is a WHOLE-STASH snapshot recorded on
599
- // every run, NOT a per-run increment — summing it across the window inflates
600
- // it ~N× (the 1.2M-eligible bug). It is set from the most recent run in
601
- // summarizeImproveRuns instead, so it is intentionally not merged here.
602
- dst.memoryCleanup.pruneCandidates += src.memoryCleanup.pruneCandidates;
603
- dst.memoryCleanup.contradictionCandidates += src.memoryCleanup.contradictionCandidates;
604
- dst.memoryCleanup.beliefStateTransitions += src.memoryCleanup.beliefStateTransitions;
605
- dst.memoryCleanup.consolidationCandidates += src.memoryCleanup.consolidationCandidates;
606
- dst.memoryCleanup.archived += src.memoryCleanup.archived;
607
- dst.memoryCleanup.warnings += src.memoryCleanup.warnings;
608
258
  dst.consolidation.processed += src.consolidation.processed;
609
259
  dst.consolidation.promoted += src.consolidation.promoted;
610
260
  dst.consolidation.merged += src.consolidation.merged;
611
261
  dst.consolidation.deleted += src.consolidation.deleted;
612
262
  dst.consolidation.contradicted += src.consolidation.contradicted;
263
+ dst.consolidation.judgedNoAction += src.consolidation.judgedNoAction;
613
264
  dst.consolidation.failedChunks += src.consolidation.failedChunks;
614
265
  dst.consolidation.totalChunks += src.consolidation.totalChunks;
615
266
  dst.consolidation.durationMs += src.consolidation.durationMs;
616
- dst.consolidation.judgedNoAction += src.consolidation.judgedNoAction;
617
- dst.consolidation.mergedSecondaries += src.consolidation.mergedSecondaries;
618
- dst.consolidation.failedChunkMemories += src.consolidation.failedChunkMemories;
619
- for (const [reason, count] of Object.entries(src.consolidation.skipReasons)) {
620
- dst.consolidation.skipReasons[reason] = (dst.consolidation.skipReasons[reason] ?? 0) + count;
621
- }
622
267
  dst.memoryInference.considered += src.memoryInference.considered;
623
- dst.memoryInference.cacheHits += src.memoryInference.cacheHits;
624
- dst.memoryInference.splitParents += src.memoryInference.splitParents;
268
+ dst.memoryInference.freshAttempts += src.memoryInference.freshAttempts;
625
269
  dst.memoryInference.written += src.memoryInference.written;
626
270
  dst.memoryInference.skippedNoFacts += src.memoryInference.skippedNoFacts;
627
- dst.memoryInference.skippedChildExists += src.memoryInference.skippedChildExists;
628
- dst.memoryInference.skippedAborted += src.memoryInference.skippedAborted;
629
- dst.memoryInference.unaccounted += src.memoryInference.unaccounted;
630
- dst.memoryInference.htmlErrorCount += src.memoryInference.htmlErrorCount;
631
- dst.memoryInference.retryAttempts += src.memoryInference.retryAttempts;
632
271
  dst.memoryInference.durationMs += src.memoryInference.durationMs;
633
- dst.graphExtraction.extractedFiles += src.graphExtraction.extractedFiles;
634
- dst.graphExtraction.consideredFiles += src.graphExtraction.consideredFiles;
635
- dst.graphExtraction.entities += src.graphExtraction.entities;
636
- dst.graphExtraction.relations += src.graphExtraction.relations;
637
- dst.graphExtraction.cacheHits += src.graphExtraction.cacheHits;
638
- dst.graphExtraction.cacheMisses += src.graphExtraction.cacheMisses;
639
- dst.graphExtraction.truncations += src.graphExtraction.truncations;
640
- dst.graphExtraction.failures += src.graphExtraction.failures;
641
- dst.graphExtraction.htmlErrors += src.graphExtraction.htmlErrors;
642
- dst.graphExtraction.nonArrayBatchFailures += src.graphExtraction.nonArrayBatchFailures;
643
- dst.graphExtraction.retryAttempts += src.graphExtraction.retryAttempts;
644
- dst.graphExtraction.durationMs += src.graphExtraction.durationMs;
645
- dst.sessionExtraction.sessionsScanned += src.sessionExtraction.sessionsScanned;
646
- dst.sessionExtraction.sessionsExtracted += src.sessionExtraction.sessionsExtracted;
647
- dst.sessionExtraction.sessionsSkipped += src.sessionExtraction.sessionsSkipped;
648
- dst.sessionExtraction.proposalsCreated += src.sessionExtraction.proposalsCreated;
649
- dst.sessionExtraction.warnings += src.sessionExtraction.warnings;
650
- dst.sessionExtraction.durationMs += src.sessionExtraction.durationMs;
651
- // WS-5: merge perf telemetry (additive sums).
652
- dst.perfTelemetry.dedupPoolSize += src.perfTelemetry.dedupPoolSize;
653
- dst.perfTelemetry.llmPoolSize += src.perfTelemetry.llmPoolSize;
654
- dst.perfTelemetry.embedMs += src.perfTelemetry.embedMs;
655
- dst.perfTelemetry.embedCacheHits += src.perfTelemetry.embedCacheHits;
656
- dst.perfTelemetry.embedCacheMisses += src.perfTelemetry.embedCacheMisses;
657
- dst.perfTelemetry.overBudgetRuns += src.perfTelemetry.overBudgetRuns;
658
- dst.perfTelemetry.runsWithTelemetry += src.perfTelemetry.runsWithTelemetry;
659
- // coverage: acceptedProposals is additive; totalAssets is a snapshot (like memorySummary).
660
- // totalAssets is intentionally NOT merged here — set from the most recent run in summarizeImproveRuns.
661
- dst.coverage.acceptedProposals += src.coverage.acceptedProposals;
662
272
  }
663
273
  function compareImproveRunRecency(a, b) {
664
274
  const started = a.started_at.localeCompare(b.started_at);
@@ -669,114 +279,52 @@ function compareImproveRunRecency(a, b) {
669
279
  return completed;
670
280
  return a.id.localeCompare(b.id);
671
281
  }
282
+ /**
283
+ * Aggregate the window's `improve_runs` rows. `runCount` is every non-dry-run
284
+ * row in the window; rows whose envelope does not decode are counted under
285
+ * `resultRows.skipped.invalid` and excluded from the result-derived metrics.
286
+ */
672
287
  export function summarizeImproveRuns(db, since, until) {
673
- const accum = createUnknownImproveMetrics();
288
+ const accum = emptyImproveMetrics();
674
289
  const rows = queryImproveRuns(db, since, until);
675
- // Per-phase wall-time samples. Each entry is one envelope's durationMs for
676
- // that phase. Phases that did not run on a given envelope are simply
677
- // omitted (NOT counted as 0) so the median/p95 reflect actual phase work.
678
- const phaseDurations = {
679
- consolidation: [],
680
- memoryInference: [],
681
- graphExtraction: [],
682
- };
290
+ const resultRows = { total: rows.length, included: 0, skipped: { invalid: 0 } };
683
291
  // memorySummary is a whole-stash snapshot per run, so the window value is the
684
- // MOST RECENT run's snapshot (current state) — not a sum across runs.
685
- let latestCompleteRow;
686
- let latestMemorySummary;
687
- let latestStrategyFilteredRefs = 0;
688
- if (!accum.resultRows)
689
- throw new Error("invariant: improve result-row accounting was not initialized");
690
- accum.resultRows.total = rows.length;
292
+ // newest complete run's snapshot (current state) — not a sum across runs.
293
+ let latest;
691
294
  for (const row of rows) {
692
- let decoded;
693
- try {
694
- decoded = decodeImproveResult(row.result_json);
695
- }
696
- catch {
697
- accum.resultRows.skipped.invalid += 1;
295
+ const result = decodeImproveResultRow(row);
296
+ if (!result) {
297
+ resultRows.skipped.invalid += 1;
698
298
  continue;
699
299
  }
700
- accum.resultRows.included += 1;
701
- const result = decoded.envelope;
300
+ resultRows.included += 1;
702
301
  const perRow = projectRunMetrics(result);
703
302
  mergeImproveMetrics(accum, perRow);
704
- const startMs = new Date(row.started_at).getTime();
705
303
  if (result.terminated === undefined &&
706
- Number.isFinite(startMs) &&
707
- (latestCompleteRow === undefined || compareImproveRunRecency(row, latestCompleteRow) > 0)) {
708
- latestCompleteRow = row;
709
- latestMemorySummary = perRow.memorySummary;
710
- latestStrategyFilteredRefs = perRow.strategyFilteredRefs;
304
+ Number.isFinite(new Date(row.started_at).getTime()) &&
305
+ (latest === undefined || compareImproveRunRecency(row, latest.row) > 0)) {
306
+ latest = { row, memorySummary: perRow.memorySummary };
711
307
  }
712
- // Collect per-phase durations directly off the envelope. consolidation's
713
- // duration lives inside the sub-object; memoryInference and graphExtraction
714
- // expose top-level *DurationMs keys (`memoryInferenceDurationMs`,
715
- // `graphExtractionDurationMs`) when they actually ran on that envelope.
716
- const consol = result.consolidation;
717
- const consolMs = toFiniteNumber(consol?.durationMs);
718
- if (consolMs > 0)
719
- phaseDurations.consolidation.push(consolMs);
720
- const memMs = toFiniteNumber(result.memoryInferenceDurationMs);
721
- if (memMs > 0)
722
- phaseDurations.memoryInference.push(memMs);
723
- const graphMs = toFiniteNumber(result.graphExtractionDurationMs);
724
- if (graphMs > 0)
725
- phaseDurations.graphExtraction.push(graphMs);
726
308
  }
727
309
  finalizeImproveMetrics(accum);
728
- if (latestMemorySummary)
729
- accum.memorySummary = latestMemorySummary;
730
- accum.strategyFilteredRefs = latestStrategyFilteredRefs;
731
- accum.wallTime.byPhase = {
732
- consolidation: summarizePhaseDurations(phaseDurations.consolidation),
733
- memoryInference: summarizePhaseDurations(phaseDurations.memoryInference),
734
- graphExtraction: summarizePhaseDurations(phaseDurations.graphExtraction),
735
- };
310
+ accum.resultRows = resultRows;
311
+ if (latest)
312
+ accum.memorySummary = latest.memorySummary;
736
313
  return { metrics: accum, runCount: rows.length };
737
314
  }
738
- /**
739
- * Aggregate a list of per-envelope phase durations into the
740
- * `wallTime.byPhase.*` shape: count, total, median, p95. Median/p95 use the
741
- * same nearest-rank picker as the top-level wallTime stats so the two are
742
- * comparable.
743
- */
744
- export function summarizePhaseDurations(samples) {
745
- if (samples.length === 0)
746
- return { count: 0, totalMs: 0, medianMs: 0, p95Ms: 0 };
747
- const sorted = [...samples].sort((a, b) => a - b);
748
- const pick = (q) => sorted[Math.min(sorted.length - 1, Math.floor(q * sorted.length))] ?? 0;
749
- const totalMs = sorted.reduce((acc, n) => acc + n, 0);
750
- return {
751
- count: sorted.length,
752
- totalMs,
753
- medianMs: pick(0.5),
754
- p95Ms: pick(0.95),
755
- };
756
- }
757
- /**
758
- * Project an improve_runs row + wall-time lookup into a single ImproveRunSummary.
759
- * Used by `akm health --detail per-run`.
760
- */
315
+ /** Project an improve_runs row + wall time + task attribution into one {@link ImproveRunSummary}. */
761
316
  export function projectImproveRunSummary(row, wallTimeMs, taskId) {
762
- let result = {};
763
- let resultStatus = "invalid";
764
- try {
765
- const decoded = decodeImproveResult(row.result_json);
766
- result = decoded.envelope;
767
- resultStatus = "valid";
768
- }
769
- catch {
317
+ const decoded = decodeImproveResultRow(row);
318
+ const result = decoded ?? {};
319
+ const resultStatus = decoded ? "valid" : "invalid";
320
+ if (!decoded) {
770
321
  // Keep the persisted row visible in per-run output, but do not project its
771
322
  // unknown payload or admit its duration to result-derived denominators.
772
323
  wallTimeMs = 0;
773
324
  }
774
325
  const perRow = projectRunMetrics(result);
775
326
  finalizeImproveMetrics(perRow);
776
- const orphansPurged = toFiniteNumber(result.orphansPurged);
777
327
  const lintSummary = result.lintSummary;
778
- const lintFixed = lintSummary ? toFiniteNumber(lintSummary.fixed) : 0;
779
- const lintFlagged = lintSummary ? toFiniteNumber(lintSummary.flagged) : 0;
780
328
  return {
781
329
  id: row.id,
782
330
  startedAt: row.started_at,
@@ -793,37 +341,20 @@ export function projectImproveRunSummary(row, wallTimeMs, taskId) {
793
341
  taskId,
794
342
  actions: perRow.actions,
795
343
  memorySummary: perRow.memorySummary,
796
- memoryCleanup: perRow.memoryCleanup,
797
344
  consolidation: perRow.consolidation,
798
345
  memoryInference: perRow.memoryInference,
799
- graphExtraction: perRow.graphExtraction,
800
- reflectsWithErrorContext: perRow.reflectsWithErrorContext,
801
- orphansPurged,
802
- lintFixed,
803
- lintFlagged,
346
+ orphansPurged: toFiniteNumber(result.orphansPurged),
347
+ lintFixed: lintSummary ? toFiniteNumber(lintSummary.fixed) : 0,
348
+ lintFlagged: lintSummary ? toFiniteNumber(lintSummary.flagged) : 0,
804
349
  };
805
350
  }
806
- function emptyPhaseStats() {
807
- return {
808
- consolidation: { count: 0, totalMs: 0, medianMs: 0, p95Ms: 0 },
809
- memoryInference: { count: 0, totalMs: 0, medianMs: 0, p95Ms: 0 },
810
- graphExtraction: { count: 0, totalMs: 0, medianMs: 0, p95Ms: 0 },
811
- };
812
- }
813
- export function computeWallTimeStats(durationsMs, byPhase) {
814
- const phase = byPhase ?? emptyPhaseStats();
351
+ /** Nearest-rank median and p95 of the window's run wall times. */
352
+ export function computeWallTimeStats(durationsMs) {
815
353
  if (durationsMs.length === 0)
816
- return { count: 0, medianMs: 0, p95Ms: 0, minMs: 0, maxMs: 0, byPhase: phase };
354
+ return { medianMs: 0, p95Ms: 0 };
817
355
  const sorted = [...durationsMs].sort((a, b) => a - b);
818
356
  const pick = (q) => sorted[Math.min(sorted.length - 1, Math.floor(q * sorted.length))] ?? 0;
819
- return {
820
- count: sorted.length,
821
- medianMs: pick(0.5),
822
- p95Ms: pick(0.95),
823
- minMs: sorted[0] ?? 0,
824
- maxMs: sorted[sorted.length - 1] ?? 0,
825
- byPhase: phase,
826
- };
357
+ return { medianMs: pick(0.5), p95Ms: pick(0.95) };
827
358
  }
828
359
  export function buildImproveSkipSummary(events) {
829
360
  // Two kinds of skip events:
@@ -833,9 +364,9 @@ export function buildImproveSkipSummary(events) {
833
364
  // (`no_new_signal`, `strategy_filtered_all_passes`). Each run re-counts the
834
365
  // same stable set, so summing across the window re-counts it N times (the
835
366
  // 2.7M / 3M inflation). For these we keep the MOST RECENT run's count — the
836
- // current snapshot — matching how memorySummary/strategyFilteredRefs are
837
- // handled. Events arrive in chronological (offset) order, so the last
838
- // count-bearing event per reason is the latest run's value.
367
+ // current snapshot — matching how memorySummary is handled. Events arrive
368
+ // in chronological (offset) order, so the last count-bearing event per
369
+ // reason is the latest run's value.
839
370
  const summed = {};
840
371
  const latestSnapshot = {};
841
372
  for (const event of events) {
@@ -855,3 +386,21 @@ export function buildImproveSkipSummary(events) {
855
386
  const skipped = Object.values(skipReasons).reduce((a, b) => a + b, 0);
856
387
  return { skipped, skipReasons };
857
388
  }
389
+ /**
390
+ * Proposals accepted in `[since, until)` (by `updated_at`) and the distinct
391
+ * refs among them — N accepted rewrites of one asset touch one ref. A single
392
+ * SQL aggregate; proposal bodies are never loaded. Fails open to zeros when
393
+ * the table is absent.
394
+ */
395
+ export function computeWindowProposalCoverage(db, since, until) {
396
+ try {
397
+ const row = db
398
+ .prepare("SELECT COUNT(*) AS accepted, COUNT(DISTINCT ref) AS refs FROM proposals " +
399
+ "WHERE status = 'accepted' AND updated_at >= ? AND (? IS NULL OR updated_at < ?)")
400
+ .get(since, until ?? null, until ?? null);
401
+ return { acceptedProposals: row?.accepted ?? 0, distinctRefs: row?.refs ?? 0 };
402
+ }
403
+ catch {
404
+ return { acceptedProposals: 0, distinctRefs: 0 };
405
+ }
406
+ }