akm-cli 0.9.0-rc.8 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (450) hide show
  1. package/CHANGELOG.md +1063 -44
  2. package/README.md +51 -25
  3. package/SECURITY.md +14 -1
  4. package/STABILITY.md +497 -0
  5. package/dist/akm +148 -35
  6. package/dist/{akm-migrate-storage → akm-migrate} +6 -9
  7. package/dist/assets/hints/cli-hints-full.md +223 -95
  8. package/dist/assets/hints/cli-hints-short.md +85 -22
  9. package/dist/assets/improve-strategies/default.json +1 -1
  10. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  11. package/dist/assets/prompts/memory-infer-user.md +2 -3
  12. package/dist/assets/stash-skeleton/README.md +6 -5
  13. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +2 -0
  14. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +2 -0
  15. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +2 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +2 -0
  17. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +2 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -0
  21. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +2 -0
  22. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -0
  23. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -0
  24. package/dist/assets/stash-skeleton/facts/conventions/organization.md +20 -9
  25. package/dist/assets/tasks/core/extract.yml +1 -1
  26. package/dist/assets/tasks/core/version-check.yml +1 -1
  27. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  28. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  29. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  30. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  31. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  32. package/dist/assets/templates/html/health.html +1 -3
  33. package/dist/assets/workflows/workflow-template.md +32 -15
  34. package/dist/cli/invocation.js +40 -15
  35. package/dist/cli/parse-args.js +0 -22
  36. package/dist/cli/retired-commands.js +121 -0
  37. package/dist/cli/shared.js +154 -22
  38. package/dist/cli/unknown-flags.js +236 -0
  39. package/dist/cli-node.mjs +2 -1
  40. package/dist/cli.js +696 -258
  41. package/dist/commands/agent/agent-dispatch.js +14 -3
  42. package/dist/commands/agent/contribute-cli.js +73 -88
  43. package/dist/commands/completions.js +79 -22
  44. package/dist/commands/config-cli.js +17 -150
  45. package/dist/commands/env/env-cli.js +59 -143
  46. package/dist/commands/env/env.js +12 -163
  47. package/dist/commands/env/marker-path.js +6 -0
  48. package/dist/commands/env/secret-cli.js +36 -66
  49. package/dist/commands/env/secret.js +24 -57
  50. package/dist/commands/feedback-cli.js +141 -87
  51. package/dist/commands/health/accept-rate.js +58 -0
  52. package/dist/commands/health/advisories.js +3 -4
  53. package/dist/commands/health/checks.js +85 -23
  54. package/dist/commands/health/html-report.js +7 -10
  55. package/dist/commands/health/improve-metrics.js +25 -83
  56. package/dist/commands/health/md-report.js +5 -9
  57. package/dist/commands/health/metrics.js +62 -20
  58. package/dist/commands/health/renderers.js +47 -0
  59. package/dist/commands/health/report-view-model.js +4 -5
  60. package/dist/commands/health/stash-exposure.js +1 -1
  61. package/dist/commands/health/surfaces.js +3 -48
  62. package/dist/commands/health/task-runs.js +3 -67
  63. package/dist/commands/health/types-improve.js +7 -0
  64. package/dist/commands/health.js +99 -28
  65. package/dist/commands/improve/anti-collapse.js +2 -2
  66. package/dist/commands/improve/autonomy-gate.js +68 -0
  67. package/dist/commands/improve/collapse-detector.js +41 -40
  68. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  69. package/dist/commands/improve/consolidate/merge.js +4 -0
  70. package/dist/commands/improve/consolidate.js +140 -1000
  71. package/dist/commands/improve/distill/promote-memory.js +12 -12
  72. package/dist/commands/improve/distill/quality-gate.js +6 -6
  73. package/dist/commands/improve/distill.js +58 -69
  74. package/dist/commands/improve/eligibility.js +105 -57
  75. package/dist/commands/improve/extract-cli.js +14 -133
  76. package/dist/commands/improve/improve-cli.js +98 -114
  77. package/dist/commands/improve/improve-result-file.js +1 -28
  78. package/dist/commands/improve/improve-strategies.js +8 -5
  79. package/dist/commands/improve/improve.js +128 -91
  80. package/dist/commands/improve/loop-stages.js +182 -20
  81. package/dist/commands/improve/memory/derived-ref.js +45 -43
  82. package/dist/commands/improve/memory/memory-belief.js +1 -1
  83. package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
  84. package/dist/commands/improve/memory/memory-improve.js +6 -5
  85. package/dist/commands/improve/outcome-loop.js +22 -65
  86. package/dist/commands/improve/preparation.js +114 -123
  87. package/dist/commands/improve/proactive-maintenance.js +2 -5
  88. package/dist/commands/improve/reflect.js +56 -160
  89. package/dist/commands/improve/salience.js +11 -122
  90. package/dist/commands/improve/source-identity.js +10 -38
  91. package/dist/commands/lint/base-linter.js +20 -124
  92. package/dist/commands/lint/env-key-rules.js +31 -47
  93. package/dist/commands/lint/index.js +249 -43
  94. package/dist/commands/{events.js → log.js} +33 -38
  95. package/dist/commands/migrate-cli.js +92 -12
  96. package/dist/commands/migration-tool.js +46 -0
  97. package/dist/commands/observability-cli.js +70 -209
  98. package/dist/commands/proposal/drain.js +101 -29
  99. package/dist/commands/proposal/proposal-cli.js +76 -48
  100. package/dist/commands/proposal/proposal.js +54 -18
  101. package/dist/commands/proposal/propose-cli.js +88 -0
  102. package/dist/commands/proposal/propose.js +23 -15
  103. package/dist/commands/proposal/repository.js +701 -278
  104. package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
  105. package/dist/commands/proposal/validators/proposal-validators.js +55 -7
  106. package/dist/commands/proposal/validators/proposals.js +4 -7
  107. package/dist/commands/read/curate.js +34 -53
  108. package/dist/commands/read/knowledge.js +150 -95
  109. package/dist/commands/read/registry-search.js +2 -2
  110. package/dist/commands/read/remember-cli.js +42 -15
  111. package/dist/commands/read/search-cli.js +180 -78
  112. package/dist/commands/read/search.js +58 -43
  113. package/dist/commands/read/show.js +197 -141
  114. package/dist/commands/registry-cli.js +12 -51
  115. package/dist/commands/remember.js +14 -57
  116. package/dist/commands/sources/add-cli.js +100 -31
  117. package/dist/commands/sources/bundle-cli.js +166 -0
  118. package/dist/commands/sources/bundle-config-ops.js +7 -2
  119. package/dist/commands/sources/info.js +18 -5
  120. package/dist/commands/sources/init.js +12 -12
  121. package/dist/commands/sources/installed-stashes.js +382 -98
  122. package/dist/commands/sources/schema-repair.js +3 -2
  123. package/dist/commands/sources/self-update.js +131 -38
  124. package/dist/commands/sources/source-add.js +72 -17
  125. package/dist/commands/sources/source-clone.js +129 -45
  126. package/dist/commands/sources/source-manage.js +43 -23
  127. package/dist/commands/sources/sources-cli.js +57 -208
  128. package/dist/commands/sources/stash-cli.js +46 -53
  129. package/dist/commands/tasks/tasks-cli.js +91 -97
  130. package/dist/commands/tasks/tasks.js +276 -421
  131. package/dist/commands/workflow-cli.js +175 -450
  132. package/dist/core/adapter/adapters/akm-adapter.js +47 -28
  133. package/dist/core/adapter/adapters/akm-lint.js +42 -27
  134. package/dist/core/adapter/adapters/akm-metadata.js +15 -44
  135. package/dist/core/adapter/adapters/akm-task-adapter.js +15 -13
  136. package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
  137. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  138. package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
  139. package/dist/core/adapter/adapters/index.js +6 -6
  140. package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
  141. package/dist/core/adapter/adapters/okf-adapter.js +187 -19
  142. package/dist/core/adapter/adapters/shared.js +3 -19
  143. package/dist/core/adapter/adapters/tool-dir-shared.js +8 -3
  144. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
  145. package/dist/core/adapter/detect-adapter.js +17 -0
  146. package/dist/core/adapter/recognize-match.js +6 -4
  147. package/dist/core/adapter/validate-context.js +214 -0
  148. package/dist/core/asset/akm-markdown.js +63 -0
  149. package/dist/core/asset/asset-placement.js +20 -6
  150. package/dist/core/asset/asset-ref.js +11 -9
  151. package/dist/core/asset/frontmatter-lint.js +30 -0
  152. package/dist/core/asset/frontmatter.js +37 -9
  153. package/dist/core/asset/markdown.js +40 -51
  154. package/dist/core/asset/resolve-ref.js +89 -18
  155. package/dist/core/asset/stash-meta.js +1 -1
  156. package/dist/core/bundle-id.js +51 -0
  157. package/dist/core/common.js +152 -38
  158. package/dist/core/config/config-io.js +12 -1
  159. package/dist/core/config/config-schema.js +35 -8
  160. package/dist/core/config/config-sources.js +55 -11
  161. package/dist/core/config/config-walker.js +25 -9
  162. package/dist/core/config/config.js +9 -48
  163. package/dist/core/config/experimental.js +21 -0
  164. package/dist/core/config/schema/embedding.js +5 -1
  165. package/dist/core/config/schema/experimental.js +30 -0
  166. package/dist/core/config/schema/improve-processes.js +0 -6
  167. package/dist/core/config/schema/improve.js +21 -3
  168. package/dist/core/config/schema/index-config.js +8 -15
  169. package/dist/core/config/schema/output.js +4 -1
  170. package/dist/core/config/schema/setup.js +9 -18
  171. package/dist/core/config/schema/sources-bundles.js +49 -33
  172. package/dist/core/config/schema/workflow.js +3 -3
  173. package/dist/core/env-secret-ref.js +76 -46
  174. package/dist/core/errors.js +18 -12
  175. package/dist/core/events.js +46 -128
  176. package/dist/core/file-change.js +6 -5
  177. package/dist/core/fs-txn.js +83 -7
  178. package/dist/core/git-message.js +2 -2
  179. package/dist/core/improve-result.js +1 -100
  180. package/dist/core/lesson-lint.js +1 -17
  181. package/dist/core/logs-db.js +2 -1
  182. package/dist/core/migration-operation.js +16 -0
  183. package/dist/core/mutation-target.js +78 -0
  184. package/dist/core/parse.js +4 -1
  185. package/dist/core/paths.js +17 -20
  186. package/dist/core/recognition-util.js +12 -14
  187. package/dist/core/redaction.js +34 -0
  188. package/dist/core/standards/resolve-standards-context.js +2 -14
  189. package/dist/core/standards/resolve-stash-standards.js +2 -2
  190. package/dist/core/standards/resolve-type-conventions.js +2 -2
  191. package/dist/core/state/migrations.js +41 -18
  192. package/dist/core/state-db.js +5 -14
  193. package/dist/core/structured.js +1 -1
  194. package/dist/core/subprocess.js +6 -4
  195. package/dist/core/text-truncation.js +9 -5
  196. package/dist/core/type-presentation.js +3 -3
  197. package/dist/core/warn.js +0 -3
  198. package/dist/core/write-source.js +771 -95
  199. package/dist/indexer/bundle-identity-guard.js +3 -2
  200. package/dist/indexer/db/graph-db.js +0 -24
  201. package/dist/indexer/ensure-index.js +1 -0
  202. package/dist/indexer/graph/graph-boost.js +9 -34
  203. package/dist/indexer/graph/graph-extraction.js +8 -5
  204. package/dist/indexer/index-writer-lock.js +53 -17
  205. package/dist/indexer/index-written-assets.js +16 -22
  206. package/dist/indexer/indexer.js +497 -239
  207. package/dist/indexer/installations.js +14 -96
  208. package/dist/indexer/passes/dir-staleness.js +16 -9
  209. package/dist/indexer/passes/memory-inference.js +11 -9
  210. package/dist/indexer/passes/metadata.js +113 -47
  211. package/dist/indexer/scan/doc-to-entry.js +38 -1
  212. package/dist/indexer/scan/drain-dir.js +13 -23
  213. package/dist/indexer/search/db-search.js +99 -54
  214. package/dist/indexer/search/fts-query.js +47 -24
  215. package/dist/indexer/search/ranking-contributors.js +42 -20
  216. package/dist/indexer/search/ranking.js +18 -99
  217. package/dist/indexer/search/search-fields.js +7 -2
  218. package/dist/indexer/search/search-source.js +82 -93
  219. package/dist/indexer/usage/usage-events.js +0 -89
  220. package/dist/indexer/walk/file-context.js +2 -1
  221. package/dist/indexer/walk/matchers.js +30 -43
  222. package/dist/indexer/walk/path-resolver.js +7 -2
  223. package/dist/indexer/walk/walker.js +38 -12
  224. package/dist/integrations/agent/builders.js +0 -6
  225. package/dist/integrations/agent/config.js +2 -2
  226. package/dist/integrations/agent/detect.js +49 -19
  227. package/dist/integrations/agent/engine-fallback.js +76 -0
  228. package/dist/integrations/agent/profiles.js +14 -0
  229. package/dist/integrations/agent/prompts.js +12 -8
  230. package/dist/integrations/agent/runner-dispatch.js +4 -2
  231. package/dist/integrations/agent/runner.js +0 -1
  232. package/dist/integrations/agent/spawn.js +5 -6
  233. package/dist/integrations/github.js +1 -1
  234. package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
  235. package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
  236. package/dist/integrations/harnesses/claude/session-log.js +0 -10
  237. package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
  238. package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
  239. package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
  240. package/dist/integrations/harnesses/index.js +3 -7
  241. package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
  242. package/dist/integrations/harnesses/opencode/session-log.js +0 -15
  243. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  244. package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
  245. package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
  246. package/dist/integrations/lockfile.js +101 -6
  247. package/dist/integrations/session-logs/index.js +3 -28
  248. package/dist/llm/client.js +136 -100
  249. package/dist/llm/embedders/remote.js +13 -5
  250. package/dist/llm/feature-gate.js +4 -12
  251. package/dist/llm/graph-extract.js +5 -11
  252. package/dist/llm/memory-infer.js +144 -1
  253. package/dist/llm/metadata-enhance.js +5 -7
  254. package/dist/llm/structured-call.js +1 -1
  255. package/dist/llm/usage-persist.js +26 -5
  256. package/dist/llm/usage-telemetry.js +25 -2
  257. package/dist/output/cli-hints.js +1 -2
  258. package/dist/output/context.js +22 -7
  259. package/dist/output/format-exempt.js +80 -0
  260. package/dist/output/generic-render.js +259 -0
  261. package/dist/output/render-registry.js +57 -0
  262. package/dist/output/renderers.js +14 -36
  263. package/dist/output/shapes/curate.js +10 -1
  264. package/dist/output/shapes/events.js +12 -7
  265. package/dist/output/shapes/helpers.js +56 -83
  266. package/dist/output/shapes/migrate.js +8 -0
  267. package/dist/output/shapes/passthrough.js +7 -41
  268. package/dist/output/shapes/proposal/producer.js +15 -7
  269. package/dist/output/shapes.js +2 -9
  270. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  271. package/dist/output/text/bundle-show.js +7 -0
  272. package/dist/output/text/command-format.js +164 -96
  273. package/dist/output/text/env.js +1 -3
  274. package/dist/output/text/events.js +8 -7
  275. package/dist/output/text/health-format.js +103 -0
  276. package/dist/output/text/health.js +7 -0
  277. package/dist/output/text/helpers.js +10 -8
  278. package/dist/output/text/lint-format.js +43 -0
  279. package/dist/output/text/{save.js → lint.js} +2 -2
  280. package/dist/output/text/migrate.js +88 -0
  281. package/dist/output/text/proposal/producer.js +4 -2
  282. package/dist/output/text/proposal-format.js +44 -72
  283. package/dist/output/text/registry-commands.js +1 -2
  284. package/dist/output/text/show-directives.js +15 -7
  285. package/dist/output/text/status-list.js +32 -0
  286. package/dist/output/text/sync.js +5 -0
  287. package/dist/output/text/workflow-format.js +24 -203
  288. package/dist/output/text/workflow.js +1 -7
  289. package/dist/output/text.js +16 -17
  290. package/dist/registry/factory.js +4 -6
  291. package/dist/registry/origin-resolve.js +16 -27
  292. package/dist/registry/providers/skills-sh.js +3 -3
  293. package/dist/registry/providers/static-index.js +13 -23
  294. package/dist/registry/resolve.js +42 -7
  295. package/dist/registry/semver.js +34 -84
  296. package/dist/runtime.js +2 -23
  297. package/dist/scripts/akm-migrate-node.js +60290 -0
  298. package/dist/scripts/akm-migrate.js +59628 -0
  299. package/dist/setup/detect.js +42 -15
  300. package/dist/setup/registry-stash-loader.js +2 -2
  301. package/dist/setup/setup.js +236 -136
  302. package/dist/setup/steps/connection.js +7 -9
  303. package/dist/setup/steps/platforms.js +9 -9
  304. package/dist/setup/steps/semantic.js +15 -3
  305. package/dist/setup/steps/sources.js +12 -13
  306. package/dist/setup/steps/stashdir.js +2 -3
  307. package/dist/setup/steps/tasks.js +237 -120
  308. package/dist/sources/freshness.js +1 -1
  309. package/dist/sources/provider-factory.js +11 -17
  310. package/dist/sources/providers/filesystem.js +2 -3
  311. package/dist/sources/providers/git-install.js +278 -34
  312. package/dist/sources/providers/git-provider.js +25 -23
  313. package/dist/sources/providers/git-stash.js +395 -106
  314. package/dist/sources/providers/git.js +2 -2
  315. package/dist/sources/providers/npm.js +16 -19
  316. package/dist/sources/providers/provider-utils.js +7 -4
  317. package/dist/sources/providers/sync-from-ref.js +3 -9
  318. package/dist/sources/providers/website.js +6 -1
  319. package/dist/sources/resolve.js +6 -5
  320. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  321. package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
  322. package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
  323. package/dist/sources/snapshot-fetchers/github.js +100 -0
  324. package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
  325. package/dist/sources/snapshot-fetchers/registry.js +17 -1
  326. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  327. package/dist/sources/snapshot-fetchers/rss.js +282 -0
  328. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  329. package/dist/sources/snapshot-fetchers/website-ingest.js +566 -268
  330. package/dist/sources/snapshot-fetchers/x.js +910 -0
  331. package/dist/storage/database.js +7 -0
  332. package/dist/storage/engines/sqlite-migrations.js +23 -111
  333. package/dist/storage/managed-db.js +2 -2
  334. package/dist/storage/repositories/canaries-repository.js +1 -1
  335. package/dist/storage/repositories/events-repository.js +27 -11
  336. package/dist/storage/repositories/improve-runs-repository.js +6 -12
  337. package/dist/storage/repositories/index-connection.js +17 -6
  338. package/dist/storage/repositories/index-entries-repository.js +151 -240
  339. package/dist/storage/repositories/index-entry-mapper.js +15 -11
  340. package/dist/storage/repositories/index-fts-repository.js +5 -2
  341. package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
  342. package/dist/storage/repositories/index-meta-repository.js +2 -3
  343. package/dist/storage/repositories/index-schema.js +10 -25
  344. package/dist/storage/repositories/index-utility-repository.js +15 -28
  345. package/dist/storage/repositories/index-vec-repository.js +6 -1
  346. package/dist/storage/repositories/outcome-repository.js +119 -0
  347. package/dist/storage/repositories/proposals-repository.js +296 -59
  348. package/dist/storage/repositories/registry-cache.js +19 -0
  349. package/dist/storage/repositories/salience-repository.js +172 -0
  350. package/dist/storage/repositories/task-history-repository.js +15 -13
  351. package/dist/storage/repositories/workflow-runs-repository.js +52 -40
  352. package/dist/tasks/backends/cron.js +105 -15
  353. package/dist/tasks/backends/index.js +1 -1
  354. package/dist/tasks/backends/launchd.js +85 -38
  355. package/dist/tasks/backends/schtasks.js +135 -15
  356. package/dist/tasks/embedded.js +56 -40
  357. package/dist/tasks/parser.js +7 -157
  358. package/dist/tasks/resolve-akm-bin.js +137 -59
  359. package/dist/tasks/runner.js +79 -42
  360. package/dist/tasks/scheduler-invocation.js +220 -10
  361. package/dist/tasks/schema.js +24 -1
  362. package/dist/tasks/task-id.js +1 -3
  363. package/dist/tasks/validator.js +20 -6
  364. package/dist/workflows/authoring/authoring.js +94 -143
  365. package/dist/workflows/authoring/scope-key.js +1 -1
  366. package/dist/workflows/exec/frozen-judge.js +28 -2
  367. package/dist/workflows/exec/native-executor.js +77 -57
  368. package/dist/workflows/exec/param-secrets.js +9 -9
  369. package/dist/workflows/exec/run-workflow.js +133 -79
  370. package/dist/workflows/exec/step-work.js +219 -346
  371. package/dist/{migrate-storage-node.mjs → workflows/exec/unit-dispatch.js} +1 -5
  372. package/dist/workflows/ir/compile.js +141 -270
  373. package/dist/workflows/ir/freeze.js +40 -30
  374. package/dist/workflows/ir/params.js +135 -11
  375. package/dist/workflows/ir/plan-hash.js +1 -1
  376. package/dist/workflows/ir/schema.js +25 -26
  377. package/dist/workflows/parser.js +872 -307
  378. package/dist/workflows/program/expressions.js +20 -208
  379. package/dist/workflows/program/schema.js +7 -10
  380. package/dist/workflows/renderer.js +95 -68
  381. package/dist/workflows/resource-limits.js +2 -0
  382. package/dist/workflows/runtime/checkin.js +3 -3
  383. package/dist/workflows/runtime/plan-classifier.js +16 -75
  384. package/dist/workflows/runtime/runs.js +186 -127
  385. package/dist/workflows/runtime/unit-checkin.js +1 -1
  386. package/dist/workflows/runtime/unit-phases.js +2 -2
  387. package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
  388. package/dist/workflows/schema.js +1 -11
  389. package/dist/workflows/validate-summary.js +30 -36
  390. package/dist/workflows/validator.js +21 -62
  391. package/docs/README.md +68 -0
  392. package/docs/migration/README.md +8 -0
  393. package/docs/migration/release-notes/0.7.0.md +11 -11
  394. package/docs/migration/release-notes/0.9.0.md +208 -27
  395. package/docs/migration/v0.7-to-v0.8.md +46 -47
  396. package/docs/migration/v0.8-to-v0.9.md +564 -208
  397. package/docs/migration/v0.9.0-troubleshooting.md +561 -0
  398. package/docs/reference/README.md +12 -0
  399. package/docs/reference/cli.md +2253 -0
  400. package/docs/reference/configuration.md +358 -0
  401. package/docs/reference/data-and-telemetry.md +105 -42
  402. package/docs/reference/workflows.md +647 -0
  403. package/package.json +22 -11
  404. package/schemas/akm-asset-envelope.json +93 -0
  405. package/schemas/akm-config.json +81 -128
  406. package/schemas/akm-workflow.json +74 -73
  407. package/dist/assets/tasks/core/backup.yml +0 -5
  408. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  409. package/dist/cli/config-migrate.js +0 -1806
  410. package/dist/cli/config-validate.js +0 -41
  411. package/dist/commands/backup-cli.js +0 -56
  412. package/dist/commands/bundle/bundle-cli.js +0 -68
  413. package/dist/commands/bundle/bundle.js +0 -219
  414. package/dist/commands/graph/graph-cli.js +0 -124
  415. package/dist/commands/graph/graph.js +0 -489
  416. package/dist/commands/improve/extract-watch.js +0 -140
  417. package/dist/commands/mv-cli.js +0 -1221
  418. package/dist/commands/sources/history.js +0 -201
  419. package/dist/commands/tasks/default-tasks.js +0 -186
  420. package/dist/core/migration-backup.js +0 -1234
  421. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
  422. package/dist/llm/memory-infer-impl.js +0 -138
  423. package/dist/migrate/legacy/config-source-migration.js +0 -223
  424. package/dist/migrate/legacy/content-migration.js +0 -305
  425. package/dist/migrate/legacy/legacy-layout.js +0 -779
  426. package/dist/migrate/legacy/legacy-paths.js +0 -25
  427. package/dist/migrate/legacy/legacy-stash-json.js +0 -72
  428. package/dist/migrate/legacy/proposal-fs-import.js +0 -168
  429. package/dist/migrate/legacy/task-target-ref-migration.js +0 -272
  430. package/dist/migrate/legacy/three-db-cutover.js +0 -841
  431. package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
  432. package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
  433. package/dist/migrate/legacy-ref-grammar.js +0 -214
  434. package/dist/output/shapes/distill.js +0 -14
  435. package/dist/output/shapes/history.js +0 -11
  436. package/dist/output/text/distill.js +0 -6
  437. package/dist/output/text/enable-disable.js +0 -8
  438. package/dist/output/text/history.js +0 -6
  439. package/dist/registry/build-index.js +0 -382
  440. package/dist/schemas/akm-config.json +0 -4704
  441. package/dist/schemas/akm-task.json +0 -87
  442. package/dist/schemas/akm-workflow.json +0 -372
  443. package/dist/scripts/migrate-storage.js +0 -3816
  444. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  445. package/dist/workflows/cli.js +0 -53
  446. package/dist/workflows/exec/brief.js +0 -481
  447. package/dist/workflows/exec/report.js +0 -1460
  448. package/dist/workflows/exec/watch.js +0 -116
  449. package/dist/workflows/program/parser.js +0 -813
  450. package/dist/workflows/program/project.js +0 -104
