akm-cli 0.9.17-alpha.2 → 0.9.17-alpha.4

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 (343) hide show
  1. package/CHANGELOG.md +756 -0
  2. package/dist/akm +94 -196
  3. package/dist/cli/shared.js +6 -2
  4. package/dist/cli.js +22 -9
  5. package/dist/commands/agent/agent-dispatch.js +1 -1
  6. package/dist/commands/command/command-execution.js +24 -62
  7. package/dist/commands/feedback-cli.js +0 -1
  8. package/dist/commands/health/accept-rate.js +2 -2
  9. package/dist/commands/health/checks.js +30 -75
  10. package/dist/commands/health/config-skew.js +38 -0
  11. package/dist/commands/health/egress.js +54 -0
  12. package/dist/commands/health/html-report.js +0 -38
  13. package/dist/commands/health/improve-metrics.js +123 -562
  14. package/dist/commands/health/plugin-staleness.js +53 -3
  15. package/dist/commands/health/renderers.js +12 -4
  16. package/dist/commands/health/report-view-model.js +11 -106
  17. package/dist/commands/health/types-improve.js +4 -19
  18. package/dist/commands/health/windows.js +64 -73
  19. package/dist/commands/health.js +122 -143
  20. package/dist/commands/improve/consolidate/chunking.js +25 -100
  21. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  22. package/dist/commands/improve/consolidate.js +538 -1075
  23. package/dist/commands/improve/content-hash.js +16 -24
  24. package/dist/commands/improve/distill/content-repair.js +18 -100
  25. package/dist/commands/improve/distill-guards.js +20 -81
  26. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  27. package/dist/commands/improve/distill.js +608 -1075
  28. package/dist/commands/improve/eligibility.js +126 -400
  29. package/dist/commands/improve/execution.js +3 -5
  30. package/dist/commands/improve/extract.js +487 -1046
  31. package/dist/commands/improve/feedback-valence.js +0 -25
  32. package/dist/commands/improve/improve-cli.js +29 -166
  33. package/dist/commands/improve/improve-result-file.js +10 -66
  34. package/dist/commands/improve/improve-strategies.js +12 -7
  35. package/dist/commands/improve/improve-usage-report.js +18 -64
  36. package/dist/commands/improve/improve.js +443 -1063
  37. package/dist/commands/improve/ledger.js +114 -0
  38. package/dist/commands/improve/locks.js +2 -8
  39. package/dist/commands/improve/loop-stages.js +459 -1172
  40. package/dist/commands/improve/memory/derived-ref.js +12 -77
  41. package/dist/commands/improve/memory/memory-belief.js +14 -118
  42. package/dist/commands/improve/memory/memory-improve.js +4 -3
  43. package/dist/commands/improve/outcome-loop.js +28 -156
  44. package/dist/commands/improve/planner.js +5 -10
  45. package/dist/commands/improve/preparation.js +851 -2339
  46. package/dist/commands/improve/proactive-maintenance.js +34 -101
  47. package/dist/commands/improve/reflect-noise.js +104 -280
  48. package/dist/commands/improve/reflect.js +621 -1367
  49. package/dist/commands/improve/salience.js +46 -232
  50. package/dist/commands/improve/session-asset.js +19 -100
  51. package/dist/commands/improve/stage.js +323 -0
  52. package/dist/commands/proposal/drain.js +251 -644
  53. package/dist/commands/proposal/proposal-cli.js +3 -18
  54. package/dist/commands/proposal/proposal-types.js +20 -41
  55. package/dist/commands/proposal/proposal.js +1 -2
  56. package/dist/commands/proposal/propose.js +134 -160
  57. package/dist/commands/proposal/repository.js +502 -1487
  58. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  59. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  60. package/dist/commands/proposal/validators/proposals.js +13 -89
  61. package/dist/commands/read/curate.js +63 -413
  62. package/dist/commands/read/search-cli.js +16 -33
  63. package/dist/commands/read/search.js +17 -23
  64. package/dist/commands/read/show.js +2 -13
  65. package/dist/commands/sources/bundle-cli.js +25 -2
  66. package/dist/commands/sources/bundle-config-ops.js +7 -0
  67. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  68. package/dist/commands/sources/info.js +2 -11
  69. package/dist/commands/sources/installed-stashes.js +197 -746
  70. package/dist/commands/sources/schema-repair.js +98 -129
  71. package/dist/commands/sources/source-add.js +62 -12
  72. package/dist/commands/sources/stash-cli.js +1 -1
  73. package/dist/commands/tasks/explain.js +10 -13
  74. package/dist/commands/tasks/tasks-cli.js +9 -8
  75. package/dist/commands/tasks/tasks.js +326 -930
  76. package/dist/commands/tasks/validate.js +42 -21
  77. package/dist/commands/workflow/plan.js +22 -29
  78. package/dist/commands/workflow-cli.js +4 -4
  79. package/dist/core/adapter/adapters/akm-adapter.js +0 -1
  80. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  81. package/dist/core/adapter/adapters/akm-metadata.js +11 -12
  82. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  83. package/dist/core/adapter/execution-source.js +17 -29
  84. package/dist/core/asset/resolve-ref.js +1 -1
  85. package/dist/core/bundle-id.js +42 -5
  86. package/dist/core/bundle-rename.js +291 -0
  87. package/dist/core/config/config-io.js +1 -2
  88. package/dist/core/config/config-schema.js +1 -33
  89. package/dist/core/config/config-walker.js +1 -1
  90. package/dist/core/config/config.js +163 -68
  91. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  92. package/dist/core/config/schema/embedding.js +20 -5
  93. package/dist/core/config/schema/engines.js +5 -0
  94. package/dist/core/config/schema/execution.js +1 -1
  95. package/dist/core/config/schema/experimental.js +1 -1
  96. package/dist/core/config/schema/improve-processes.js +21 -95
  97. package/dist/core/config/schema/improve.js +4 -42
  98. package/dist/core/config/schema/scheduler.js +12 -12
  99. package/dist/core/config/schema/search.js +6 -22
  100. package/dist/core/env-secret-ref.js +0 -1
  101. package/dist/core/errors.js +8 -9
  102. package/dist/core/file-lock.js +76 -173
  103. package/dist/core/logs-db.js +2 -2
  104. package/dist/core/paths.js +0 -27
  105. package/dist/core/redaction.js +109 -2
  106. package/dist/core/run-lock.js +2 -5
  107. package/dist/core/spawn-env.js +1 -1
  108. package/dist/core/state/migrations.js +108 -61
  109. package/dist/core/state-db-scope.js +2 -4
  110. package/dist/core/state-db.js +126 -692
  111. package/dist/core/type-presentation.js +1 -9
  112. package/dist/core/write-source.js +293 -1012
  113. package/dist/execution/input-contract.js +1 -1
  114. package/dist/execution/resolved-request.js +135 -689
  115. package/dist/execution/source.js +63 -257
  116. package/dist/execution/target-ref.js +1 -1
  117. package/dist/indexer/bundle-identity-guard.js +2 -2
  118. package/dist/indexer/db/graph-db.js +106 -46
  119. package/dist/indexer/ensure-index.js +44 -85
  120. package/dist/indexer/graph/graph-extraction.js +340 -562
  121. package/dist/indexer/graph/graph-related.js +130 -0
  122. package/dist/indexer/index-rebuild-lock.js +3 -11
  123. package/dist/indexer/index-writer-lock.js +8 -17
  124. package/dist/indexer/index-written-assets.js +139 -151
  125. package/dist/indexer/indexer.js +524 -846
  126. package/dist/indexer/materialize-embeddings.js +60 -397
  127. package/dist/indexer/passes/memory-inference.js +81 -90
  128. package/dist/indexer/passes/metadata.js +132 -200
  129. package/dist/indexer/read-preflight.js +0 -7
  130. package/dist/indexer/scan/doc-to-entry.js +1 -3
  131. package/dist/indexer/scan/drain-dir.js +1 -1
  132. package/dist/indexer/search/db-search.js +181 -590
  133. package/dist/indexer/search/fts-query.js +30 -41
  134. package/dist/indexer/search/ranking.js +28 -154
  135. package/dist/indexer/search/search-attribution.js +12 -32
  136. package/dist/indexer/search/search-fields.js +11 -15
  137. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  138. package/dist/indexer/search/search-source.js +1 -4
  139. package/dist/indexer/usage/usage-events.js +2 -7
  140. package/dist/integrations/agent/engine-fallback.js +23 -40
  141. package/dist/integrations/agent/engine-resolution.js +93 -183
  142. package/dist/integrations/agent/execution.js +507 -0
  143. package/dist/integrations/agent/model-map.js +28 -156
  144. package/dist/integrations/agent/request-lowering.js +66 -141
  145. package/dist/integrations/agent/runner-dispatch.js +143 -321
  146. package/dist/integrations/agent/runner.js +54 -14
  147. package/dist/integrations/lockfile.js +53 -101
  148. package/dist/llm/embedders/deterministic.js +2 -3
  149. package/dist/llm/embedders/profile.js +71 -0
  150. package/dist/llm/embedders/remote.js +10 -15
  151. package/dist/llm/graph-extract.js +3 -12
  152. package/dist/llm/index-passes.js +3 -5
  153. package/dist/llm/memory-infer.js +1 -2
  154. package/dist/llm/metadata-enhance.js +1 -2
  155. package/dist/llm/structured-call.js +5 -24
  156. package/dist/output/generic-render.js +23 -11
  157. package/dist/output/html-render.js +13 -10
  158. package/dist/output/render-registry.js +3 -32
  159. package/dist/output/shapes/helpers.js +2 -34
  160. package/dist/output/shapes/passthrough.js +1 -9
  161. package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
  162. package/dist/output/text/command-format.js +60 -23
  163. package/dist/output/text/helpers.js +1 -1
  164. package/dist/output/text/migrate.js +5 -14
  165. package/dist/output/text/proposal-format.js +1 -2
  166. package/dist/output/text/workflow-format.js +0 -32
  167. package/dist/output/text.js +2 -0
  168. package/dist/registry/factory.js +4 -19
  169. package/dist/registry/network.js +66 -220
  170. package/dist/registry/providers/index.js +0 -2
  171. package/dist/registry/providers/skills-sh.js +3 -14
  172. package/dist/registry/providers/static-index.js +24 -26
  173. package/dist/registry/resolve.js +55 -131
  174. package/dist/scripts/akm-migrate-node.js +43937 -93313
  175. package/dist/scripts/akm-migrate.js +43697 -93071
  176. package/dist/setup/registry-stash-loader.js +4 -13
  177. package/dist/setup/semantic-assets.js +3 -44
  178. package/dist/setup/setup.js +1 -1
  179. package/dist/setup/steps/tasks.js +25 -15
  180. package/dist/sources/provider-factory.js +17 -18
  181. package/dist/sources/providers/filesystem.js +2 -3
  182. package/dist/sources/providers/git-install.js +7 -1
  183. package/dist/sources/providers/git-provider.js +0 -3
  184. package/dist/sources/providers/git-stash.js +0 -17
  185. package/dist/sources/providers/npm.js +2 -4
  186. package/dist/sources/providers/provider-utils.js +5 -10
  187. package/dist/sources/providers/website.js +0 -2
  188. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  189. package/dist/sources/website-url.js +2 -2
  190. package/dist/storage/database.js +9 -35
  191. package/dist/storage/repositories/improve-ledger-repository.js +168 -0
  192. package/dist/storage/repositories/index-connection.js +34 -70
  193. package/dist/storage/repositories/index-entries-repository.js +69 -111
  194. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  195. package/dist/storage/repositories/index-entry-schema.js +83 -269
  196. package/dist/storage/repositories/index-fts-repository.js +86 -256
  197. package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
  198. package/dist/storage/repositories/index-meta-repository.js +6 -4
  199. package/dist/storage/repositories/index-schema.js +192 -220
  200. package/dist/storage/repositories/index-utility-repository.js +8 -29
  201. package/dist/storage/repositories/index-vec-repository.js +133 -414
  202. package/dist/storage/repositories/outcome-repository.js +2 -1
  203. package/dist/storage/repositories/proposals-repository.js +35 -0
  204. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  205. package/dist/storage/repositories/task-history-repository.js +26 -4
  206. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  207. package/dist/storage/sqlite-migrations.js +136 -0
  208. package/dist/storage/sqlite-pragmas.js +11 -9
  209. package/dist/storage/sqlite-transaction.js +170 -0
  210. package/dist/storage/state-db-integrity.js +34 -27
  211. package/dist/tasks/activation-config.js +134 -62
  212. package/dist/tasks/backends/cron.js +129 -277
  213. package/dist/tasks/backends/exec-utils.js +2 -5
  214. package/dist/tasks/backends/launchd.js +125 -745
  215. package/dist/tasks/backends/schtasks.js +101 -620
  216. package/dist/tasks/prepare/prepare-support.js +5 -15
  217. package/dist/tasks/prepare/prepare.js +0 -2
  218. package/dist/tasks/resolve-akm-bin.js +20 -79
  219. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  220. package/dist/tasks/scheduler-binding.js +18 -238
  221. package/dist/tasks/scheduler-invocation.js +52 -52
  222. package/dist/tasks/scheduler-lock.js +53 -0
  223. package/dist/tasks/scheduler-sync.js +363 -679
  224. package/dist/tasks/source/parse-task-source.js +160 -10
  225. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  226. package/dist/tasks/source/task-to-v4.js +2 -2
  227. package/dist/workflows/authoring/authoring.js +3 -12
  228. package/dist/workflows/compile.js +211 -0
  229. package/dist/workflows/concurrency-policy.js +13 -74
  230. package/dist/workflows/exec/child-invocation.js +3 -17
  231. package/dist/workflows/exec/child-workflow.js +32 -141
  232. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  233. package/dist/workflows/exec/environment.js +98 -0
  234. package/dist/workflows/exec/exec-unit.js +33 -140
  235. package/dist/workflows/exec/frozen-judge.js +7 -59
  236. package/dist/workflows/exec/native-executor.js +82 -341
  237. package/dist/workflows/exec/param-secrets.js +29 -47
  238. package/dist/workflows/exec/run-workflow.js +154 -387
  239. package/dist/workflows/exec/scheduler.js +9 -36
  240. package/dist/workflows/exec/step-work.js +127 -430
  241. package/dist/workflows/exec/unit-dispatch.js +11 -63
  242. package/dist/workflows/exec/unit-writer.js +8 -52
  243. package/dist/workflows/exec/worktree.js +39 -273
  244. package/dist/workflows/freeze/child-output-references.js +4 -15
  245. package/dist/workflows/freeze/environment.js +99 -92
  246. package/dist/workflows/freeze/freeze.js +172 -0
  247. package/dist/workflows/freeze/step-values.js +19 -21
  248. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  249. package/dist/workflows/freeze/targets/command.js +10 -33
  250. package/dist/workflows/freeze/targets/script.js +5 -12
  251. package/dist/workflows/freeze/targets/shell.js +3 -6
  252. package/dist/workflows/freeze/targets/task.js +25 -80
  253. package/dist/workflows/freeze/task-bindings.js +20 -67
  254. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  255. package/dist/workflows/ir/params.js +6 -51
  256. package/dist/workflows/ir/plan-hash.js +2 -34
  257. package/dist/workflows/parser.js +140 -43
  258. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  259. package/dist/workflows/renderer.js +36 -69
  260. package/dist/workflows/resource-limits.js +12 -120
  261. package/dist/workflows/runtime/agent-identity.js +8 -40
  262. package/dist/workflows/runtime/run-outputs.js +3 -6
  263. package/dist/workflows/runtime/run-plan.js +316 -0
  264. package/dist/workflows/runtime/runs.js +48 -200
  265. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  266. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  267. package/dist/workflows/validate-summary.js +2 -7
  268. package/docs/integration/bundling-akm.md +49 -42
  269. package/docs/migration/README.md +1 -0
  270. package/docs/migration/release-notes/0.9.17.md +41 -0
  271. package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
  272. package/docs/reference/cli.md +182 -125
  273. package/docs/reference/configuration.md +49 -56
  274. package/docs/reference/data-and-telemetry.md +19 -20
  275. package/docs/reference/tasks.md +86 -38
  276. package/docs/reference/workflow-schema.md +14 -18
  277. package/docs/reference/workflows.md +6 -9
  278. package/package.json +1 -1
  279. package/schemas/akm-config.json +87 -406
  280. package/dist/commands/health/advisories.js +0 -150
  281. package/dist/commands/health/metrics.js +0 -329
  282. package/dist/commands/health/surfaces.js +0 -102
  283. package/dist/commands/improve/anti-collapse.js +0 -83
  284. package/dist/commands/improve/collapse-detector.js +0 -432
  285. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  286. package/dist/commands/improve/consolidate/merge.js +0 -146
  287. package/dist/commands/improve/distill/promote-memory.js +0 -329
  288. package/dist/commands/improve/distill/quality-gate.js +0 -500
  289. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  290. package/dist/commands/improve/proposal-envelope.js +0 -31
  291. package/dist/commands/improve/run-context.js +0 -123
  292. package/dist/commands/improve/shared.js +0 -21
  293. package/dist/commands/improve/source-identity.js +0 -28
  294. package/dist/commands/improve/triage.js +0 -96
  295. package/dist/commands/proposal/drain-policies.js +0 -151
  296. package/dist/commands/sources/update-transaction.js +0 -220
  297. package/dist/core/action-contributors.js +0 -28
  298. package/dist/core/config/config-version-shim.js +0 -101
  299. package/dist/core/config/retired-experimental-keys-shim.js +0 -62
  300. package/dist/core/fs-txn.js +0 -405
  301. package/dist/core/lexical-score.js +0 -25
  302. package/dist/core/maintenance-barrier.js +0 -167
  303. package/dist/execution/executable-identity.js +0 -105
  304. package/dist/execution/guarded-source.js +0 -427
  305. package/dist/indexer/graph/graph-boost.js +0 -427
  306. package/dist/indexer/graph/graph-dedup.js +0 -95
  307. package/dist/indexer/search/name-match.js +0 -35
  308. package/dist/indexer/search/ranking-contributors.js +0 -515
  309. package/dist/indexer/walk/project-context.js +0 -192
  310. package/dist/integrations/agent/execution-cascade.js +0 -566
  311. package/dist/integrations/agent/execution-definitions.js +0 -202
  312. package/dist/integrations/agent/execution-lowering.js +0 -841
  313. package/dist/integrations/agent/execution-preparation.js +0 -98
  314. package/dist/integrations/agent/inline-execution.js +0 -74
  315. package/dist/registry/create-provider-registry.js +0 -29
  316. package/dist/registry/pinned-request-helper.js +0 -247
  317. package/dist/registry/pinned-transport.js +0 -717
  318. package/dist/sources/providers/index.js +0 -14
  319. package/dist/storage/engines/sqlite-migrations.js +0 -271
  320. package/dist/storage/repositories/canaries-repository.js +0 -107
  321. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  322. package/dist/storage/repositories/registry-cache.js +0 -113
  323. package/dist/tasks/scheduler-sync-preview.js +0 -52
  324. package/dist/workflows/freeze/resolve-steps.js +0 -86
  325. package/dist/workflows/freeze/source-freeze.js +0 -64
  326. package/dist/workflows/ir/compile.js +0 -321
  327. package/dist/workflows/ir/environment-v4.js +0 -330
  328. package/dist/workflows/ir/freeze-v4.js +0 -153
  329. package/dist/workflows/ir/schema-v4.js +0 -745
  330. package/dist/workflows/ir/schema.js +0 -354
  331. package/dist/workflows/program/schema.js +0 -78
  332. package/dist/workflows/runtime/checkin.js +0 -57
  333. package/dist/workflows/runtime/plan-classifier.js +0 -196
  334. package/dist/workflows/runtime/unit-checkin.js +0 -45
  335. package/dist/workflows/runtime/unit-phases.js +0 -20
  336. package/dist/workflows/schema.js +0 -4
  337. package/dist/workflows/source-ir/compile.js +0 -200
  338. package/dist/workflows/source-ir/program.js +0 -50
  339. package/dist/workflows/source-ir/result.js +0 -26
  340. package/dist/workflows/source-ir/schema.js +0 -786
  341. package/dist/workflows/source-ir/triggers.js +0 -79
  342. package/dist/workflows/source-ir/uses.js +0 -40
  343. package/dist/workflows/validator.js +0 -60
