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
@@ -2,50 +2,78 @@
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
- * Observability command cluster — `akm log` (events list/tail), `akm lessons`
6
- * (coverage), and `akm hints`. Extracted verbatim from src/cli.ts (WS6) so the
7
- * God Module shrinks; the `main.subCommands.{log,lessons,hints}` keys and every
8
- * subcommand's args/output shape are byte-identical.
5
+ * Observability command cluster — `akm log` (append-only events stream).
6
+ * Extracted verbatim from src/cli.ts (WS6) so the God Module shrinks; the
7
+ * `main.subCommands.log` key and its args/output shape are byte-identical.
9
8
  *
10
- * These three surfaces are cohesive read-only "tell me what happened / what to
11
- * do" commands: `log` reads the append-only state.db events stream, `lessons
12
- * coverage` reports tag-coverage gaps from the index, and `hints` prints the
13
- * embedded AGENTS.md guidance. They share no helpers with any command still
14
- * inline in cli.ts, so the `loadHints` private helper and the
15
- * `formatEventLine` / `EMBEDDED_HINTS*` / db-tag-set imports move with them.
9
+ * `log` reads the append-only state.db events stream. It shares no helpers
10
+ * with any command still inline in cli.ts.
16
11
  *
17
- * The leaf handlers whose body is a plain `runWithJsonErrors(...) + output(...)`
18
- * (`events list`, `lessons coverage`) are migrated onto `defineJsonCommand`,
19
- * which emits the same JSON envelope (stdout/stderr/exit-code) as the inline
20
- * form. `events tail` (manual streaming console/stderr writes) and `hints`
21
- * (direct `process.stdout.write`) keep a plain `defineCommand` wrapping
22
- * `runWithJsonErrors` so their byte-for-byte output stays untouched.
12
+ * 0.9.0 CLI overhaul (S3): `log` was a group with `list`/`tail` subcommands;
13
+ * `tail` (a foreground polling daemon) is dropped and `log` becomes a LEAF
14
+ * command that is today's `list` surface, unchanged (including the
15
+ * `--since @offset:<id>` durable cursor and --ref/--type filters). The
16
+ * `lessons`/`lesson` group (`coverage`, `strength`) is dropped entirely —
17
+ * ranking's `lessonStrength` contributor is untouched, only the CLI read
18
+ * surface for it goes away.
19
+ *
20
+ * The embedded agent-guide surfaces (`akm hints` and `akm help agents`) live
21
+ * in src/cli.ts, so their loading code is intentionally outside this module.
22
+ *
23
+ * The leaf handler's body is a plain `runWithJsonErrors(...) + output(...)`,
24
+ * migrated onto `defineJsonCommand`, which emits the same JSON envelope
25
+ * (stdout/stderr/exit-code) as the inline form.
23
26
  */
24
- import fs from "node:fs";
25
- import path from "node:path";
26
- import { defineCommand } from "citty";
27
27
  import { parsePositiveIntFlag } from "../cli/parse-args.js";
28
- import { defineJsonCommand, output, parseAllFlagValues, runWithJsonErrors } from "../cli/shared.js";
29
- import { EMBEDDED_HINTS, EMBEDDED_HINTS_FULL } from "../output/cli-hints.js";
30
- import { getOutputMode, parseDetailLevel } from "../output/context.js";
31
- import { formatEventLine } from "../output/text.js";
32
- import { getDirname } from "../runtime.js";
33
- import { closeDatabase, openExistingDatabase } from "../storage/repositories/index-connection.js";
34
- import { collectTagSetFromEntries } from "../storage/repositories/index-entries-repository.js";
35
- import { akmEventsList, akmEventsTail } from "./events.js";
28
+ import { retiredCommandHint } from "../cli/retired-commands.js";
29
+ import { defineJsonCommand, output, parseAllFlagValues } from "../cli/shared.js";
30
+ import { UsageError } from "../core/errors.js";
31
+ import { akmEventsList } from "./log.js";
32
+ /**
33
+ * `log` flattened from a group (`log list`/`log tail`) to a leaf in 0.9 (S3).
34
+ * `log` declares no positional args, so citty leaves any leftover positional
35
+ * token in `args._` uninterpreted rather than rejecting it — meaning
36
+ * `akm log tail` and `akm log list` silently ran today's `log` surface
37
+ * instead of failing like the other removed spellings (`log tail` is the
38
+ * dangerous one: it used to stream/follow, so an unmigrated caller got a
39
+ * silent one-shot snapshot instead of an error).
40
+ */
41
+ function rejectExtraLogPositionals(positionals) {
42
+ const extra = Array.isArray(positionals) ? positionals.map(String) : [];
43
+ if (extra.length === 0)
44
+ return;
45
+ // A retired subcommand spelling gets its replacement hint (same table the
46
+ // unknown-command path uses), so `akm log tail` teaches the durable-cursor
47
+ // polling pattern instead of a bare "takes no positional arguments".
48
+ const retired = extra[0] === undefined ? undefined : retiredCommandHint(["log"], extra[0]);
49
+ throw new UsageError(`akm log takes no positional arguments, but got ${extra.map((token) => `"${token}"`).join(" ")}. ` +
50
+ '"log list"/"log tail" were removed in 0.9.0 — `akm log` alone is today\'s `log list` surface.', "INVALID_FLAG_VALUE", retired);
51
+ }
36
52
  // ── `akm log` ────────────────────────────────────────────────────────────────
37
- // Append-only events stream surface (#204). `list` reads state.db events
38
- // with optional --since/--type/--ref filters; `tail` follows the table via
39
- // a polling loop and prints each event as a single JSONL line.
40
- const eventsListCommand = defineJsonCommand({
41
- meta: { name: "list", description: "List events from the append-only state.db events stream" },
53
+ // Append-only events stream surface (#204). Reads state.db events with
54
+ // optional --since/--type/--ref filters.
55
+ //
56
+ // R-060: the internal output()/shape-registry command name is `log-list`
57
+ // (renamed from the pre-rename `events-list`, back when the command group was
58
+ // still called `akm events`). This string never reaches the wire —
59
+ // `shapeEventsOutput` (src/output/shapes/helpers.ts) builds its own envelope
60
+ // and never stamps a `shape` field — so the rename is a coherence-only
61
+ // change, not a schemaVersion bump. Kept as `log-list` (not renamed to `log`)
62
+ // even though the group flattened to a leaf, to avoid unrelated churn on the
63
+ // internal registry key.
64
+ export const logCommand = defineJsonCommand({
65
+ meta: { name: "log", description: "List events from the append-only state.db events stream" },
42
66
  args: {
43
67
  since: {
44
68
  type: "string",
45
69
  description: "ISO timestamp / epoch ms, OR `@offset:<id>` for a durable row-id cursor (resume across processes)",
46
70
  },
47
71
  type: { type: "string", description: "Filter by event type (add, remove, remember, feedback, ...)" },
48
- ref: { type: "string", description: "Filter by asset ref (type:name)" },
72
+ ref: { type: "string", description: "Filter by asset ref ([bundle//]conceptId)" },
73
+ run: {
74
+ type: "string",
75
+ description: "Filter to a workflow run's events (metadata.runId), e.g. the id from `akm workflow run`",
76
+ },
49
77
  "exclude-tags": {
50
78
  type: "string",
51
79
  description: "Exclude events matching these tags (repeatable)",
@@ -54,192 +82,25 @@ const eventsListCommand = defineJsonCommand({
54
82
  type: "string",
55
83
  description: "Only include events with ALL these tags (repeatable)",
56
84
  },
85
+ limit: {
86
+ type: "string",
87
+ description: "Return only the most recent N events matching every other filter (default: unlimited)",
88
+ },
57
89
  },
58
90
  run({ args }) {
91
+ rejectExtraLogPositionals(args._);
59
92
  const excludeTags = parseAllFlagValues("--exclude-tags");
60
93
  const includeTags = parseAllFlagValues("--include-tags");
94
+ const limit = parsePositiveIntFlag(args.limit);
61
95
  const result = akmEventsList({
62
96
  since: args.since,
63
97
  type: args.type,
64
98
  ref: args.ref,
99
+ run: args.run,
65
100
  ...(excludeTags.length > 0 ? { excludeTags } : {}),
66
101
  ...(includeTags.length > 0 ? { includeTags } : {}),
102
+ ...(limit !== undefined ? { limit } : {}),
67
103
  });
68
- output("events-list", result);
104
+ output("log-list", result);
69
105
  },
70
106
  });
71
- const eventsTailCommand = defineCommand({
72
- meta: { name: "tail", description: "Follow the append-only state.db events stream (polling)" },
73
- args: {
74
- since: {
75
- type: "string",
76
- description: "ISO timestamp / epoch ms, OR `@offset:<id>` for a durable row-id cursor (resume across processes)",
77
- },
78
- type: { type: "string", description: "Filter by event type" },
79
- ref: { type: "string", description: "Filter by asset ref (type:name)" },
80
- "interval-ms": { type: "string", description: "Polling interval in ms (default: 75)" },
81
- "max-duration-ms": { type: "string", description: "Stop after this many ms (default: never)" },
82
- "max-events": { type: "string", description: "Stop after observing this many events" },
83
- "exclude-tags": {
84
- type: "string",
85
- description: "Exclude events matching these tags (repeatable)",
86
- },
87
- "include-tags": {
88
- type: "string",
89
- description: "Only include events with ALL these tags (repeatable)",
90
- },
91
- },
92
- async run({ args }) {
93
- await runWithJsonErrors(async () => {
94
- const intervalMs = parsePositiveIntFlag(args["interval-ms"], "--interval-ms");
95
- const maxDurationMs = parsePositiveIntFlag(args["max-duration-ms"], "--max-duration-ms");
96
- const maxEvents = parsePositiveIntFlag(args["max-events"], "--max-events");
97
- const mode = getOutputMode();
98
- // In streaming text mode we want each event to print as soon as it
99
- // arrives. The polling loop emits via `onEvent`; the final result is
100
- // also rendered through the standard output() pipeline so JSON
101
- // consumers always get the canonical envelope.
102
- const stream = mode.format === "text" || mode.format === "jsonl";
103
- const excludeTags = parseAllFlagValues("--exclude-tags");
104
- const includeTags = parseAllFlagValues("--include-tags");
105
- const result = await akmEventsTail({
106
- since: args.since,
107
- type: args.type,
108
- ref: args.ref,
109
- intervalMs,
110
- maxDurationMs,
111
- maxEvents,
112
- ...(excludeTags.length > 0 ? { excludeTags } : {}),
113
- ...(includeTags.length > 0 ? { includeTags } : {}),
114
- onEvent: stream
115
- ? (event) => {
116
- if (mode.format === "jsonl") {
117
- console.log(JSON.stringify(event));
118
- }
119
- else {
120
- console.log(formatEventLine(event));
121
- }
122
- }
123
- : undefined,
124
- });
125
- // Emit the canonical envelope last (JSON/YAML modes rely on this;
126
- // streaming modes already printed each event but we still emit a
127
- // trailer so callers can persist the resumable cursor).
128
- if (!stream) {
129
- output("events-tail", result);
130
- }
131
- else if (mode.format === "jsonl") {
132
- // Final discriminated trailer row so jsonl consumers can resume.
133
- const trailer = {
134
- _kind: "trailer",
135
- schemaVersion: 1,
136
- nextOffset: result.nextOffset,
137
- totalCount: result.totalCount,
138
- reason: result.reason,
139
- };
140
- console.log(JSON.stringify(trailer));
141
- }
142
- else {
143
- // text mode: keep stdout pristine for line-oriented parsers and
144
- // emit the trailer on stderr.
145
- process.stderr.write(`[events-tail] reason=${result.reason} nextOffset=${result.nextOffset} total=${result.totalCount}\n`);
146
- }
147
- });
148
- },
149
- });
150
- export const logCommand = defineCommand({
151
- meta: {
152
- name: "log",
153
- description: "Read or follow the append-only state.db events stream (mutations, feedback, indexing)",
154
- },
155
- subCommands: {
156
- list: eventsListCommand,
157
- tail: eventsTailCommand,
158
- },
159
- });
160
- // ── lessons subcommands (Phase 7A / Advantage D4c) ──────────────────────────
161
- const lessonsCoverageCommand = defineJsonCommand({
162
- meta: {
163
- name: "coverage",
164
- description: "Report tags that exist on indexed assets but are NOT yet covered by any lesson.\n\n" +
165
- "Useful for spotting topics where the stash has skills/commands/scripts but no\n" +
166
- "crystallized lesson — a signal that the team has tacit knowledge worth distilling.\n\n" +
167
- "Default output is JSON: { uncoveredTags: string[], lessonTagCount: number, totalTagCount: number }.\n" +
168
- "Pass --format text for a plain-text bulleted list.",
169
- },
170
- args: {},
171
- run() {
172
- const db = openExistingDatabase();
173
- try {
174
- const allTagSet = collectTagSetFromEntries(db, undefined);
175
- const lessonTagSet = collectTagSetFromEntries(db, "lesson");
176
- const uncovered = [];
177
- for (const tag of allTagSet) {
178
- if (!lessonTagSet.has(tag))
179
- uncovered.push(tag);
180
- }
181
- uncovered.sort((a, b) => a.localeCompare(b));
182
- output("lessons-coverage", {
183
- ok: true,
184
- uncoveredTags: uncovered,
185
- lessonTagCount: lessonTagSet.size,
186
- totalTagCount: allTagSet.size,
187
- });
188
- }
189
- finally {
190
- closeDatabase(db);
191
- }
192
- },
193
- });
194
- export const lessonsCommand = defineCommand({
195
- meta: {
196
- name: "lessons",
197
- alias: "lesson",
198
- description: "Lesson-asset tooling: tag-coverage gaps, strength queries.",
199
- },
200
- subCommands: {
201
- coverage: lessonsCoverageCommand,
202
- },
203
- });
204
- // ── `akm hints` ──────────────────────────────────────────────────────────────
205
- export const hintsCommand = defineCommand({
206
- meta: {
207
- name: "hints",
208
- description: "Print agent instructions on how to use akm, use --detail full for a complete guide",
209
- },
210
- args: {
211
- detail: {
212
- type: "string",
213
- description: "Hints detail level (brief|normal|full). `brief` prints the short guide; `normal`/`full` print the complete guide.",
214
- default: "normal",
215
- },
216
- },
217
- run({ args }) {
218
- return runWithJsonErrors(() => {
219
- // Let the global parser validate the value so an invalid `--detail`
220
- // returns the standard JSON error envelope (exit 2) rather than a raw
221
- // stack trace + exit 1. `brief` → short doc; `normal`/`full` → full doc.
222
- const detail = parseDetailLevel(args.detail) ?? "normal";
223
- process.stdout.write(loadHints(detail === "brief" ? "brief" : "full"));
224
- });
225
- },
226
- });
227
- // ── Hints (embedded AGENTS.md) ──────────────────────────────────────────────
228
- function loadHints(detail = "normal") {
229
- // `brief` → the short AGENTS.md guide; `normal`/`full` → the complete guide.
230
- const wantFull = detail !== "brief";
231
- const filename = wantFull ? "AGENTS.full.md" : "AGENTS.md";
232
- const fallback = wantFull ? EMBEDDED_HINTS_FULL : EMBEDDED_HINTS;
233
- // Try reading from the docs/ directory (works in dev and when installed via npm)
234
- try {
235
- const docsPath = path.resolve(getDirname(import.meta.url), `../../docs/agents/${filename}`);
236
- if (fs.existsSync(docsPath)) {
237
- return fs.readFileSync(docsPath, "utf8");
238
- }
239
- }
240
- catch {
241
- // fall through
242
- }
243
- // Fallback for compiled binary — inline content
244
- return fallback;
245
- }
@@ -13,8 +13,7 @@
13
13
  * - Reuses `listProposals` (no source filter — generator filtering is
14
14
  * in-memory) and the `akmProposalAccept` / `akmProposalReject` wrappers from
15
15
  * `proposal.ts` so the standard `promoted` / `rejected` events are emitted.
16
- * Deterministic by design — never confidence-gated (the improve confidence
17
- * gate that once ran alongside this engine died in 0.9.0).
16
+ * Deterministic by design; only the configured drain policy decides.
18
17
  * - Backlog-only: `excludeIds` removes this-run's fresh proposals so triage
19
18
  * never re-adjudicates a current run's output (decision #2).
20
19
  * - Hard guardrails enforced in code: a `maxAccepts` ceiling checked *before*
@@ -35,17 +34,19 @@
35
34
  * (mirrors reflect's dual test seams) so tests can run the full engine without
36
35
  * touching the filesystem or spawning a process.
37
36
  */
37
+ import { createHash } from "node:crypto";
38
38
  import fs from "node:fs";
39
39
  import path from "node:path";
40
40
  import { assetPathForName, stashDirFor } from "../../core/asset/asset-placement.js";
41
41
  import { parseFrontmatter } from "../../core/asset/frontmatter.js";
42
42
  import { parseRefInput } from "../../core/asset/resolve-ref.js";
43
43
  import { appendEvent } from "../../core/events.js";
44
+ import { escapeJsonStringControls, stripCodeFences, stripThinkBlocks } from "../../core/parse.js";
44
45
  import { info, warn } from "../../core/warn.js";
45
46
  import { executeRunner } from "../../integrations/agent/runner-dispatch.js";
46
- import { chatCompletion, stripJsonFences } from "../../llm/client.js";
47
+ import { chatCompletion } from "../../llm/client.js";
47
48
  import { akmProposalAccept, akmProposalReject } from "./proposal.js";
48
- import { listProposals, proposalContent, recordGateDecision, } from "./repository.js";
49
+ import { listProposals, preflightProposalPromotion, proposalContent, recordGateDecision, } from "./repository.js";
49
50
  // ---------------------------------------------------------------------------
50
51
  // Content helpers
51
52
  // ---------------------------------------------------------------------------
@@ -184,7 +185,7 @@ export function buildJudgmentPrompt(proposal, reason, ctx) {
184
185
  }
185
186
  /** Parse a {@link JudgmentVerdict} from raw runner output. Lenient. */
186
187
  export function parseJudgmentVerdict(raw) {
187
- const cleaned = stripJsonFences(raw).trim();
188
+ const cleaned = escapeJsonStringControls(stripCodeFences(stripThinkBlocks(raw))).trim();
188
189
  if (!cleaned)
189
190
  return null;
190
191
  // Find the first balanced-looking JSON object in the output.
@@ -241,6 +242,9 @@ async function dispatchJudgment(runner, prompt, seams) {
241
242
  }
242
243
  return parseJudgmentVerdict(result.stdout);
243
244
  }
245
+ function judgedContentHash(proposal) {
246
+ return createHash("sha256").update(proposalContent(proposal), "utf8").digest("hex");
247
+ }
244
248
  /**
245
249
  * Run the judgment tier over the deferred items. The runner only *judges*; the
246
250
  * engine performs the resulting accept (respecting `applyMode`) / reject write.
@@ -285,7 +289,12 @@ async function runJudgmentTier(input) {
285
289
  continue;
286
290
  }
287
291
  try {
288
- await input.rejectFn({ stashDir: input.stashDir, id: item.id, reason: verdict.reason || "judgment: reject" });
292
+ await input.rejectFn({
293
+ stashDir: input.stashDir,
294
+ id: item.id,
295
+ reason: verdict.reason || "judgment: reject",
296
+ gateDecision: { outcome: "auto-rejected", reason: "judgment-reject", gate: input.gateLabel },
297
+ });
289
298
  rejected.push(item.id);
290
299
  }
291
300
  catch (err) {
@@ -301,6 +310,21 @@ async function runJudgmentTier(input) {
301
310
  // is RESOLVED (the runner judged it). Track separately so it is NOT
302
311
  // reported as "left unresolved" and a follow-up promote run picks it up.
303
312
  staged.push(item.id);
313
+ if (!input.dryRun) {
314
+ try {
315
+ recordGateDecision(input.stashDir, item.id, {
316
+ outcome: "staged",
317
+ reason: "judgment-accept",
318
+ contentHash: judgedContentHash(proposal),
319
+ gate: input.gateLabel,
320
+ });
321
+ }
322
+ catch (err) {
323
+ warn(`[triage] failed to stage judgment for ${item.id}: ${err instanceof Error ? err.message : String(err)}`);
324
+ staged.pop();
325
+ stillDeferred.push(item);
326
+ }
327
+ }
304
328
  continue;
305
329
  }
306
330
  // Accept cap: once the shared budget is exhausted, route further accepts to
@@ -310,6 +334,19 @@ async function runJudgmentTier(input) {
310
334
  continue;
311
335
  }
312
336
  if (input.dryRun) {
337
+ try {
338
+ if (input.config) {
339
+ preflightProposalPromotion(input.config, proposal, {
340
+ ...(input.target ? { target: input.target } : {}),
341
+ gateDecision: { outcome: "auto-accepted", reason: "judgment-accept", gate: input.gateLabel },
342
+ });
343
+ }
344
+ }
345
+ catch (err) {
346
+ warn(`[triage] judgment preflight failed for ${item.id}: ${err instanceof Error ? err.message : String(err)}`);
347
+ stillDeferred.push(item);
348
+ continue;
349
+ }
313
350
  promoted.push(item.id);
314
351
  acceptBudget -= 1;
315
352
  continue;
@@ -320,6 +357,7 @@ async function runJudgmentTier(input) {
320
357
  id: item.id,
321
358
  ...(input.target ? { target: input.target } : {}),
322
359
  ...(input.config ? { config: input.config } : {}),
360
+ gateDecision: { outcome: "auto-accepted", reason: "judgment-accept", gate: input.gateLabel },
323
361
  });
324
362
  promoted.push(item.id);
325
363
  acceptBudget -= 1;
@@ -347,6 +385,7 @@ export async function drainProposals(opts, promoteFn = akmProposalAccept, reject
347
385
  const pending = listProposals(opts.stashDir, { status: "pending" }).filter((p) => !exclude.has(p.id));
348
386
  // First, classify every proposal deterministically.
349
387
  const acceptIds = [];
388
+ const acceptGateReasons = new Map();
350
389
  const rejectTargets = [];
351
390
  const gateLabel = `triage:${opts.policy.name}`;
352
391
  // Items deferred purely because they need a judge (no threshold-based reason)
@@ -359,22 +398,31 @@ export async function drainProposals(opts, promoteFn = akmProposalAccept, reject
359
398
  // the audit trail and silently promote content the gate explicitly rejected.
360
399
  // Such proposals remain pending until the TTL expires them — the queue is
361
400
  // audited-autonomous; no manual-review rung exists (06-M3).
362
- if (proposal.gateDecision?.outcome === "auto-rejected")
401
+ if (proposal.gateDecision?.outcome === "auto-rejected" && !proposal.gateDecision.gate?.startsWith("triage:")) {
363
402
  continue;
403
+ }
404
+ if (proposal.gateDecision?.outcome === "staged" &&
405
+ proposal.gateDecision.gate === gateLabel &&
406
+ proposal.gateDecision.contentHash === judgedContentHash(proposal)) {
407
+ acceptIds.push(proposal.id);
408
+ acceptGateReasons.set(proposal.id, "judgment-accept");
409
+ continue;
410
+ }
364
411
  const decision = classifyProposal(proposal, opts.policy, opts.maxDiffLines);
365
412
  if (decision === null)
366
413
  continue;
367
414
  // #577: stamp the gate's verdict onto the proposal so `akm proposal show`
368
415
  // can explain WHY it landed here. A dry-run performs zero writes, so it
369
416
  // records nothing.
370
- const outcome = decision.verdict === "accept" ? "auto-accepted" : decision.verdict === "reject" ? "auto-rejected" : "deferred";
371
- stampGateDecision(opts, proposal.id, {
372
- outcome,
373
- reason: decision.gate.reason,
374
- ...(decision.gate.measured !== undefined ? { measured: decision.gate.measured } : {}),
375
- ...(decision.gate.thresholds ? { thresholds: decision.gate.thresholds } : {}),
376
- gate: gateLabel,
377
- });
417
+ if (decision.verdict === "defer") {
418
+ stampGateDecision(opts, proposal.id, {
419
+ outcome: "deferred",
420
+ reason: decision.gate.reason,
421
+ ...(decision.gate.measured !== undefined ? { measured: decision.gate.measured } : {}),
422
+ ...(decision.gate.thresholds ? { thresholds: decision.gate.thresholds } : {}),
423
+ gate: gateLabel,
424
+ });
425
+ }
378
426
  // A defer with no threshold (mid-band / possible-dup from the defer list) is
379
427
  // pending only because it needs adjudication — re-stampable to
380
428
  // `no-judge-configured`. A band-based defer keeps its specific reason.
@@ -383,6 +431,7 @@ export async function drainProposals(opts, promoteFn = akmProposalAccept, reject
383
431
  }
384
432
  if (decision.verdict === "accept") {
385
433
  acceptIds.push(proposal.id);
434
+ acceptGateReasons.set(proposal.id, "policy-accept");
386
435
  }
387
436
  else if (decision.verdict === "reject") {
388
437
  rejectTargets.push({ id: proposal.id, reason: decision.reason });
@@ -398,7 +447,12 @@ export async function drainProposals(opts, promoteFn = akmProposalAccept, reject
398
447
  continue;
399
448
  }
400
449
  try {
401
- await rejectFn({ stashDir: opts.stashDir, id: target.id, reason: target.reason });
450
+ await rejectFn({
451
+ stashDir: opts.stashDir,
452
+ id: target.id,
453
+ reason: target.reason,
454
+ gateDecision: { outcome: "auto-rejected", reason: "empty-diff", gate: gateLabel },
455
+ });
402
456
  result.rejected.push(target.id);
403
457
  }
404
458
  catch (err) {
@@ -424,6 +478,11 @@ export async function drainProposals(opts, promoteFn = akmProposalAccept, reject
424
478
  id,
425
479
  ...(opts.target ? { target: opts.target } : {}),
426
480
  ...(opts.config ? { config: opts.config } : {}),
481
+ gateDecision: {
482
+ outcome: "auto-accepted",
483
+ reason: acceptGateReasons.get(id) ?? "policy-accept",
484
+ gate: gateLabel,
485
+ },
427
486
  });
428
487
  result.promoted.push(id);
429
488
  deterministicPromoted += 1;
@@ -434,10 +493,31 @@ export async function drainProposals(opts, promoteFn = akmProposalAccept, reject
434
493
  }
435
494
  }
436
495
  else if (opts.applyMode === "promote" && opts.dryRun) {
437
- // Dry-run promote: report (and count, for the shared budget) what would be
438
- // promoted without writing.
439
- result.promoted.push(...withinCap);
440
- deterministicPromoted = withinCap.length;
496
+ // Exercise the same stamped candidate and lint boundary as real promotion.
497
+ // Tests that omit config retain the classification-only seam.
498
+ const byId = new Map(pending.map((proposal) => [proposal.id, proposal]));
499
+ for (const id of withinCap) {
500
+ try {
501
+ if (opts.config) {
502
+ const proposal = byId.get(id);
503
+ if (!proposal)
504
+ throw new Error(`Proposal ${id} disappeared during drain preflight.`);
505
+ preflightProposalPromotion(opts.config, proposal, {
506
+ ...(opts.target ? { target: opts.target } : {}),
507
+ gateDecision: {
508
+ outcome: "auto-accepted",
509
+ reason: acceptGateReasons.get(id) ?? "policy-accept",
510
+ gate: gateLabel,
511
+ },
512
+ });
513
+ }
514
+ result.promoted.push(id);
515
+ deterministicPromoted += 1;
516
+ }
517
+ catch (err) {
518
+ warn(`[triage] preflight failed for ${id}: ${err instanceof Error ? err.message : String(err)}`);
519
+ }
520
+ }
441
521
  }
442
522
  // applyMode "queue": leave accept candidates pending (staged). No promotion.
443
523
  // Remaining accept budget for the judgment tier: maxAccepts minus what was
@@ -461,6 +541,7 @@ export async function drainProposals(opts, promoteFn = akmProposalAccept, reject
461
541
  ...(opts.target ? { target: opts.target } : {}),
462
542
  ...(opts.config ? { config: opts.config } : {}),
463
543
  remainingAcceptBudget,
544
+ gateLabel,
464
545
  });
465
546
  result.promoted.push(...tier.promoted);
466
547
  result.rejected.push(...tier.rejected);
@@ -471,15 +552,6 @@ export async function drainProposals(opts, promoteFn = akmProposalAccept, reject
471
552
  if (tier.skippedByCap.length > 0) {
472
553
  info(`[triage] accept ceiling reached in judgment tier: ${tier.skippedByCap.length} judged-accept items skipped by cap (maxAccepts=${opts.maxAccepts})`);
473
554
  }
474
- // #577: re-stamp the gate decision for items the judgment tier resolved so
475
- // `akm proposal show` reflects the judge's verdict, not the earlier
476
- // deterministic defer.
477
- for (const id of tier.promoted) {
478
- stampGateDecision(opts, id, { outcome: "auto-accepted", reason: "judgment-accept", gate: gateLabel });
479
- }
480
- for (const id of tier.rejected) {
481
- stampGateDecision(opts, id, { outcome: "auto-rejected", reason: "judgment-reject", gate: gateLabel });
482
- }
483
555
  // Replace the deferred list with only the items the judgment tier could NOT
484
556
  // resolve (verdict "defer", parse failure, or runner error). Staged
485
557
  // queue-mode accepts are RESOLVED and tracked in result.staged instead.