@@ -1,6 +1,17 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
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
+ /**
5
+ * Repository for the state.db `proposals` table. Extracted verbatim from
6
+ * core/state-db.ts — queries and row-mapping unchanged, only relocated behind
7
+ * the repository boundary. Re-exported by core/state-db.ts so existing importers
8
+ * resolve.
9
+ *
10
+ * @module proposals-repository
11
+ */
12
+ import path from "node:path";
13
+ import { stashDirFor } from "../../core/asset/asset-placement.js";
14
+ import { bundleRefToString, isBundleSlug, parseBundleRef } from "../../core/asset/asset-ref.js";
4
15
  /** Serialize `Proposal.changes` for `metadata_json` (see {@link StoredFileChange}). */
5
16
  function changesToStored(changes) {
6
17
  return changes.map((c, i) => ({
@@ -11,42 +22,277 @@ function changesToStored(changes) {
11
22
  }
12
23
  /**
13
24
  * Reconstruct `Proposal.changes` from `metadata_json.changes` + the `content`
14
- * column. Legacy rows (persisted before the envelope existed) synthesize one
15
- * `update` entry with an empty `path` sentinel (resolve from the ref instead).
25
+ * column.
16
26
  */
17
27
  function storedToChanges(stored, content) {
18
28
  if (!Array.isArray(stored) || stored.length === 0) {
19
- return [{ path: "", after: content, op: "update" }];
29
+ throw new Error("Proposal metadata is missing changes.");
30
+ }
31
+ return stored.map((value, i) => {
32
+ if (typeof value !== "object" || value === null)
33
+ throw new Error("Proposal metadata has invalid changes.");
34
+ const c = value;
35
+ if (typeof c.path !== "string" ||
36
+ (c.op !== "create" && c.op !== "update" && c.op !== "delete") ||
37
+ (c.after !== undefined && typeof c.after !== "string") ||
38
+ (c.op === "delete" && c.after !== undefined) ||
39
+ (i > 0 && c.op !== "delete" && typeof c.after !== "string")) {
40
+ throw new Error("Proposal metadata has invalid changes.");
41
+ }
42
+ return {
43
+ path: c.path,
44
+ op: c.op,
45
+ ...(i === 0 ? (c.op === "delete" ? {} : { after: content }) : c.after !== undefined ? { after: c.after } : {}),
46
+ };
47
+ });
48
+ }
49
+ function currentProposalRef(ref, requireQualified = false) {
50
+ let parsed;
51
+ try {
52
+ parsed = parseBundleRef(ref);
53
+ }
54
+ catch (error) {
55
+ throw new Error(`Proposal row has an invalid current ref: ${ref}`, { cause: error });
56
+ }
57
+ const colon = parsed.conceptId.indexOf(":");
58
+ if (colon > 0 && stashDirFor(parsed.conceptId.slice(0, colon)) !== undefined) {
59
+ throw new Error(`Proposal row has an invalid current ref: ${ref}`);
60
+ }
61
+ if (parsed.fragment !== undefined)
62
+ throw new Error(`Proposal row ref contains a fragment: ${ref}`);
63
+ if (requireQualified && parsed.bundle === undefined)
64
+ throw new Error(`Proposal ref is not bundle-qualified: ${ref}`);
65
+ const canonical = bundleRefToString(parsed);
66
+ if (canonical !== ref)
67
+ throw new Error(`Proposal row ref is not canonical: ${ref}`);
68
+ return canonical;
69
+ }
70
+ function currentProposalTarget(value) {
71
+ if (typeof value !== "object" || value === null)
72
+ throw new Error("Proposal metadata is missing proposedTarget.");
73
+ const target = value;
74
+ if (typeof target.source !== "string" ||
75
+ !isBundleSlug(target.source) ||
76
+ typeof target.root !== "string" ||
77
+ !path.isAbsolute(target.root)) {
78
+ throw new Error("Proposal metadata has an invalid proposedTarget.");
79
+ }
80
+ return { source: target.source, root: path.resolve(target.root) };
81
+ }
82
+ function invalidPresentField(name) {
83
+ throw new Error(`Proposal metadata has an invalid ${name}.`);
84
+ }
85
+ const GATE_OUTCOMES = {
86
+ "auto-accepted": true,
87
+ deferred: true,
88
+ staged: true,
89
+ "auto-rejected": true,
90
+ };
91
+ function validatePresentMetadata(meta) {
92
+ const stringFields = ["sourceRun", "beforeHash", "backupContent"];
93
+ for (const field of stringFields) {
94
+ if (Object.hasOwn(meta, field) && typeof meta[field] !== "string")
95
+ invalidPresentField(field);
96
+ }
97
+ if (Object.hasOwn(meta, "confidence") &&
98
+ (typeof meta.confidence !== "number" ||
99
+ !Number.isFinite(meta.confidence) ||
100
+ meta.confidence < 0 ||
101
+ meta.confidence > 1)) {
102
+ invalidPresentField("confidence");
103
+ }
104
+ if (Object.hasOwn(meta, "review")) {
105
+ const review = meta.review;
106
+ if (typeof review !== "object" ||
107
+ review === null ||
108
+ (review.outcome !== "accepted" && review.outcome !== "rejected") ||
109
+ typeof review.decidedAt !== "string" ||
110
+ (review.reason !== undefined && typeof review.reason !== "string")) {
111
+ invalidPresentField("review");
112
+ }
113
+ }
114
+ if (Object.hasOwn(meta, "gateDecision")) {
115
+ const gate = meta.gateDecision;
116
+ if (typeof gate !== "object" ||
117
+ gate === null ||
118
+ typeof gate.outcome !== "string" ||
119
+ !Object.hasOwn(GATE_OUTCOMES, gate.outcome) ||
120
+ typeof gate.reason !== "string" ||
121
+ typeof gate.decidedAt !== "string" ||
122
+ (gate.measured !== undefined && (typeof gate.measured !== "number" || !Number.isFinite(gate.measured))) ||
123
+ (gate.contentHash !== undefined && typeof gate.contentHash !== "string") ||
124
+ (gate.gate !== undefined && typeof gate.gate !== "string")) {
125
+ invalidPresentField("gateDecision");
126
+ }
127
+ if (gate.thresholds !== undefined) {
128
+ const thresholds = gate.thresholds;
129
+ if (typeof thresholds !== "object" ||
130
+ thresholds === null ||
131
+ [thresholds.maxDiffLines, thresholds.minContentLines].some((value) => value !== undefined && (typeof value !== "number" || !Number.isFinite(value)))) {
132
+ invalidPresentField("gateDecision");
133
+ }
134
+ }
135
+ }
136
+ if (Object.hasOwn(meta, "acceptedTarget")) {
137
+ const target = meta.acceptedTarget;
138
+ if (typeof target !== "object" ||
139
+ target === null ||
140
+ typeof target.source !== "string" ||
141
+ !isBundleSlug(target.source) ||
142
+ typeof target.root !== "string" ||
143
+ !path.isAbsolute(target.root) ||
144
+ typeof target.path !== "string" ||
145
+ !path.isAbsolute(target.path) ||
146
+ typeof target.contentHash !== "string") {
147
+ invalidPresentField("acceptedTarget");
148
+ }
149
+ }
150
+ if (Object.hasOwn(meta, "eligibilitySource") && typeof meta.eligibilitySource !== "string") {
151
+ invalidPresentField("eligibilitySource");
20
152
  }
21
- return stored.map((c, i) => ({
22
- path: typeof c.path === "string" ? c.path : "",
23
- op: c.op === "create" || c.op === "delete" ? c.op : "update",
24
- ...(i === 0 ? (c.op === "delete" ? {} : { after: content }) : c.after !== undefined ? { after: c.after } : {}),
25
- }));
26
153
  }
27
154
  /**
28
155
  * Convert a raw `ProposalRow` to the public `Proposal` shape.
29
156
  */
30
157
  export function proposalRowToProposal(row) {
158
+ if (!["pending", "accepted", "rejected", "reverted"].includes(row.status)) {
159
+ throw new Error(`Proposal row has invalid status: ${row.status}`);
160
+ }
31
161
  let frontmatter;
32
- if (row.frontmatter_json) {
162
+ if (row.frontmatter_json !== null) {
33
163
  try {
34
- frontmatter = JSON.parse(row.frontmatter_json);
164
+ const parsed = JSON.parse(row.frontmatter_json);
165
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
166
+ throw new Error("Proposal frontmatter_json must contain an object.");
167
+ }
168
+ frontmatter = parsed;
35
169
  }
36
- catch {
37
- /* ignore corrupt frontmatter JSON */
170
+ catch (error) {
171
+ throw new Error("Proposal row has invalid frontmatter_json.", { cause: error });
38
172
  }
39
173
  }
40
174
  let meta = {};
41
175
  try {
42
- meta = JSON.parse(row.metadata_json);
176
+ const parsed = JSON.parse(row.metadata_json);
177
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
178
+ throw new Error("Proposal metadata_json must contain an object.");
179
+ }
180
+ meta = parsed;
181
+ }
182
+ catch (error) {
183
+ throw new Error("Proposal row has invalid metadata_json.", { cause: error });
184
+ }
185
+ validatePresentMetadata(meta);
186
+ const changes = Object.hasOwn(meta, "changes")
187
+ ? storedToChanges(meta.changes, row.content)
188
+ : [{ path: "", op: "update", after: row.content }];
189
+ const proposedTarget = Object.hasOwn(meta, "proposedTarget") ? currentProposalTarget(meta.proposedTarget) : undefined;
190
+ return {
191
+ id: row.id,
192
+ ref: currentProposalRef(row.ref),
193
+ status: row.status,
194
+ source: row.source,
195
+ ...(typeof meta.sourceRun === "string" ? { sourceRun: meta.sourceRun } : {}),
196
+ createdAt: row.created_at,
197
+ updatedAt: row.updated_at,
198
+ payload: {
199
+ content: row.content,
200
+ ...(frontmatter !== undefined ? { frontmatter } : {}),
201
+ },
202
+ changes,
203
+ ...(proposedTarget !== undefined ? { proposedTarget } : {}),
204
+ ...(typeof meta.beforeHash === "string" ? { beforeHash: meta.beforeHash } : {}),
205
+ ...(meta.review !== undefined ? { review: meta.review } : {}),
206
+ ...(typeof meta.confidence === "number" ? { confidence: meta.confidence } : {}),
207
+ ...(meta.gateDecision !== undefined ? { gateDecision: meta.gateDecision } : {}),
208
+ ...(typeof meta.backupContent === "string" ? { backupContent: meta.backupContent } : {}),
209
+ ...(meta.acceptedTarget !== undefined ? { acceptedTarget: meta.acceptedTarget } : {}),
210
+ ...(typeof meta.eligibilitySource === "string"
211
+ ? { eligibilitySource: meta.eligibilitySource }
212
+ : {}),
213
+ };
214
+ }
215
+ /**
216
+ * Lenient counterpart to {@link proposalRowToProposal} for the terminal-status
217
+ * (reject/archive) read paths (repository.ts `archiveProposal` /
218
+ * `rejectProposalDurably`). Those paths must succeed even on legacy rows
219
+ * minted before a field was required — e.g. 0.8-era rows missing
220
+ * `proposedTarget` and/or carrying empty/missing `changes[].path` metadata —
221
+ * so an operator can reject them instead of resorting to manual SQL.
222
+ *
223
+ * Per-field decode failures are swallowed: the offending field degrades to a
224
+ * safe placeholder (matching the strict decoder's own "no changes recorded"
225
+ * fallback) and the failure is appended to `decodeWarnings` instead of
226
+ * throwing. `id` / `status` / timestamps / `source` are trusted verbatim (the
227
+ * schema guarantees they're non-null strings). The strict decoder remains the
228
+ * default everywhere else, including every other read of a `pending` or
229
+ * `accepted` proposal (accept/revert must keep failing exactly as today on a
230
+ * malformed row).
231
+ */
232
+ export function proposalRowToProposalLenient(row) {
233
+ const warnings = [];
234
+ let meta = {};
235
+ try {
236
+ const parsed = JSON.parse(row.metadata_json);
237
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
238
+ throw new Error("metadata_json must contain an object.");
239
+ }
240
+ meta = parsed;
241
+ validatePresentMetadata(meta);
242
+ }
243
+ catch (error) {
244
+ warnings.push(`metadata_json is invalid (${error instanceof Error ? error.message : String(error)}); optional fields dropped.`);
245
+ meta = {};
246
+ }
247
+ let frontmatter;
248
+ if (row.frontmatter_json !== null) {
249
+ try {
250
+ const parsed = JSON.parse(row.frontmatter_json);
251
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
252
+ throw new Error("frontmatter_json must contain an object.");
253
+ }
254
+ frontmatter = parsed;
255
+ }
256
+ catch (error) {
257
+ warnings.push(`frontmatter_json is invalid (${error instanceof Error ? error.message : String(error)}); frontmatter dropped.`);
258
+ }
259
+ }
260
+ let ref;
261
+ try {
262
+ ref = currentProposalRef(row.ref);
263
+ }
264
+ catch (error) {
265
+ warnings.push(`ref is invalid (${error instanceof Error ? error.message : String(error)}); keeping the raw stored value.`);
266
+ ref = row.ref;
267
+ }
268
+ let changes;
269
+ try {
270
+ changes = Object.hasOwn(meta, "changes")
271
+ ? storedToChanges(meta.changes, row.content)
272
+ : [{ path: "", op: "update", after: row.content }];
273
+ }
274
+ catch (error) {
275
+ warnings.push(`changes metadata is invalid (${error instanceof Error ? error.message : String(error)}); degraded to a single placeholder change.`);
276
+ changes = [{ path: "", op: "update", after: row.content }];
277
+ }
278
+ let proposedTarget;
279
+ if (Object.hasOwn(meta, "proposedTarget")) {
280
+ try {
281
+ proposedTarget = currentProposalTarget(meta.proposedTarget);
282
+ }
283
+ catch (error) {
284
+ warnings.push(`proposedTarget is invalid (${error instanceof Error ? error.message : String(error)}); omitted.`);
285
+ }
43
286
  }
44
- catch {
45
- /* ignore */
287
+ else {
288
+ warnings.push("proposedTarget is missing (pre-WI-6.2 legacy row).");
289
+ }
290
+ if (!["pending", "accepted", "rejected", "reverted"].includes(row.status)) {
291
+ warnings.push(`status "${row.status}" is not a recognized value.`);
46
292
  }
47
293
  return {
48
294
  id: row.id,
49
- ref: row.ref,
295
+ ref,
50
296
  status: row.status,
51
297
  source: row.source,
52
298
  ...(typeof meta.sourceRun === "string" ? { sourceRun: meta.sourceRun } : {}),
@@ -56,19 +302,18 @@ export function proposalRowToProposal(row) {
56
302
  content: row.content,
57
303
  ...(frontmatter !== undefined ? { frontmatter } : {}),
58
304
  },
59
- changes: storedToChanges(meta.changes, row.content),
305
+ changes,
306
+ ...(proposedTarget !== undefined ? { proposedTarget } : {}),
60
307
  ...(typeof meta.beforeHash === "string" ? { beforeHash: meta.beforeHash } : {}),
61
308
  ...(meta.review !== undefined ? { review: meta.review } : {}),
62
309
  ...(typeof meta.confidence === "number" ? { confidence: meta.confidence } : {}),
63
310
  ...(meta.gateDecision !== undefined ? { gateDecision: meta.gateDecision } : {}),
64
311
  ...(typeof meta.backupContent === "string" ? { backupContent: meta.backupContent } : {}),
65
- ...(typeof meta.acceptedContentHash === "string" ? { acceptedContentHash: meta.acceptedContentHash } : {}),
66
312
  ...(meta.acceptedTarget !== undefined ? { acceptedTarget: meta.acceptedTarget } : {}),
67
- ...(meta.legacyAcceptedTargetDerived === true ? { legacyAcceptedTargetDerived: true } : {}),
68
- ...(meta.legacyAcceptedAssetWasAbsent === true ? { legacyAcceptedAssetWasAbsent: true } : {}),
69
313
  ...(typeof meta.eligibilitySource === "string"
70
314
  ? { eligibilitySource: meta.eligibilitySource }
71
315
  : {}),
316
+ ...(warnings.length > 0 ? { decodeWarnings: warnings } : {}),
72
317
  };
73
318
  }
74
319
  /**
@@ -78,15 +323,17 @@ export function proposalRowToProposal(row) {
78
323
  export function proposalToRowValues(proposal, stashDir) {
79
324
  // Fields that have no dedicated column live in metadata_json.
80
325
  const metaObj = {};
81
- // Legacy filesystem proposal.json objects (pre-envelope, imported by the
82
- // migrator's proposal-fs-import.ts) reach this mapper without `changes` at
83
- // runtime despite the type — or, if hand-edited, with a malformed value.
84
- // Synthesize the same sentinel entry the read path uses rather than letting
85
- // one corrupt legacy file abort a whole import batch.
86
- const safeChanges = Array.isArray(proposal.changes)
87
- ? proposal.changes.filter((c) => typeof c === "object" && c !== null)
88
- : undefined;
89
- metaObj.changes = changesToStored(safeChanges && safeChanges.length > 0 ? safeChanges : [{ path: "", after: proposal.payload.content, op: "update" }]);
326
+ if (!Array.isArray(proposal.changes) || proposal.changes.length === 0) {
327
+ throw new Error(`Proposal ${proposal.id} has no file changes.`);
328
+ }
329
+ if (proposal.changes.some((change) => typeof change.path !== "string" ||
330
+ change.path.length === 0 ||
331
+ !["create", "update", "delete"].includes(change.op) ||
332
+ (change.op === "delete" ? change.after !== undefined : typeof change.after !== "string"))) {
333
+ throw new Error(`Proposal ${proposal.id} has invalid file changes.`);
334
+ }
335
+ metaObj.changes = changesToStored(proposal.changes);
336
+ metaObj.proposedTarget = currentProposalTarget(proposal.proposedTarget);
90
337
  if (proposal.beforeHash !== undefined)
91
338
  metaObj.beforeHash = proposal.beforeHash;
92
339
  if (proposal.sourceRun !== undefined)
@@ -99,22 +346,15 @@ export function proposalToRowValues(proposal, stashDir) {
99
346
  metaObj.gateDecision = proposal.gateDecision;
100
347
  if (proposal.backupContent !== undefined)
101
348
  metaObj.backupContent = proposal.backupContent;
102
- if (proposal.acceptedContentHash !== undefined)
103
- metaObj.acceptedContentHash = proposal.acceptedContentHash;
104
349
  if (proposal.acceptedTarget !== undefined)
105
350
  metaObj.acceptedTarget = proposal.acceptedTarget;
106
- if (proposal.legacyAcceptedTargetDerived !== undefined) {
107
- metaObj.legacyAcceptedTargetDerived = proposal.legacyAcceptedTargetDerived;
108
- }
109
- if (proposal.legacyAcceptedAssetWasAbsent !== undefined) {
110
- metaObj.legacyAcceptedAssetWasAbsent = proposal.legacyAcceptedAssetWasAbsent;
111
- }
112
351
  if (proposal.eligibilitySource !== undefined)
113
352
  metaObj.eligibilitySource = proposal.eligibilitySource;
353
+ validatePresentMetadata(metaObj);
114
354
  return {
115
355
  id: proposal.id,
116
356
  stash_dir: stashDir,
117
- ref: proposal.ref,
357
+ ref: currentProposalRef(proposal.ref, true),
118
358
  status: proposal.status,
119
359
  source: proposal.source,
120
360
  created_at: proposal.createdAt,
@@ -173,7 +413,12 @@ export function listStateProposals(db, options = {}) {
173
413
  content, frontmatter_json, metadata_json
174
414
  FROM proposals ${where} ORDER BY created_at ASC, rowid ASC`)
175
415
  .all(...params);
176
- return rows.map(proposalRowToProposal);
416
+ // Archived rows (accepted/rejected/reverted) decode leniently so a
417
+ // terminal-status row that was rejected via the lenient path (or predates
418
+ // a required field) remains listable — a strict throw here would defeat
419
+ // the point of tolerating it at reject/archive time. Pending rows keep the
420
+ // strict decoder; nothing about the live queue changes here.
421
+ return rows.map((row) => (row.status === "pending" ? proposalRowToProposal(row) : proposalRowToProposalLenient(row)));
177
422
  }
178
423
  /**
179
424
  * Look up a single proposal by id, optionally scoped to one stash root.
@@ -186,6 +431,18 @@ export function getStateProposal(db, id, stashDir) {
186
431
  const row = (stashDir ? db.prepare(sql).get(id, stashDir) : db.prepare(sql).get(id));
187
432
  return row ? proposalRowToProposal(row) : undefined;
188
433
  }
434
+ /**
435
+ * Lenient counterpart to {@link getStateProposal} — used ONLY by the
436
+ * terminal-status (reject/archive) read paths in repository.ts that must
437
+ * tolerate a malformed row (see {@link proposalRowToProposalLenient}).
438
+ */
439
+ export function getStateProposalLenient(db, id, stashDir) {
440
+ const sql = `SELECT id, stash_dir, ref, status, source, created_at, updated_at,
441
+ content, frontmatter_json, metadata_json
442
+ FROM proposals WHERE id = ?${stashDir ? " AND stash_dir = ?" : ""}`;
443
+ const row = (stashDir ? db.prepare(sql).get(id, stashDir) : db.prepare(sql).get(id));
444
+ return row ? proposalRowToProposalLenient(row) : undefined;
445
+ }
189
446
  /**
190
447
  * Find PENDING proposal ids in one stash whose id starts with `idPrefix`.
191
448
  * Backs the UUID-prefix form of `akm proposal show/accept/... <prefix>` —
@@ -203,23 +460,3 @@ export function listStateProposalIdsByPrefix(db, stashDir, idPrefix) {
203
460
  .all(stashDir, `${escaped}%`);
204
461
  return rows.map((r) => r.id);
205
462
  }
206
- /**
207
- * Insert a proposal row ONLY when the id is not already present (used by the
208
- * one-time legacy filesystem import in the migrator —
209
- * `src/migrate/legacy/proposal-fs-import.ts` — so a re-run never clobbers rows
210
- * that have since been mutated through the canonical store, and re-walking the
211
- * still-on-disk legacy files is idempotent). Returns true when a row was
212
- * inserted.
213
- */
214
- export function insertProposalIfAbsent(db, proposal, stashDir) {
215
- const v = proposalToRowValues(proposal, stashDir);
216
- const result = db
217
- .prepare(`
218
- INSERT OR IGNORE INTO proposals
219
- (id, stash_dir, ref, status, source, created_at, updated_at, content, frontmatter_json, metadata_json)
220
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
221
- `)
222
- .run(v.id, v.stash_dir, v.ref, v.status, v.source, v.created_at, v.updated_at, v.content, v.frontmatter_json, v.metadata_json);
223
- const changes = result.changes ?? 0;
224
- return Number(changes) > 0;
225
- }
@@ -2,6 +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
  import { rethrowIfTestIsolationError } from "../../core/errors.js";
5
+ import { warn } from "../../core/warn.js";
5
6
  import { closeDatabase, openIndexDatabase } from "./index-connection.js";
6
7
  import { getRegistryIndexCache, upsertRegistryIndexCache } from "./registry-index-cache-repository.js";
7
8
  /**
@@ -87,6 +88,24 @@ export async function fetchCachedJson(opts) {
87
88
  if (stale !== undefined)
88
89
  return stale;
89
90
  }
91
+ // No in-TTL row — consult the cache PAST its TTL before giving up
92
+ // (§24.2 "Durability" gate): a briefly unreachable registry should
93
+ // degrade to the last-known index, loudly, not hard-fail the command.
94
+ try {
95
+ const expiredRow = db ? getRegistryIndexCache(db, cacheKey, Number.POSITIVE_INFINITY) : undefined;
96
+ if (expiredRow) {
97
+ const stale = parseCache(expiredRow.indexJson, { stale: true });
98
+ if (stale !== undefined) {
99
+ warn(`Registry fetch failed (${err instanceof Error ? err.message : String(err)}); ` +
100
+ "serving the last cached index, which is past its refresh interval.");
101
+ return stale;
102
+ }
103
+ }
104
+ }
105
+ catch (cacheErr) {
106
+ rethrowIfTestIsolationError(cacheErr);
107
+ // cache read failed — fall through to the original fetch error
108
+ }
90
109
  throw err;
91
110
  }
92
111
  });
@@ -0,0 +1,172 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * Upsert salience scores for one asset into state.db.
6
+ *
7
+ * Idempotent: safe to call every run; updates the outcome / retrieval / rank
8
+ * columns on conflict.
9
+ *
10
+ * #644 — encoding provenance guard: the `encoding_salience` + `encoding_source`
11
+ * columns are NOT lowered from a real content-derived score to a type-weight
12
+ * stub. When the stored row is `encoding_source = 'content'` and the incoming
13
+ * vector is a `type-stub` fallback, the stored encoding score and its provenance
14
+ * are preserved (only the other sub-scores and `rank_score` advance). A `content`
15
+ * write always wins; a `type-stub` write only seeds a row that has no content
16
+ * score yet. This stops the improve loop's type-weight fallback re-asserting the
17
+ * stub over a distill-written score on every run.
18
+ *
19
+ * NOTE: when the guard preserves the stored encoding score, the incoming
20
+ * `vector.rankScore` (computed from the stub encoding) is still written. Callers
21
+ * that want the rank_score to reflect the preserved content score should pass the
22
+ * stored content score back in as `inputs.encodingSalience` to `computeSalience`
23
+ * — which the improve loop does. The guard here is the defensive backstop.
24
+ */
25
+ export function upsertAssetSalience(db, ref, vector, now) {
26
+ const ts = now ?? Date.now();
27
+ db.prepare(`INSERT INTO asset_salience
28
+ (asset_ref, encoding_salience, outcome_salience, retrieval_salience, rank_score, consecutive_no_ops, updated_at, encoding_source)
29
+ VALUES (?, ?, ?, ?, ?, 0, ?, ?)
30
+ ON CONFLICT(asset_ref) DO UPDATE SET
31
+ -- #644: never lower a real content-derived score to a type-weight stub.
32
+ -- Keep the stored encoding score + provenance when the stored row is
33
+ -- 'content' and the incoming write is a 'type-stub' fallback.
34
+ encoding_salience = CASE
35
+ WHEN asset_salience.encoding_source = 'content' AND excluded.encoding_source = 'type-stub'
36
+ THEN asset_salience.encoding_salience
37
+ ELSE excluded.encoding_salience
38
+ END,
39
+ encoding_source = CASE
40
+ WHEN asset_salience.encoding_source = 'content' AND excluded.encoding_source = 'type-stub'
41
+ THEN asset_salience.encoding_source
42
+ ELSE excluded.encoding_source
43
+ END,
44
+ outcome_salience = excluded.outcome_salience,
45
+ retrieval_salience = excluded.retrieval_salience,
46
+ rank_score = excluded.rank_score,
47
+ updated_at = excluded.updated_at`).run(ref, vector.encoding, vector.outcome, vector.retrieval, vector.rankScore, ts, vector.encodingSource ?? "type-stub");
48
+ }
49
+ /**
50
+ * Load the salience row for one asset, or undefined if not yet computed.
51
+ */
52
+ export function getAssetSalience(db, ref) {
53
+ const row = db
54
+ .prepare(`SELECT asset_ref, encoding_salience, outcome_salience, retrieval_salience,
55
+ rank_score, consecutive_no_ops, updated_at, encoding_source
56
+ FROM asset_salience WHERE asset_ref = ?`)
57
+ .get(ref);
58
+ // Bun SQLite returns null (not undefined) when no row found.
59
+ return row == null ? undefined : row;
60
+ }
61
+ /**
62
+ * Load ALL rank scores from the asset_salience table (full-stash query).
63
+ *
64
+ * Used by the forgetting-safety report (plan §WS-1 step 7) to compute stash-wide
65
+ * rank positions rather than pool-relative positions. Returns an empty Map when the
66
+ * table is empty (first WS-1 run = no pre-existing rows).
67
+ *
68
+ * Order is unspecified; callers must sort before assigning 1-indexed positions.
69
+ */
70
+ export function getAllRankScores(db) {
71
+ const rows = db.prepare("SELECT asset_ref, rank_score FROM asset_salience").all();
72
+ const result = new Map();
73
+ for (const row of rows) {
74
+ result.set(row.asset_ref, row.rank_score);
75
+ }
76
+ return result;
77
+ }
78
+ // ── Plasticity helpers ────────────────────────────────────────────────────────
79
+ /**
80
+ * Increment `consecutive_no_ops` for an asset. Called after a no-op reflect/distill.
81
+ * Has NO effect on `rank_score` — the plasticity counter only dampens consolidation
82
+ * selection, not retrieval ranking. See plan §WS-1 step 8.
83
+ *
84
+ * Invariant: recordNoOp must never originate rank_score semantics. If the asset has
85
+ * no salience row yet (persistence's best-effort try/catch may have swallowed an
86
+ * error), we do nothing — a no-op counter is meaningless without a rank_score row,
87
+ * and a synthetic INSERT would fabricate a rank_score=0 entry that could produce
88
+ * false catastrophic-forgetting signals in buildRankChangeReport.
89
+ */
90
+ export function recordNoOp(db, ref) {
91
+ db.prepare(`UPDATE asset_salience SET consecutive_no_ops = consecutive_no_ops + 1, updated_at = ? WHERE asset_ref = ?`).run(Date.now(), ref);
92
+ // If changes === 0 the asset has no salience row yet — leave the table unchanged.
93
+ }
94
+ /**
95
+ * Reset `consecutive_no_ops` to 0 when an asset produces an accepted change.
96
+ * Call after a successful proposal acceptance or detected mutation.
97
+ */
98
+ export function resetConsecutiveNoOps(db, ref) {
99
+ db.prepare(`UPDATE asset_salience SET consecutive_no_ops = 0, updated_at = ? WHERE asset_ref = ?`).run(Date.now(), ref);
100
+ }
101
+ /**
102
+ * Return the `consecutive_no_ops` count for one asset. 0 when unknown.
103
+ */
104
+ export function getConsecutiveNoOps(db, ref) {
105
+ const row = db.prepare(`SELECT consecutive_no_ops FROM asset_salience WHERE asset_ref = ?`).get(ref);
106
+ return row?.consecutive_no_ops ?? 0;
107
+ }
108
+ // ── New in #672 part 2 ────────────────────────────────────────────────────────
109
+ /**
110
+ * Load the `retrieval_salience` column for the top `limit` assets ordered by
111
+ * `rank_score` descending.
112
+ *
113
+ * Absorbs the health read formerly inlined in `commands/health/metrics.ts`
114
+ * (`computeDegradationMetrics`'s corpus-diversity Gini calculation) — same
115
+ * query, now parameterised on `limit` instead of a hardcoded 100. This is new
116
+ * behavior (a named, parameterised repository entry point), not a verbatim
117
+ * move; callers that need fail-open behaviour on a missing table (pre-WS-1
118
+ * installs) must wrap the call in their own try/catch, matching the existing
119
+ * call site.
120
+ */
121
+ export function getTopRetrievalSalience(db, limit) {
122
+ return db
123
+ .prepare(`SELECT retrieval_salience FROM asset_salience ORDER BY rank_score DESC LIMIT ?`)
124
+ .all(limit);
125
+ }
126
+ /** List every `asset_salience` ref with its current `missing_since` marker. */
127
+ export function listAssetSalienceMissingState(db) {
128
+ return db.prepare(`SELECT asset_ref, missing_since FROM asset_salience`).all();
129
+ }
130
+ /**
131
+ * Stamp `missing_since = now` for the given refs, but ONLY where it is
132
+ * currently NULL — a ref already stamped keeps its original timestamp so the
133
+ * grace window measures from first sighting, not last. Refs with no row at
134
+ * all are silently skipped (0 rows affected). Returns the number of rows
135
+ * actually stamped.
136
+ */
137
+ export function stampAssetSalienceMissing(db, refs, now) {
138
+ if (refs.length === 0)
139
+ return 0;
140
+ const stmt = db.prepare(`UPDATE asset_salience SET missing_since = ? WHERE asset_ref = ? AND missing_since IS NULL`);
141
+ let changed = 0;
142
+ for (const ref of refs)
143
+ changed += Number(stmt.run(now, ref).changes);
144
+ return changed;
145
+ }
146
+ /**
147
+ * Clear `missing_since` for refs that resolved again. Returns the number of
148
+ * rows actually cleared.
149
+ */
150
+ export function clearAssetSalienceMissing(db, refs) {
151
+ if (refs.length === 0)
152
+ return 0;
153
+ const stmt = db.prepare(`UPDATE asset_salience SET missing_since = NULL WHERE asset_ref = ? AND missing_since IS NOT NULL`);
154
+ let changed = 0;
155
+ for (const ref of refs)
156
+ changed += Number(stmt.run(ref).changes);
157
+ return changed;
158
+ }
159
+ /**
160
+ * Delete `asset_salience` rows whose `missing_since` is older than
161
+ * `cutoffMs`. A single DELETE — SQLite runs it as one implicit transaction.
162
+ * Returns the number of rows deleted.
163
+ */
164
+ export function deleteAssetSalienceMissingBefore(db, cutoffMs) {
165
+ return Number(db.prepare(`DELETE FROM asset_salience WHERE missing_since IS NOT NULL AND missing_since < ?`).run(cutoffMs)
166
+ .changes);
167
+ }
168
+ /** Count `asset_salience` rows currently marked missing (the live backlog size). */
169
+ export function countAssetSalienceMissing(db) {
170
+ const row = db.prepare(`SELECT COUNT(*) AS n FROM asset_salience WHERE missing_since IS NOT NULL`).get();
171
+ return row.n;
172
+ }