@@ -63,6 +63,46 @@ import { isExactSemver, isSemverRange, maxSatisfying, satisfiesRange } from "../
63
63
  function claudePluginsDir() {
64
64
  return process.env.AKM_CLAUDE_PLUGINS_DIR ?? path.join(os.homedir(), ".claude", "plugins");
65
65
  }
66
+ /**
67
+ * Root of OpenCode's package cache, same override pattern as
68
+ * {@link claudePluginsDir} (`AKM_OPENCODE_CACHE_DIR` for tests).
69
+ * The OpenCode plugin (`akm-opencode`) bundles its own `akm-cli` under
70
+ * `packages/akm-opencode/node_modules/akm-cli` and runs it in-process,
71
+ * sharing the host's databases with every other akm install — its version
72
+ * lag matters the same way a stale Claude plugin's does.
73
+ */
74
+ function opencodeCacheDir() {
75
+ return process.env.AKM_OPENCODE_CACHE_DIR ?? path.join(os.homedir(), ".cache", "opencode");
76
+ }
77
+ /**
78
+ * Read the OpenCode plugin's bundled `akm-cli` version from its
79
+ * `package.json` — never executed, just parsed, mirroring
80
+ * {@link detectInstalledPlugins}'s manifest read. `undefined` when the
81
+ * OpenCode plugin, or its bundled akm-cli, is not installed.
82
+ */
83
+ function detectOpencodeBundledAkmVersion(cacheRoot) {
84
+ const pkgPath = path.join(cacheRoot, "packages", "akm-opencode", "node_modules", "akm-cli", "package.json");
85
+ try {
86
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
87
+ return typeof pkg.version === "string" ? pkg.version : undefined;
88
+ }
89
+ catch {
90
+ return undefined;
91
+ }
92
+ }
93
+ function buildOpencodeBundledAdvisory(bundledVersion, cliVersion) {
94
+ const stale = bundledVersion !== cliVersion;
95
+ return {
96
+ name: "opencode-plugin-version",
97
+ kind: "deterministic",
98
+ status: stale ? "warn" : "pass",
99
+ confidence: "high",
100
+ message: stale
101
+ ? `OpenCode's bundled akm-cli is v${bundledVersion}, but the running CLI is v${cliVersion} — it shares the same databases in-process; update the OpenCode plugin.`
102
+ : `OpenCode's bundled akm-cli matches the running v${cliVersion}.`,
103
+ evidence: { bundledVersion, cliVersion },
104
+ };
105
+ }
66
106
  /**
67
107
  * Scan `<pluginsRoot>/cache/<marketplace>/akm/<version>/` for every
68
108
  * `akm` plugin cache entry, picking the highest cached version per
@@ -165,14 +205,24 @@ const realListRemoteTags = (marketplaceDir) => {
165
205
  return [...new Set(tags)];
166
206
  };
167
207
  /**
168
- * Build one `plugin-version` advisory per detected `akm` harness plugin.
169
- * Returns `[]` when no plugin is installed — the benign, common case.
208
+ * Build one `plugin-version` advisory per detected `akm` harness plugin,
209
+ * plus one `opencode-plugin-version` advisory when the
210
+ * OpenCode plugin's bundled `akm-cli` is found — it shares the host's
211
+ * databases in-process, so its lag matters the same way a stale Claude
212
+ * plugin's does. Returns `[]` when neither is installed — the benign,
213
+ * common case.
170
214
  */
