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