171
215
  export function collectPluginStalenessAdvisories(options) {
172
216
  const pluginsRoot = options.pluginsRoot ?? claudePluginsDir();
173
217
  const listRemoteTags = options.listRemoteTags ?? realListRemoteTags;
174
218
  const plugins = detectInstalledPlugins(pluginsRoot);
175
- return plugins.map((plugin) => buildAdvisory(plugin, options.cliVersion, pluginsRoot, listRemoteTags));
219
+ const results = plugins.map((plugin) => buildAdvisory(plugin, options.cliVersion, pluginsRoot, listRemoteTags));
220
+ const opencodeCacheRoot = options.opencodeCacheRoot ?? opencodeCacheDir();
221
+ const bundledVersion = detectOpencodeBundledAkmVersion(opencodeCacheRoot);
222
+ if (bundledVersion !== undefined) {
223
+ results.push(buildOpencodeBundledAdvisory(bundledVersion, options.cliVersion));
224
+ }
225
+ return results;
176
226
  }
177
227
  function buildAdvisory(plugin, cliVersion, pluginsRoot, listRemoteTags) {
178
228
  const pluginRef = `${plugin.pluginName}@${plugin.marketplace}`;
@@ -2,7 +2,7 @@
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  /**
5
- * `akm health` document renderers, registered rather than intercepted (D7).
5
+ * `akm health` document renderers.
6
6
  *
7
7
  * Both handlers are **pure functions of the shaped result**. Everything the
8
8
  * rich report needs — per-run rows, window-compare deltas, the pending
@@ -13,12 +13,19 @@
13
13
  * renderer to call order and made the command branch on the output format to
14
14
  * know when to bind it. Putting the data in the envelope removed both.
15
15
  *
16
+ * The Markdown renderer registers into the shared per-command registry (D7,
17
+ * `../../output/render-registry.ts`) alongside every other command's `--format
18
+ * md` renderer. The HTML renderer does not: `health` was, and remains, the
19
+ * only command with a bespoke HTML report, so `cli/shared.ts` calls
20
+ * {@link renderHealthHtml} directly instead of going through a registry with
21
+ * exactly one possible registrant.
22
+ *
16
23
  * Returning `null` falls through to the generic renderer, so a result without
17
24
  * the report dataset still renders — generically — instead of erroring or
18
25
  * emitting an empty table.
19
26
  */
20
27
  import { renderHtml, resolveTemplatePath } from "../../output/html-render.js";
21
- import { registerHtmlRenderer, registerMdRenderer } from "../../output/render-registry.js";
28
+ import { registerMdRenderer } from "../../output/render-registry.js";
22
29
  import { buildHealthHtmlReplacements } from "./html-report.js";
23
30
  import { renderRunsDetailMd, renderWindowCompareMd } from "./md-report.js";
24
31
  function isHealthResult(value) {
@@ -34,7 +41,8 @@ registerMdRenderer("health", (result) => {
34
41
  return renderRunsDetailMd(result.runs);
35
42
  return null;
36
43
  });
37
- registerHtmlRenderer("health", (result) => {
44
+ /** `--format html` renderer for `akm health`, called directly from `cli/shared.ts`. */
45
+ export function renderHealthHtml(result) {
38
46
  if (!isHealthResult(result) || !result.report)
39
47
  return null;
40
48
  return renderHtml(resolveTemplatePath("health"), buildHealthHtmlReplacements(result, {
@@ -44,4 +52,4 @@ registerHtmlRenderer("health", (result) => {
44
52
  proposals: result.report.pendingProposals,
45
53
  deltas: result.deltas,
46
54
  }));
47
- });
55
+ }
@@ -79,31 +79,18 @@ function reshapeRun(r) {
79
79
  completedAt: r.completedAt,
80
80
  wallTimeMs: wall,
81
81
  ok: r.ok,
82
- mode: r.scope.mode,
83
82
  consDurationMs: consMs,
84
83
  miDurationMs: miMs,
85
84
  geDurationMs: geMs,
86
85
  otherMs: Math.max(0, wall - consMs - miMs - geMs),
87
- consRan: cons.ran,
88
86
  promoted: cons.promoted,
89
87
  merged: cons.merged,
90
88
  deleted: cons.deleted,
91
89
  contradicted: cons.contradicted,
92
90
  judgedNoAction: cons.judgedNoAction,
93
- processed: cons.processed,
94
- failedChunks: cons.failedChunks,
95
- totalChunks: cons.totalChunks,
96
91
  miWritten: mi.written,
97
- miConsidered: mi.considered,
98
- miYieldRate: mi.yieldRate,
99
- miCacheHits: mi.cacheHits,
100
92
  geEntities: ge.entities,
101
93
  geRelations: ge.relations,
102
- geCacheHitRate: ge.cacheHitRate,
103
- geFailures: ge.failures,
104
- distillSkipped: r.actions.distill.skipped,
105
- distillQueued: r.actions.distill.queued,
106
- distillLlmFailed: r.actions.distill.llmFailed,
107
94
  distillByReason: r.actions.distill.skippedByReason,
108
95
  reflectOk: r.actions.reflect.ok,
109
96
  reflectFailed: r.actions.reflect.failed,
@@ -111,8 +98,6 @@ function reshapeRun(r) {
111
98
  eligible: r.memorySummary.eligible,
112
99
  lintFlagged: r.lintFlagged,
113
100
  lintFixed: r.lintFixed,
114
- reflectsWithErrorContext: r.reflectsWithErrorContext,
115
- orphansPurged: r.orphansPurged,
116
101
  };
117
102
  }
118
103
  function compareRuns(a, b) {
@@ -135,11 +120,7 @@ function classify(deltas, metricKeys, lowerIsBetter = false) {
135
120
  }
136
121
  function buildTrend(deltas) {
137
122
  const decisionQuality = classify(deltas, ["improve.memoryInference.yieldRate", "improve.consolidation.promoted"]);
138
- const outputVolume = classify(deltas, [
139
- "improve.consolidation.promoted",
140
- "improve.memoryInference.written",
141
- "improve.sessionExtraction.proposalsCreated",
142
- ]);
123
+ const outputVolume = classify(deltas, ["improve.consolidation.promoted", "improve.memoryInference.written"]);
143
124
  const failures = classify(deltas, ["improve.graphExtraction.failures"], true);
144
125
  const latency = classify(deltas, ["improve.wallTime.medianMs", "improve.wallTime.p95Ms"], true);
145
126
  const score = [decisionQuality, outputVolume, failures, latency].reduce((acc, d) => acc + (d === "up" ? 1 : d === "down" ? -1 : 0), 0);
@@ -205,19 +186,7 @@ function buildAggregatesPhase(result, runsPhase) {
205
186
  const mi = improve.memoryInference;
206
187
  const ge = improve.graphExtraction;
207
188
  const wallTime = improve.wallTime;
208
- // WS-5: perf telemetry, coverage, and degradation metrics.
209
- const perf = improve.perfTelemetry ?? {
210
- dedupPoolSize: 0,
211
- llmPoolSize: 0,
212
- embedMs: 0,
213
- embedCacheHits: 0,
214
- embedCacheMisses: 0,
215
- overBudgetRuns: 0,
216
- runsWithTelemetry: 0,
217
- };
218
189
  const coverage = improve.coverage;
219
- const degradation = improve.degradation;
220
- const minting = improve.enrichmentMinting;
221
190
  // #576: real per-stage LLM token/time accounting (replaces the GPU-time
222
191
  // proxy). Optional-guarded so reports built from older health JSON without
223
192
  // the aggregate still render.
@@ -242,10 +211,7 @@ function buildAggregatesPhase(result, runsPhase) {
242
211
  memoryInference: mi,
243
212
  graphExtraction: ge,
244
213
  wallTime,
245
- perf,
246
214
  coverage,
247
- degradation,
248
- minting,
249
215
  llm,
250
216
  memorySummary: improve.memorySummary,
251
217
  includedResultRows: improve.resultRows?.included ?? totalRuns,
@@ -343,7 +309,7 @@ function groupProposalsBySource(proposals) {
343
309
  }
344
310
  /** Summary table rows: the base metric set + the WS-5 coverage/minting/perf/degradation extensions, when present. */
345
311
  function buildSummaryRows(aggregates, trend) {
346
- const { consolidation: cons, graphExtraction: ge, wallTime, llm, coverage, minting, perf, degradation } = aggregates;
312
+ const { consolidation: cons, graphExtraction: ge, wallTime, llm, coverage } = aggregates;
347
313
  const summaryRows = [
348
314
  ["Task fail rate", aggregates.taskFailRate, "flat"],
349
315
  ["Agent fail rate", aggregates.agentFailRate, "flat"],
@@ -388,80 +354,19 @@ function buildSummaryRows(aggregates, trend) {
388
354
  "Tokens spent on model reasoning/thinking, billed separately from prompt and completion.",
389
355
  ],
390
356
  ["LLM wall time", fmtMs(llm.totalDurationMs), trend.latency],
391
- ];
392
- // WS-5: denominator-fixed coverage rows (only when we have real data).
393
- if (coverage && !Number.isNaN(coverage.rate)) {
394
- summaryRows.push([
395
- "Coverage rate",
396
- pct(coverage.rate, 1),
397
- "flat",
398
- "Distinct accepted refs / total stash assets (denominator-fixed). Shows what fraction of the corpus has been touched.",
399
- ], [
400
- "Eligible fraction",
401
- pct(coverage.eligibleFraction, 1),
402
- "flat",
403
- "Eligible assets / total stash assets. Fraction the improve pipeline actively considers.",
404
- ], [
357
+ [
405
358
  "Coverage accepted",
406
359
  num(coverage.acceptedProposals),
407
360
  "flat",
408
- "Total accepted proposals in the window (raw volume — includes repeated rewrites of the same asset).",
409
- ], [
410
- "Churn ratio",
411
- Number.isFinite(coverage.churnRatio) ? num(coverage.churnRatio) : "—",
412
- Number.isFinite(coverage.churnRatio) && coverage.churnRatio > 1.5 ? "down" : "flat",
413
- "Accepted proposals / distinct refs touched. >1.5 = the loop is repeatedly rewriting the same assets (churn, not coverage).",
414
- ]);
415
- }
416
- // Enrichment-vs-minting policy rollup (reporting-only).
417
- if (minting && Number.isFinite(minting.share)) {
418
- summaryRows.push([
419
- "Enrichment-lane minted share",
420
- pct(minting.share, 1),
421
- minting.share > 0.05 ? "down" : "flat",
422
- `New assets minted by enrichment lanes / their accepted total (${minting.minted} minted vs ${minting.updated} updated). Enrichment lanes are ratified to edit existing assets only; WARN >5%, FAIL >15%.`,
423
- ]);
424
- }
425
- // WS-5: perf telemetry rows (only when at least one run reported telemetry).
426
- if (perf.runsWithTelemetry > 0) {
427
- const embedCacheTotal = perf.embedCacheHits + perf.embedCacheMisses;
428
- const embedCacheHitRate = embedCacheTotal > 0 ? pct(perf.embedCacheHits / embedCacheTotal, 1) : "—";
429
- summaryRows.push([
430
- "Embed cache hit rate",
431
- embedCacheHitRate,
432
- "flat",
433
- "Fraction of embedding lookups served from cache (>95% is healthy). Aggregated across WS-5 runs.",
434
- ], [
435
- "Embed wall time",
436
- fmtMs(perf.embedMs),
437
- "flat",
438
- "Cumulative embedding wall-clock time across consolidation runs in the window.",
439
- ], [
440
- "Dedup pool size",
441
- num(perf.dedupPoolSize),
442
- "flat",
443
- "Memory pool size after incremental narrowing, before the limit cap. WS-5 perf telemetry.",
444
- ], [
445
- "Over-budget consolidation runs",
446
- String(perf.overBudgetRuns),
447
- perf.overBudgetRuns > 0 ? "down" : "flat",
448
- "Runs where consolidation alone exceeded the total run budget (estimatedBudgetFractionUsed > 1.0).",
449
- ]);
450
- }
451
- // WS-5: degradation metrics rows.
452
- if (degradation) {
453
- summaryRows.push([
454
- "Corpus diversity (Gini)",
455
- num(degradation.corpusCentroidDistance),
456
- degradation.entrenchmentFlagged || degradation.salienceUniformityFlagged ? "down" : "flat",
457
- `Gini coefficient of positive retrieval_salience values across ${degradation.retrievalSalienceSampleSize} resolvable assets. Two-tailed: >0.35 = entrenchment risk; <0.08 = collapsed toward uniform.`,
458
- ], [
459
- "Merge fidelity contradiction rate",
460
- pct(degradation.mergeFidelityContradictionRate, 1),
361
+ "Accepted proposals in the window (raw volume — includes repeated rewrites of the same asset).",
362
+ ],
363
+ [
364
+ "Distinct assets touched",
365
+ num(coverage.distinctRefs),
461
366
  "flat",
462
- "Fraction of consolidated proposals that involved a contradiction, from consolidation result envelopes.",
463
- ]);
464
- }
367
+ "Distinct assets touched by accepted proposals in the window.",
368
+ ],
369
+ ];
465
370
  return summaryRows;
466
371
  }
467
372
  /**
@@ -2,28 +2,13 @@
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  /**
5
- * Lanes ratified as ENRICHMENT-ONLY: they may propose edits to existing
6
- * assets (metadata, relations, content refresh) but must not mint new ones.
7
- * New-asset generation belongs to the signal-gated minting lanes
8
- * (extract/distill/memory-inference).
9
- */
10
- export const ENRICHMENT_LANES = ["proactive", "high-salience", "signal-delta"];
11
- /** Minted share of enrichment-lane accepts that triggers a WARN advisory. */
12
- export const ENRICHMENT_MINTED_WARN_SHARE = 0.05;
13
- /** Minted share of enrichment-lane accepts that triggers a FAIL advisory. */
14
- export const ENRICHMENT_MINTED_FAIL_SHARE = 0.15;
15
- /**
16
- * Cron task failure rate at or above which the `task-fail-rate` health advisory
17
- * warns. 0.05 (5%) is the SAME threshold the HTML report already applies as its
18
- * fail-rate pass/warn cutoff (`failOk = taskFailRate < 0.05` in
19
- * src/commands/health/html-report.ts) — this constant makes it the single
20
- * source so the advisory and the rendered badge cannot drift.
5
+ * Cron task failure rate at or above which the `task-fail-rate` advisory
6
+ * warns; also the HTML report's fail-rate badge cutoff, so the two cannot drift.
21
7
  */
22
8
  export const TASK_FAIL_RATE_WARN = 0.05;
23
9
  /**
24
10
  * Minimum task_history rows a single task_id needs in the window before its
25
- * per-task fail rate feeds the `task-fail-rate` advisory's "worst single
26
- * task" signal. Guards against a rarely-run task (e.g. 1 failure out of 1
27
- * run) reading as a 100% fail rate off a single noisy sample.
11
+ * own fail rate feeds `task-fail-rate` — a task run once and failed once is
12
+ * not a 100% signal.
28
13
  */
29
14
  export const MIN_ROWS_FOR_WORST_TASK_FAIL_RATE = 5;
@@ -3,17 +3,14 @@
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  /**
5
5
  * Window handling for `akm health`: `--window-compare` / `--windows` parsing,
6
- * per-window metric assembly, log-backing partition, and delta computation.
6
+ * per-window metric assembly, and delta computation between two windows.
7
7
  */
8
- import fs from "node:fs";
9
8
  import { UsageError } from "../../core/errors.js";
10
9
  import { readEvents } from "../../core/events.js";
11
- import { buildTaskRunId, getLoggedRunIds } from "../../core/logs-db.js";
12
10
  import { DURATION_UNITS, parseDuration } from "../../core/time.js";
13
11
  import { queryTaskHistory } from "../../storage/repositories/task-history-repository.js";
14
- import { buildImproveSkipSummary, computeWallTimeStats, countAgentFailureReasons, isAgentTaskHistoryRow, roundRate, summarizeImproveCompleted, summarizeImproveRuns, taskFailureDetail, } from "./improve-metrics.js";
12
+ import { buildImproveSkipSummary, computeWallTimeStats, computeWindowProposalCoverage, countAgentFailureReasons, isAgentTaskHistoryRow, roundRate, summarizeImproveRuns, taskFailureDetail, } from "./improve-metrics.js";
15
13
  import { readLlmUsageAggregate } from "./llm-usage.js";
16
- import { computeDegradationMetrics, computeDenominatorFixedCoverage } from "./metrics.js";
17
14
  import { buildPerRunSummaries } from "./task-runs.js";
18
15
  import { ACTIVE_RUN_WARN_MS, IMPROVE_COMPLETED_EVENT, } from "./types.js";
19
16
  /**
@@ -71,28 +68,30 @@ export function parseWindowSpec(raw) {
71
68
  ...(fields.until ? { until: fields.until } : {}),
72
69
  };
73
70
  }
74
- /** Hard-coded list of "interesting" metric paths for window-compare deltas. */
71
+ /**
72
+ * Metric paths diffed between the earliest and latest window (`deltas` in the
73
+ * JSON result; the delta column of `--format md`). Paths are relative to a
74
+ * {@link WindowResult}.
75
+ */
75
76
  export const INTERESTING_DELTA_PATHS = [
76
77
  "improve.actions.reflect.failed",
77
- "improve.actions.reflect.guardRejected",
78
- "improve.actions.distill.llmFailed",
79
78
  "improve.actions.distill.queued",
80
- "improve.actions.distill.deferred",
79
+ "improve.actions.distill.llmFailed",
81
80
  "improve.consolidation.promoted",
82
81
  "improve.memoryInference.written",
83
82
  "improve.memoryInference.yieldRate",
84
83
  "improve.memoryInference.skippedNoFacts",
85
- "improve.memoryInference.htmlErrorCount",
86
- "improve.graphExtraction.cacheHitRate",
87
84
  "improve.graphExtraction.failures",
88
- "improve.graphExtraction.htmlErrors",
89
- "improve.graphExtraction.nonArrayBatchFailures",
90
- "improve.sessionExtraction.sessionsScanned",
91
- "improve.sessionExtraction.proposalsCreated",
92
85
  "improve.autoAccept.promoted",
93
86
  "improve.autoAccept.validationFailed",
87
+ "improve.coverage.acceptedProposals",
88
+ "improve.coverage.distinctRefs",
94
89
  "improve.wallTime.medianMs",
95
90
  "improve.wallTime.p95Ms",
91
+ "metrics.llmUsage.calls",
92
+ "metrics.llmUsage.totalTokens",
93
+ "metrics.llmUsage.totalDurationMs",
94
+ "metrics.llmUsage.failures",
96
95
  ];
97
96
  export function readNumericPath(obj, path) {
98
97
  const parts = path.split(".");
@@ -123,74 +122,66 @@ export function computeDeltas(first, last) {
123
122
  return out;
124
123
  }
125
124
  /**
126
- * Partition task_history rows into "should have a log" (non-null log_path) and
127
- * "log is actually backed". A run counts as backed when logs.db holds rows for
128
- * its run_id (#579 — the DB is the primary record); rows written before logs.db
129
- * existed fall back to the transitional on-disk file check. `logsDb` may be
130
- * undefined when logs.db could not be opened — then only the file check runs.
125
+ * Task-history-derived rates, shared by the main health path
126
+ * (`health.ts`'s `gatherTaskHistoryPhase`) and {@link buildWindowMetrics}
127
+ * below — previously computed twice with the only difference being whether
128
+ * the caller had already bounded `taskRows` by an `until`. Pure projection of
129
+ * already-queried rows; callers own the `since`/`until` query.
131
130
  */
132
- export function partitionLogBackedRows(taskRows, logsDb) {
133
- const withLogs = taskRows.filter((row) => row.log_path !== null);
134
- const loggedRunIds = logsDb
135
- ? getLoggedRunIds(logsDb, withLogs.map((row) => buildTaskRunId(row.task_id, row.started_at)))
136
- : new Set();
137
- const backed = withLogs.filter((row) => loggedRunIds.has(buildTaskRunId(row.task_id, row.started_at)) ||
138
- (row.log_path !== null && fs.existsSync(row.log_path)));
139
- return { withLogs, backed };
140
- }
141
- export function buildWindowMetrics(db, stateDbPath, since, until, now = () => Date.now(), logsDb) {
142
- const taskRows = queryTaskHistory(db, { since }).filter((row) => {
143
- const startMs = new Date(row.started_at).getTime();
144
- const untilMs = new Date(until).getTime();
145
- return !Number.isFinite(untilMs) || startMs < untilMs;
146
- });
147
- const { withLogs: taskRowsWithLogs, backed: existingLogRows } = partitionLogBackedRows(taskRows, logsDb);
131
+ export function computeTaskWindowRates(taskRows, now) {
148
132
  const failedTaskRows = taskRows.filter((row) => row.status === "failed");
149
133
  const activeRows = taskRows.filter((row) => row.status === "active" && row.completed_at === null);
150
- const stuckActiveRuns = activeRows.filter((row) => now() - new Date(row.started_at).getTime() > ACTIVE_RUN_WARN_MS).length;
134
+ const stuckActiveRows = activeRows.filter((row) => now() - new Date(row.started_at).getTime() > ACTIVE_RUN_WARN_MS);
151
135
  const agentRows = taskRows.filter((row) => isAgentTaskHistoryRow(row));
152
136
  const agentFailures = agentRows.filter((row) => {
153
137
  const detail = taskFailureDetail(row);
154
138
  return typeof detail?.reason === "string" && detail.reason.length > 0;
155
139
  });
156
- const logBackingRate = taskRowsWithLogs.length === 0 ? 1 : existingLogRows.length / taskRowsWithLogs.length;
157
- const taskFailRate = taskRows.length === 0 ? 0 : failedTaskRows.length / taskRows.length;
158
- const agentFailureRate = agentRows.length === 0 ? 0 : agentFailures.length / agentRows.length;
159
- const agentFailureReasonCounts = countAgentFailureReasons(agentFailures);
160
- const improveInvoked = readEvents({ since, type: "improve_invoked" }, { dbPath: stateDbPath }).events.filter((event) => new Date(event.ts ?? since).getTime() < new Date(until).getTime()).length;
161
- const improveCompletedEvents = readEvents({ since, type: IMPROVE_COMPLETED_EVENT }, { dbPath: stateDbPath }).events.filter((event) => new Date(event.ts ?? since).getTime() < new Date(until).getTime());
162
- const improveSkippedEvents = readEvents({ since, type: "improve_skipped" }, { dbPath: stateDbPath }).events.filter((event) => new Date(event.ts ?? since).getTime() < new Date(until).getTime());
163
- const eventsMetrics = summarizeImproveCompleted(improveCompletedEvents);
164
- const { metrics: improveSummary, runCount } = summarizeImproveRuns(db, since, until);
165
- improveSummary.invoked = improveInvoked;
166
- improveSummary.completed = eventsMetrics.completed;
167
- const skipSummary = buildImproveSkipSummary(improveSkippedEvents);
168
- improveSummary.skipped = skipSummary.skipped;
169
- improveSummary.skipReasons = skipSummary.skipReasons;
170
- // Preserve the per-phase aggregation computed by summarizeImproveRuns and
171
- // derive top-level wall times from the same improve-runs window so counts
172
- // and percentiles stay aligned with per-run reporting.
140
+ return {
141
+ taskFailRate: taskRows.length === 0 ? 0 : failedTaskRows.length / taskRows.length,
142
+ agentFailureRate: agentRows.length === 0 ? 0 : agentFailures.length / agentRows.length,
143
+ agentFailureReasonCounts: countAgentFailureReasons(agentFailures),
144
+ stuckActiveRuns: stuckActiveRows.length,
145
+ stuckActiveRows,
146
+ };
147
+ }
148
+ /**
149
+ * Improve-pipeline window summary, shared by the main health path
150
+ * (`health.ts`) and {@link buildWindowMetrics} below: events-derived
151
+ * invoked/completed/skipped counts, the `result_json` aggregate, wall-time
152
+ * stats from the same per-run rows, and accepted-proposal coverage, all over
153
+ * one `[since, until)`.
154
+ */
155
+ export function buildImproveWindowSummary(db, stateDbPath, since, until) {
156
+ const untilMs = new Date(until).getTime();
157
+ const eventsBeforeUntil = (type) => readEvents({ since, type }, { dbPath: stateDbPath }).events.filter((event) => new Date(event.ts ?? since).getTime() < untilMs);
158
+ const { metrics: improve, runCount } = summarizeImproveRuns(db, since, until);
159
+ improve.invoked = eventsBeforeUntil("improve_invoked").length;
160
+ improve.completed = eventsBeforeUntil(IMPROVE_COMPLETED_EVENT).length;
161
+ const skipSummary = buildImproveSkipSummary(eventsBeforeUntil("improve_skipped"));
162
+ improve.skipped = skipSummary.skipped;
163
+ improve.skipReasons = skipSummary.skipReasons;
164
+ // Wall times come from the same improve-runs window as the per-run
165
+ // reporting so counts and percentiles stay aligned with it.
173
166
  const perRunSummaries = buildPerRunSummaries(db, since, until);
174
- const wallTimes = perRunSummaries.map((run) => run.wallTimeMs).filter((ms) => Number.isFinite(ms) && ms > 0);
175
- improveSummary.wallTime = computeWallTimeStats(wallTimes, improveSummary.wallTime.byPhase);
176
- // WS-5: Compute denominator-fixed coverage from the most recent run's
177
- // memorySummary (totalAssets = eligible + derived — the fixed denominator).
178
- const totalAssets = improveSummary.memorySummary.eligible + improveSummary.memorySummary.derived;
179
- improveSummary.coverage = computeDenominatorFixedCoverage(db, totalAssets, improveSummary.memorySummary.eligible, since, until);
180
- // WS-5: Compute per-run degradation metrics (corpus diversity, merge fidelity,
181
- // generation distribution, oracle spot-check). Health VIEWS only.
182
- const degradation = computeDegradationMetrics(db, since, until);
183
- if (degradation) {
184
- improveSummary.degradation = degradation;
185
- }
167
+ improve.wallTime = computeWallTimeStats(perRunSummaries.map((run) => run.wallTimeMs).filter((ms) => Number.isFinite(ms) && ms > 0));
168
+ improve.coverage = computeWindowProposalCoverage(db, since, until);
169
+ return { improve, runCount, perRunSummaries };
170
+ }
171
+ export function buildWindowMetrics(db, stateDbPath, since, until, now = () => Date.now()) {
172
+ const untilMs = new Date(until).getTime();
173
+ const taskRows = queryTaskHistory(db, { since }).filter((row) => {
174
+ const startMs = new Date(row.started_at).getTime();
175
+ return !Number.isFinite(untilMs) || startMs < untilMs;
176
+ });
177
+ const rates = computeTaskWindowRates(taskRows, now);
178
+ const { improve, runCount } = buildImproveWindowSummary(db, stateDbPath, since, until);
186
179
  const metrics = {
187
- taskFailRate: roundRate(taskFailRate),
188
- agentFailureRate: roundRate(agentFailureRate),
189
- agentFailureReasonCounts,
190
- stuckActiveRuns,
191
- logBackingRate: roundRate(logBackingRate),
192
- probeRoundTripMs: null,
180
+ taskFailRate: roundRate(rates.taskFailRate),
181
+ agentFailureRate: roundRate(rates.agentFailureRate),
182
+ agentFailureReasonCounts: rates.agentFailureReasonCounts,
183
+ stuckActiveRuns: rates.stuckActiveRuns,
193
184
  llmUsage: readLlmUsageAggregate(stateDbPath, since, until),
194
185
  };
195
- return { improve: improveSummary, metrics, runs: runCount };
186
+ return { improve, metrics, runs: runCount };
196
187
  }