akm-cli 0.9.1 → 0.9.2-alpha.1

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 (341) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +3 -1
  3. package/SECURITY.md +1 -1
  4. package/STABILITY.md +1 -1
  5. package/dist/akm +2 -2
  6. package/dist/akm-migrate +2 -2
  7. package/dist/assets/hints/cli-hints-full.md +14 -9
  8. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
  9. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  10. package/dist/assets/models.json +35 -0
  11. package/dist/assets/tasks/core/extract.yml +6 -5
  12. package/dist/assets/tasks/core/improve.yml +6 -5
  13. package/dist/assets/tasks/core/index-refresh.yml +6 -5
  14. package/dist/assets/tasks/core/sync.yml +6 -5
  15. package/dist/assets/tasks/core/version-check.yml +6 -5
  16. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
  17. package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
  18. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
  19. package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
  20. package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
  21. package/dist/cli/confirm.js +2 -2
  22. package/dist/cli/parse-args.js +3 -24
  23. package/dist/cli/retired-commands.js +1 -1
  24. package/dist/cli/shared.js +2 -2
  25. package/dist/cli.js +11 -9
  26. package/dist/commands/agent/agent-dispatch.js +55 -89
  27. package/dist/commands/agent/contribute-cli.js +12 -45
  28. package/dist/commands/command/builtin-action.js +32 -0
  29. package/dist/commands/command/command-cli.js +99 -0
  30. package/dist/commands/command/command-execution.js +308 -0
  31. package/dist/commands/command/execution-source-loader.js +176 -0
  32. package/dist/commands/command/portable-template.js +60 -0
  33. package/dist/commands/config-cli.js +10 -4
  34. package/dist/commands/env/env.js +4 -2
  35. package/dist/commands/feedback-cli.js +1 -1
  36. package/dist/commands/health/checks.js +241 -29
  37. package/dist/commands/health/html-report.js +0 -14
  38. package/dist/commands/health/report-view-model.js +0 -1
  39. package/dist/commands/health/surfaces.js +6 -7
  40. package/dist/commands/health/types.js +0 -2
  41. package/dist/commands/health.js +63 -18
  42. package/dist/commands/improve/collapse-detector.js +2 -2
  43. package/dist/commands/improve/consolidate.js +251 -214
  44. package/dist/commands/improve/distill/promote-memory.js +71 -34
  45. package/dist/commands/improve/distill/quality-gate.js +17 -5
  46. package/dist/commands/improve/distill.js +232 -155
  47. package/dist/commands/improve/eligibility.js +112 -79
  48. package/dist/commands/improve/execution.js +57 -0
  49. package/dist/commands/improve/extract-cli.js +5 -5
  50. package/dist/commands/improve/extract.js +521 -342
  51. package/dist/commands/improve/improve-strategies.js +43 -14
  52. package/dist/commands/improve/improve.js +249 -29
  53. package/dist/commands/improve/loop-stages.js +11 -17
  54. package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
  55. package/dist/commands/improve/outcome-loop.js +22 -38
  56. package/dist/commands/improve/planner.js +134 -0
  57. package/dist/commands/improve/preparation.js +730 -409
  58. package/dist/commands/improve/reflect.js +386 -223
  59. package/dist/commands/improve/run-context.js +3 -4
  60. package/dist/commands/improve/salience.js +6 -58
  61. package/dist/commands/improve/session-asset.js +12 -12
  62. package/dist/commands/lint/index.js +101 -29
  63. package/dist/commands/migrate-cli.js +11 -69
  64. package/dist/commands/migration-tool.js +6 -9
  65. package/dist/commands/models-cli.js +27 -0
  66. package/dist/commands/proposal/drain.js +258 -186
  67. package/dist/commands/proposal/proposal-cli.js +32 -10
  68. package/dist/commands/proposal/proposal.js +2 -5
  69. package/dist/commands/proposal/propose.js +192 -172
  70. package/dist/commands/proposal/repository.js +54 -91
  71. package/dist/commands/proposal/validators/proposal-validators.js +9 -7
  72. package/dist/commands/read/curate.js +10 -0
  73. package/dist/commands/read/registry-search.js +25 -9
  74. package/dist/commands/read/remember-cli.js +14 -2
  75. package/dist/commands/read/search.js +10 -4
  76. package/dist/commands/read/show.js +139 -153
  77. package/dist/commands/registry-cli.js +16 -7
  78. package/dist/commands/remember.js +33 -18
  79. package/dist/commands/sources/add-cli.js +19 -178
  80. package/dist/commands/sources/bundle-cli.js +15 -3
  81. package/dist/commands/sources/dangerous-env-audit.js +135 -0
  82. package/dist/commands/sources/info.js +2 -1
  83. package/dist/commands/sources/installed-stashes.js +835 -152
  84. package/dist/commands/sources/schema-repair.js +174 -95
  85. package/dist/commands/sources/self-update.js +30 -74
  86. package/dist/commands/sources/source-add.js +3 -5
  87. package/dist/commands/sources/sources-cli.js +2 -15
  88. package/dist/commands/sources/update-transaction.js +220 -0
  89. package/dist/commands/tasks/tasks-cli.js +3 -3
  90. package/dist/commands/tasks/tasks.js +736 -317
  91. package/dist/commands/workflow-cli.js +2 -2
  92. package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
  93. package/dist/core/adapter/adapters/akm-adapter.js +83 -34
  94. package/dist/core/adapter/adapters/akm-lint.js +54 -39
  95. package/dist/core/adapter/adapters/akm-metadata.js +45 -45
  96. package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
  97. package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
  98. package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
  99. package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
  100. package/dist/core/adapter/adapters/index.js +0 -9
  101. package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
  102. package/dist/core/adapter/adapters/okf-adapter.js +4 -0
  103. package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
  104. package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
  105. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
  106. package/dist/core/adapter/execution-source.js +308 -0
  107. package/dist/core/adapter/recognize-match.js +36 -13
  108. package/dist/core/adapter/registry.js +0 -9
  109. package/dist/core/asset/stash-meta.js +94 -4
  110. package/dist/core/common.js +6 -11
  111. package/dist/core/config/config-io.js +3 -3
  112. package/dist/core/config/config-schema.js +18 -40
  113. package/dist/core/config/config-sources.js +11 -21
  114. package/dist/core/config/config-walker.js +31 -13
  115. package/dist/core/config/config.js +21 -20
  116. package/dist/core/config/schema/engines.js +8 -7
  117. package/dist/core/config/schema/improve-processes.js +29 -5
  118. package/dist/core/config/schema/primitives.js +1 -23
  119. package/dist/core/config/schema/sources-bundles.js +13 -16
  120. package/dist/core/errors.js +2 -0
  121. package/dist/core/events.js +68 -32
  122. package/dist/core/extra-params.js +1 -0
  123. package/dist/core/improve-result.js +315 -0
  124. package/dist/core/lesson-lint.js +0 -6
  125. package/dist/core/maintenance-barrier.js +4 -4
  126. package/dist/core/network-policy.js +152 -0
  127. package/dist/core/paths.js +1 -1
  128. package/dist/core/recognition-util.js +4 -4
  129. package/dist/core/registry-url.js +456 -0
  130. package/dist/core/state/migrations.js +161 -47
  131. package/dist/core/state-db.js +453 -80
  132. package/dist/core/system-error.js +32 -0
  133. package/dist/core/time.js +2 -12
  134. package/dist/core/write-source.js +0 -18
  135. package/dist/execution/directory-identity.js +52 -0
  136. package/dist/execution/executable-identity.js +107 -0
  137. package/dist/execution/guarded-source.js +398 -0
  138. package/dist/execution/json.js +95 -0
  139. package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
  140. package/dist/execution/record.js +55 -0
  141. package/dist/execution/resolved-request.js +730 -0
  142. package/dist/execution/source.js +320 -0
  143. package/dist/indexer/bundle-identity-guard.js +5 -4
  144. package/dist/indexer/db/graph-db.js +33 -0
  145. package/dist/indexer/graph/graph-boost.js +3 -4
  146. package/dist/indexer/graph/graph-extraction.js +562 -373
  147. package/dist/indexer/index-written-assets.js +65 -34
  148. package/dist/indexer/indexer.js +443 -222
  149. package/dist/indexer/installations.js +6 -0
  150. package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
  151. package/dist/indexer/passes/memory-inference.js +227 -174
  152. package/dist/indexer/scan/doc-to-entry.js +7 -9
  153. package/dist/indexer/scan/drain-dir.js +51 -23
  154. package/dist/indexer/search/db-search.js +98 -22
  155. package/dist/indexer/search/search-fields.js +1 -1
  156. package/dist/indexer/search/search-hit-enrichers.js +1 -1
  157. package/dist/indexer/search/search-source.js +7 -16
  158. package/dist/indexer/search/semantic-status.js +10 -1
  159. package/dist/indexer/usage/show-usage.js +105 -0
  160. package/dist/indexer/usage/usage-events.js +7 -2
  161. package/dist/indexer/walk/matchers.js +40 -10
  162. package/dist/indexer/walk/path-resolver.js +5 -2
  163. package/dist/indexer/walk/walker.js +20 -2
  164. package/dist/integrations/agent/builder-shared.js +3 -6
  165. package/dist/integrations/agent/conversation-fallback.js +16 -0
  166. package/dist/integrations/agent/engine-resolution.js +87 -87
  167. package/dist/integrations/agent/execution-cascade.js +566 -0
  168. package/dist/integrations/agent/execution-definitions.js +211 -0
  169. package/dist/integrations/agent/execution-lowering.js +811 -0
  170. package/dist/integrations/agent/execution-preparation.js +67 -0
  171. package/dist/integrations/agent/index.js +0 -2
  172. package/dist/integrations/agent/inline-execution.js +74 -0
  173. package/dist/integrations/agent/model-map.js +515 -0
  174. package/dist/integrations/agent/persona-fallback.js +30 -0
  175. package/dist/integrations/agent/request-lowering.js +186 -0
  176. package/dist/integrations/agent/runner-dispatch.js +230 -37
  177. package/dist/integrations/agent/runner.js +12 -83
  178. package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
  179. package/dist/integrations/harnesses/aider/index.js +0 -1
  180. package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
  181. package/dist/integrations/harnesses/amazonq/index.js +0 -1
  182. package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
  183. package/dist/integrations/harnesses/claude/index.js +1 -5
  184. package/dist/integrations/harnesses/claude/session-log.js +3 -33
  185. package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
  186. package/dist/integrations/harnesses/codex/index.js +0 -1
  187. package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
  188. package/dist/integrations/harnesses/copilot/index.js +0 -1
  189. package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
  190. package/dist/integrations/harnesses/gemini/index.js +0 -1
  191. package/dist/integrations/harnesses/index.js +4 -44
  192. package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
  193. package/dist/integrations/harnesses/opencode/index.js +0 -2
  194. package/dist/integrations/harnesses/opencode/session-log.js +14 -204
  195. package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
  196. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
  197. package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
  198. package/dist/integrations/harnesses/openhands/index.js +0 -1
  199. package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
  200. package/dist/integrations/harnesses/pi/index.js +0 -1
  201. package/dist/integrations/harnesses/shared.js +0 -1
  202. package/dist/integrations/harnesses/types.js +1 -3
  203. package/dist/integrations/lockfile.js +82 -79
  204. package/dist/integrations/session-logs/index.js +6 -17
  205. package/dist/integrations/session-logs/provider-base.js +1 -29
  206. package/dist/llm/client.js +10 -5
  207. package/dist/llm/embedder.js +6 -7
  208. package/dist/llm/embedders/local.js +37 -88
  209. package/dist/llm/embedders/types.js +1 -1
  210. package/dist/llm/graph-extract.js +75 -50
  211. package/dist/llm/index-passes.js +43 -5
  212. package/dist/llm/memory-infer.js +8 -6
  213. package/dist/llm/metadata-enhance.js +5 -3
  214. package/dist/llm/structured-call.js +122 -25
  215. package/dist/output/format-exempt.js +1 -1
  216. package/dist/output/render-registry.js +0 -16
  217. package/dist/output/renderers.js +12 -7
  218. package/dist/output/shapes/curate.js +1 -0
  219. package/dist/output/shapes/helpers.js +10 -2
  220. package/dist/output/shapes/passthrough.js +2 -0
  221. package/dist/output/text/command-format.js +28 -32
  222. package/dist/output/text/health-format.js +1 -29
  223. package/dist/output/text/migrate.js +6 -56
  224. package/dist/output/text/proposal-format.js +16 -1
  225. package/dist/output/text/workflow-format.js +16 -0
  226. package/dist/registry/network.js +279 -0
  227. package/dist/registry/pinned-request-helper.js +247 -0
  228. package/dist/registry/pinned-transport.js +717 -0
  229. package/dist/registry/providers/skills-sh.js +18 -6
  230. package/dist/registry/providers/static-index.js +20 -7
  231. package/dist/registry/resolve.js +53 -28
  232. package/dist/scripts/akm-migrate-node.js +19122 -52218
  233. package/dist/scripts/akm-migrate.js +19064 -51567
  234. package/dist/setup/registry-stash-loader.js +64 -20
  235. package/dist/setup/semantic-assets.js +9 -34
  236. package/dist/setup/setup.js +12 -30
  237. package/dist/setup/source-identity.js +17 -0
  238. package/dist/setup/steps/sources.js +36 -15
  239. package/dist/setup/steps/tasks.js +39 -11
  240. package/dist/sources/providers/git-provider.js +3 -3
  241. package/dist/sources/providers/npm.js +2 -2
  242. package/dist/sources/providers/provider-utils.js +4 -3
  243. package/dist/sources/providers/website.js +11 -7
  244. package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
  245. package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
  246. package/dist/sources/website-url.js +73 -0
  247. package/dist/storage/engines/sqlite-migrations.js +81 -26
  248. package/dist/storage/managed-db.js +27 -24
  249. package/dist/storage/repositories/events-repository.js +3 -0
  250. package/dist/storage/repositories/index-connection.js +42 -10
  251. package/dist/storage/repositories/index-entries-repository.js +167 -207
  252. package/dist/storage/repositories/index-entry-mapper.js +8 -12
  253. package/dist/storage/repositories/index-entry-schema.js +255 -0
  254. package/dist/storage/repositories/index-fts-repository.js +10 -10
  255. package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
  256. package/dist/storage/repositories/index-meta-repository.js +0 -11
  257. package/dist/storage/repositories/index-schema.js +71 -342
  258. package/dist/storage/repositories/index-utility-repository.js +12 -17
  259. package/dist/storage/repositories/index-vec-repository.js +1 -1
  260. package/dist/storage/repositories/proposals-repository.js +4 -127
  261. package/dist/storage/repositories/registry-cache.js +2 -1
  262. package/dist/storage/repositories/task-history-repository.js +20 -40
  263. package/dist/storage/repositories/workflow-runs-repository.js +228 -129
  264. package/dist/storage/sqlite-read-snapshot.js +148 -0
  265. package/dist/tasks/backends/cron.js +170 -42
  266. package/dist/tasks/backends/index.js +1 -1
  267. package/dist/tasks/backends/launchd.js +787 -202
  268. package/dist/tasks/backends/schtasks.js +282 -83
  269. package/dist/tasks/embedded.js +7 -7
  270. package/dist/tasks/frozen-script.js +50 -0
  271. package/dist/tasks/resolve-akm-bin.js +5 -1
  272. package/dist/tasks/runner.js +239 -251
  273. package/dist/tasks/runtime-v3.js +281 -0
  274. package/dist/tasks/scheduler-binding.js +272 -0
  275. package/dist/tasks/scheduler-invocation.js +57 -43
  276. package/dist/tasks/scheduler-sync.js +654 -0
  277. package/dist/tasks/source-v3.js +752 -0
  278. package/dist/tasks/standalone-script-entry.js +5 -0
  279. package/dist/tasks/task-id.js +29 -0
  280. package/dist/workflows/authoring/authoring.js +15 -32
  281. package/dist/workflows/exec/dispatch-redaction.js +14 -8
  282. package/dist/workflows/exec/exec-unit.js +7 -28
  283. package/dist/workflows/exec/frozen-judge.js +57 -89
  284. package/dist/workflows/exec/lowering-notices.js +23 -0
  285. package/dist/workflows/exec/native-executor.js +301 -458
  286. package/dist/workflows/exec/param-secrets.js +4 -3
  287. package/dist/workflows/exec/run-workflow.js +26 -32
  288. package/dist/workflows/exec/step-work.js +105 -109
  289. package/dist/workflows/exec/unit-dispatch.js +103 -27
  290. package/dist/workflows/exec/unit-writer.js +3 -3
  291. package/dist/workflows/exec/worktree.js +2 -2
  292. package/dist/workflows/ir/compile.js +86 -72
  293. package/dist/workflows/ir/environment-v4.js +328 -0
  294. package/dist/workflows/ir/freeze-v4.js +122 -0
  295. package/dist/workflows/ir/plan-hash.js +13 -7
  296. package/dist/workflows/ir/schema-v4.js +525 -0
  297. package/dist/workflows/ir/schema.js +25 -284
  298. package/dist/workflows/ir/source-freeze-v4.js +506 -0
  299. package/dist/workflows/parser.js +27 -24
  300. package/dist/workflows/program/schema.js +1 -2
  301. package/dist/workflows/renderer.js +42 -29
  302. package/dist/workflows/resource-limits.js +4 -5
  303. package/dist/workflows/runtime/agent-identity.js +11 -13
  304. package/dist/workflows/runtime/plan-classifier.js +8 -8
  305. package/dist/workflows/runtime/runs.js +27 -43
  306. package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
  307. package/dist/workflows/source-files.js +373 -0
  308. package/dist/workflows/source-ir/compile.js +196 -0
  309. package/dist/workflows/source-ir/github-yaml.js +577 -0
  310. package/dist/workflows/source-ir/ordering.js +38 -0
  311. package/dist/workflows/source-ir/program.js +50 -0
  312. package/dist/workflows/source-ir/result.js +26 -0
  313. package/dist/workflows/source-ir/schema.js +772 -0
  314. package/dist/workflows/source-ir/semantics.js +242 -0
  315. package/dist/workflows/source-ir/uses.js +14 -0
  316. package/docs/README.md +2 -0
  317. package/docs/migration/README.md +3 -1
  318. package/docs/migration/release-notes/0.9.2.md +55 -0
  319. package/docs/migration/release-notes/README.md +5 -0
  320. package/docs/migration/v0.8-to-v0.9.md +76 -1077
  321. package/docs/migration/v0.9.0-troubleshooting.md +104 -516
  322. package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
  323. package/docs/reference/README.md +1 -0
  324. package/docs/reference/cli.md +230 -98
  325. package/docs/reference/configuration.md +153 -27
  326. package/docs/reference/data-and-telemetry.md +19 -1
  327. package/docs/reference/supported-formats.md +23 -3
  328. package/docs/reference/tasks.md +182 -0
  329. package/docs/reference/workflow-schema.md +91 -40
  330. package/docs/reference/workflows.md +33 -6
  331. package/package.json +10 -6
  332. package/schemas/akm-config.json +372 -216
  333. package/schemas/akm-task.json +324 -80
  334. package/schemas/akm-workflow.json +6 -9
  335. package/dist/core/migration-operation.js +0 -75
  336. package/dist/integrations/agent/model-aliases.js +0 -74
  337. package/dist/tasks/parser.js +0 -380
  338. package/dist/tasks/schema.js +0 -123
  339. package/dist/tasks/validator.js +0 -80
  340. package/dist/workflows/ir/freeze.js +0 -320
  341. package/dist/workflows/runtime/document-cache.js +0 -13
@@ -1,10 +1,34 @@
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
+ * Append-only events stream — backed by state.db (#204, Phase 3).
6
+ *
7
+ * Every mutating CLI verb funnels through `appendEvent` so external
8
+ * observers (sync, replication, audit, dashboards) can react to stash
9
+ * changes. Events are stored in the `events` table in `state.db`
10
+ * (SQLite, WAL mode) instead of a flat `events.jsonl` file.
11
+ *
12
+ * The helper is the only thing in akm that writes to the events table. It
13
+ * accepts an injectable `dbPath` (via `EventsContext`) so tests can pin a
14
+ * tmpdir without any global mutation.
15
+ *
16
+ * Format (each EventEnvelope):
17
+ * { "schemaVersion": 1, "id": <number>, "ts": "<ISO>",
18
+ * "eventType": "<verb>", "ref"?: "<asset-ref>", ... }
19
+ *
20
+ * - `id` is a monotonic SQLite AUTOINCREMENT rowid. Callers can persist it
21
+ * as a durable cursor for `--since` resumption. The public API surfaces this
22
+ * as the opaque `nextOffset` number.
23
+ * - `ts` is ISO-8601 (UTC, millisecond precision).
24
+ */
25
+ import fs from "node:fs";
26
+ import { openDatabase } from "../storage/database.js";
4
27
  import { insertEvent, readStateEvents } from "../storage/repositories/events-repository.js";
5
28
  import { rethrowIfTestIsolationError } from "./errors.js";
6
29
  import { getStateDbPath, openStateDatabase, withStateDb } from "./state-db.js";
7
30
  import { borrowScopedStateDb } from "./state-db-scope.js";
31
+ import { isReadOnlyFilesystemError } from "./system-error.js";
8
32
  import { error } from "./warn.js";
9
33
  /**
10
34
  * Resolve the state.db path from context:
@@ -39,6 +63,8 @@ function resolveNow(ctx) {
39
63
  * connection.
40
64
  */
41
65
  export function appendEvent(input, ctx) {
66
+ if (ctx?.readOnly)
67
+ return;
42
68
  const now = resolveNow(ctx);
43
69
  const ts = new Date(now()).toISOString();
44
70
  const row = { eventType: input.eventType, ts, ref: input.ref, metadata: input.metadata };
@@ -68,31 +94,40 @@ export function appendEvent(input, ctx) {
68
94
  catch (err) {
69
95
  // Never mask the bun-test isolation guard as a silent "events failed".
70
96
  rethrowIfTestIsolationError(err);
97
+ // Read-only sandboxes can serve an existing index but cannot create the
98
+ // maintenance lock or state DB used by best-effort usage tracking. That is
99
+ // expected for a read verb; unrelated storage failures remain visible.
100
+ if (isReadOnlyFilesystemError(err))
101
+ return;
71
102
  // Best-effort: events stream failures must not break the mutating verb.
72
103
  // Surface once to stderr so operators can diagnose.
73
104
  error(`akm: appendEvent failed: ${String(err)}`);
74
105
  }
75
106
  }
76
- /**
77
- * 0.9.0 breaking change (owner ruling 12): `akm sync` used to persist
78
- * `eventType: "save"`; it now writes `"sync"` instead (matching the command
79
- * name). Existing `state.db` rows — and any user script running
80
- * `akm log --type save` — still carry the old spelling. Rather than
81
- * rewriting historical rows (a migration users never asked for, on data we
82
- * don't get to touch at rest), reads treat the two names as synonyms: asking
83
- * for either "save" or "sync" returns rows written under both names. Only
84
- * the WRITE path (sources-cli.ts's `runSyncBody`) changed.
85
- */
86
- const SAVE_SYNC_EVENT_TYPE_ALIASES = new Set(["save", "sync"]);
87
107
  /**
88
108
  * Read all events matching the filter. Returns a `nextOffset` that callers
89
109
  * can persist between processes for monotonic resumption.
90
110
  */
91
111
  export function readEvents(options = {}, ctx) {
112
+ if (ctx?.readOnlySnapshotUnavailable)
113
+ return { events: [], nextOffset: 0 };
92
114
  const dbPath = resolveDbPath(ctx);
93
115
  let db;
116
+ let ownsDb = false;
94
117
  try {
95
- db = openStateDatabase(dbPath);
118
+ if (ctx?.db) {
119
+ db = ctx.db;
120
+ }
121
+ else if (ctx?.readOnly) {
122
+ if (!fs.existsSync(dbPath))
123
+ return { events: [], nextOffset: 0 };
124
+ db = openDatabase(dbPath, { readonly: true, create: false });
125
+ ownsDb = true;
126
+ }
127
+ else {
128
+ db = openStateDatabase(dbPath);
129
+ ownsDb = true;
130
+ }
96
131
  }
97
132
  catch (err) {
98
133
  // Never mask the bun-test isolation guard as "no events".
@@ -101,12 +136,7 @@ export function readEvents(options = {}, ctx) {
101
136
  return { events: [], nextOffset: 0 };
102
137
  }
103
138
  try {
104
- // A "save"/"sync" query can't be expressed as a single SQL `event_type =
105
- // ?` match (see SAVE_SYNC_EVENT_TYPE_ALIASES above), so widen the SQL
106
- // filter to "no type filter" for that one case and apply the alias match
107
- // client-side alongside the existing tag post-filter below.
108
- const typeIsAliased = options.type !== undefined && SAVE_SYNC_EVENT_TYPE_ALIASES.has(options.type);
109
- // D-38: a JS-side post-filter (the type alias above, or the tag filters
139
+ // D-38: a JS-side post-filter (the tag filters
110
140
  // below) runs AFTER the SQL read, so a SQL-level LIMIT applied before it
111
141
  // could drop rows the post-filter would have kept out anyway, silently
112
142
  // returning fewer than `limit` (or the wrong — oldest-in-the-SQL-window —
@@ -114,21 +144,26 @@ export function readEvents(options = {}, ctx) {
114
144
  // downstream can shrink the result further; otherwise read unbounded (the
115
145
  // pre-existing behavior) and apply `limit` ourselves, below, AFTER the
116
146
  // post-filter runs.
117
- const needsPostFilter = typeIsAliased ||
118
- (options.excludeTags?.length ?? 0) > 0 ||
119
- (options.includeTags?.length ?? 0) > 0 ||
120
- options.runId !== undefined;
147
+ const needsPostFilter = (options.excludeTags?.length ?? 0) > 0 || (options.includeTags?.length ?? 0) > 0 || options.runId !== undefined;
121
148
  const pushLimitToSql = options.limit !== undefined && !needsPostFilter;
122
- const { events: rawEvents, nextId } = readStateEvents(db, {
123
- sinceId: options.sinceOffset,
124
- since: options.since,
125
- type: typeIsAliased ? undefined : options.type,
126
- ref: options.ref,
127
- ...(pushLimitToSql ? { limit: options.limit } : {}),
128
- });
149
+ let rawEvents;
150
+ let nextId;
151
+ try {
152
+ ({ events: rawEvents, nextId } = readStateEvents(db, {
153
+ sinceId: options.sinceOffset,
154
+ since: options.since,
155
+ type: options.type,
156
+ ref: options.ref,
157
+ ...(pushLimitToSql ? { limit: options.limit } : {}),
158
+ }));
159
+ }
160
+ catch (error) {
161
+ if (ctx?.readOnly && error instanceof Error && /no such table:/i.test(error.message)) {
162
+ return { events: [], nextOffset: options.sinceOffset ?? 0 };
163
+ }
164
+ throw error;
165
+ }
129
166
  const filtered = rawEvents.filter((envelope) => {
130
- if (typeIsAliased && !SAVE_SYNC_EVENT_TYPE_ALIASES.has(envelope.eventType))
131
- return false;
132
167
  if (options.runId !== undefined && envelope.metadata?.runId !== options.runId)
133
168
  return false;
134
169
  // Apply tag filters after the indexed state.db read.
@@ -148,6 +183,7 @@ export function readEvents(options = {}, ctx) {
148
183
  return { events, nextOffset: nextId };
149
184
  }
150
185
  finally {
151
- db.close();
186
+ if (ownsDb)
187
+ db.close();
152
188
  }
153
189
  }
@@ -10,6 +10,7 @@ export const EXTRA_PARAMS_PROTECTED_TOP_LEVEL_KEYS = [
10
10
  "stream",
11
11
  "streamoptions",
12
12
  "enablethinking",
13
+ "reasoningeffort",
13
14
  "chattemplatekwargs",
14
15
  ];
15
16
  export const EXTRA_PARAMS_CREDENTIAL_KEYS = [
@@ -1,17 +1,20 @@
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
+ import { cloneExecutionJsonObject } from "../execution/json.js";
4
5
  const COMMON_FIELDS = [
5
6
  "schemaVersion",
6
7
  "ok",
7
8
  "scope",
8
9
  "dryRun",
10
+ "notices",
9
11
  "skipped",
10
12
  "guidance",
11
13
  "memorySummary",
12
14
  "memoryCleanup",
13
15
  "cyclesRun",
14
16
  "plannedRefs",
17
+ "plan",
15
18
  "actions",
16
19
  "distillSkipped",
17
20
  "validationFailures",
@@ -55,6 +58,308 @@ function requireExactFields(value, allowed) {
55
58
  if (unknown.length > 0)
56
59
  fail(`unknown field${unknown.length === 1 ? "" : "s"}: ${unknown.sort().join(", ")}`);
57
60
  }
61
+ function requireNumber(value, field, path) {
62
+ if (typeof value[field] !== "number" || !Number.isFinite(value[field]))
63
+ fail(`${path}.${field} must be a number`);
64
+ }
65
+ function requireCount(value, field, path) {
66
+ requireNumber(value, field, path);
67
+ if (!Number.isSafeInteger(value[field]) || value[field] < 0) {
68
+ fail(`${path}.${field} must be a non-negative integer`);
69
+ }
70
+ }
71
+ function validateLoweringNotices(value) {
72
+ if (!Array.isArray(value))
73
+ fail("notices must be an array");
74
+ for (let index = 0; index < value.length; index += 1) {
75
+ const notice = value[index];
76
+ if (!isRecord(notice))
77
+ fail(`notices[${index}] must be an object`);
78
+ requireExactFields(notice, new Set(["code", "severity", "adapter", "field", "message", "details"]));
79
+ for (const field of ["code", "adapter", "message"]) {
80
+ if (typeof notice[field] !== "string")
81
+ fail(`notices[${index}].${field} must be a string`);
82
+ }
83
+ if (notice.severity !== "info" && notice.severity !== "warning") {
84
+ fail(`notices[${index}].severity is invalid`);
85
+ }
86
+ if (notice.field !== undefined && notice.field !== null && typeof notice.field !== "string") {
87
+ fail(`notices[${index}].field must be a string or null`);
88
+ }
89
+ if (notice.details !== undefined && notice.details !== null) {
90
+ try {
91
+ cloneExecutionJsonObject(notice.details, `notices[${index}].details`);
92
+ }
93
+ catch {
94
+ fail(`notices[${index}].details must be a JSON object or null`);
95
+ }
96
+ }
97
+ }
98
+ }
99
+ function validateConsolidationPlan(value) {
100
+ if (!isRecord(value))
101
+ fail("plan.consolidation must be an object");
102
+ requireExactFields(value, new Set([
103
+ "configured",
104
+ "effective",
105
+ "poolSize",
106
+ "candidatePoolSize",
107
+ "gates",
108
+ "wouldRun",
109
+ "reason",
110
+ "estimatedChunks",
111
+ ]));
112
+ if (!isRecord(value.configured))
113
+ fail("plan.consolidation.configured must be an object");
114
+ requireExactFields(value.configured, new Set(["enabled", "minPoolSize", "limit", "maxChunkSize", "incrementalSince"]));
115
+ if (value.configured.enabled !== undefined && typeof value.configured.enabled !== "boolean") {
116
+ fail("plan.consolidation.configured.enabled must be a boolean");
117
+ }
118
+ for (const field of ["minPoolSize", "limit", "maxChunkSize"]) {
119
+ if (value.configured[field] !== undefined && typeof value.configured[field] !== "number") {
120
+ fail(`plan.consolidation.configured.${field} must be a number`);
121
+ }
122
+ }
123
+ if (value.configured.incrementalSince !== undefined && typeof value.configured.incrementalSince !== "string") {
124
+ fail("plan.consolidation.configured.incrementalSince must be a string");
125
+ }
126
+ if (!isRecord(value.effective))
127
+ fail("plan.consolidation.effective must be an object");
128
+ requireExactFields(value.effective, new Set(["enabled", "minPoolSize", "limit", "chunkSize"]));
129
+ if (typeof value.effective.enabled !== "boolean")
130
+ fail("plan.consolidation.effective.enabled must be a boolean");
131
+ requireCount(value.effective, "minPoolSize", "plan.consolidation.effective");
132
+ requireCount(value.effective, "chunkSize", "plan.consolidation.effective");
133
+ if (value.effective.limit !== undefined && typeof value.effective.limit !== "number") {
134
+ fail("plan.consolidation.effective.limit must be a number");
135
+ }
136
+ requireCount(value, "poolSize", "plan.consolidation");
137
+ requireCount(value, "candidatePoolSize", "plan.consolidation");
138
+ requireCount(value, "estimatedChunks", "plan.consolidation");
139
+ if (typeof value.wouldRun !== "boolean")
140
+ fail("plan.consolidation.wouldRun must be a boolean");
141
+ if (typeof value.reason !== "string")
142
+ fail("plan.consolidation.reason must be a string");
143
+ if (!isRecord(value.gates))
144
+ fail("plan.consolidation.gates must be an object");
145
+ requireExactFields(value.gates, new Set(["profile", "minimumPool", "delta"]));
146
+ for (const gateName of ["profile", "minimumPool", "delta"]) {
147
+ const gate = value.gates[gateName];
148
+ if (!isRecord(gate))
149
+ fail(`plan.consolidation.gates.${gateName} must be an object`);
150
+ requireExactFields(gate, new Set(["passed", "reason"]));
151
+ if (typeof gate.passed !== "boolean" || typeof gate.reason !== "string") {
152
+ fail(`plan.consolidation.gates.${gateName} must contain boolean passed and string reason`);
153
+ }
154
+ }
155
+ }
156
+ function validateProactivePlan(value) {
157
+ if (!isRecord(value))
158
+ fail("plan.proactive must be an object");
159
+ requireExactFields(value, new Set(["configured", "effective", "candidatePool", "dueTotal", "neverReflected", "selected", "selectedRefs"]));
160
+ if (!isRecord(value.configured))
161
+ fail("plan.proactive.configured must be an object");
162
+ requireExactFields(value.configured, new Set(["dueDays", "maxPerRun", "limit"]));
163
+ for (const field of ["dueDays", "maxPerRun", "limit"]) {
164
+ if (value.configured[field] !== undefined && typeof value.configured[field] !== "number") {
165
+ fail(`plan.proactive.configured.${field} must be a number`);
166
+ }
167
+ }
168
+ if (!isRecord(value.effective))
169
+ fail("plan.proactive.effective must be an object");
170
+ requireExactFields(value.effective, new Set(["dueDays", "maxPerRun"]));
171
+ requireNumber(value.effective, "dueDays", "plan.proactive.effective");
172
+ requireNumber(value.effective, "maxPerRun", "plan.proactive.effective");
173
+ for (const field of ["candidatePool", "dueTotal", "neverReflected", "selected"]) {
174
+ requireCount(value, field, "plan.proactive");
175
+ }
176
+ if (!Array.isArray(value.selectedRefs) || value.selectedRefs.some((ref) => typeof ref !== "string")) {
177
+ fail("plan.proactive.selectedRefs must be an array of strings");
178
+ }
179
+ if (value.selected !== value.selectedRefs.length) {
180
+ fail("plan.proactive.selected must equal plan.proactive.selectedRefs.length");
181
+ }
182
+ }
183
+ function validateImprovePlan(value, dryRun, plannedRefNames) {
184
+ if (!isRecord(value))
185
+ fail("plan must be an object");
186
+ requireExactFields(value, new Set([
187
+ "mode",
188
+ "dispatch",
189
+ "snapshot",
190
+ "candidates",
191
+ "limits",
192
+ "gates",
193
+ "effectiveRefs",
194
+ "proactive",
195
+ "consolidation",
196
+ "stages",
197
+ "triage",
198
+ ]));
199
+ if (value.mode !== "estimate" && value.mode !== "execution")
200
+ fail('plan.mode must be "estimate" or "execution"');
201
+ if (typeof value.dispatch !== "boolean")
202
+ fail("plan.dispatch must be a boolean");
203
+ if ((value.mode === "estimate" && value.dispatch) || (value.mode === "execution" && !value.dispatch)) {
204
+ fail("plan.dispatch must be false for estimate mode and true for execution mode");
205
+ }
206
+ if (dryRun && (value.mode !== "estimate" || value.dispatch !== false)) {
207
+ fail("dryRun=true requires estimate mode with dispatch=false");
208
+ }
209
+ if (!dryRun && (value.mode !== "execution" || value.dispatch !== true)) {
210
+ fail("dryRun=false requires execution mode with dispatch=true");
211
+ }
212
+ if (!isRecord(value.snapshot))
213
+ fail("plan.snapshot must be an object");
214
+ requireExactFields(value.snapshot, new Set(["status", "reason"]));
215
+ if (typeof value.snapshot.status !== "string" ||
216
+ !new Set(["ready", "missing", "incompatible", "unknown"]).has(value.snapshot.status)) {
217
+ fail("plan.snapshot.status is invalid");
218
+ }
219
+ if (typeof value.snapshot.reason !== "string")
220
+ fail("plan.snapshot.reason must be a string");
221
+ if (!isRecord(value.candidates))
222
+ fail("plan.candidates must be an object");
223
+ requireExactFields(value.candidates, new Set(["rawInScope", "selected", "effective"]));
224
+ for (const field of ["rawInScope", "selected", "effective"]) {
225
+ requireCount(value.candidates, field, "plan.candidates");
226
+ }
227
+ if (value.candidates.rawInScope < value.candidates.selected) {
228
+ fail("plan.candidates.rawInScope cannot be less than plan.candidates.selected");
229
+ }
230
+ if (value.candidates.selected < value.candidates.effective) {
231
+ fail("plan.candidates.selected cannot be less than plan.candidates.effective");
232
+ }
233
+ if (!isRecord(value.limits))
234
+ fail("plan.limits must be an object");
235
+ requireExactFields(value.limits, new Set(["configured", "effective", "additiveReplayAllowance", "totalCeiling"]));
236
+ if (!isRecord(value.limits.configured))
237
+ fail("plan.limits.configured must be an object");
238
+ requireExactFields(value.limits.configured, new Set(["cli", "profile", "reflect"]));
239
+ for (const field of ["cli", "profile", "reflect"]) {
240
+ if (value.limits.configured[field] !== undefined)
241
+ requireCount(value.limits.configured, field, "plan.limits.configured");
242
+ }
243
+ if (value.limits.effective !== undefined)
244
+ requireCount(value.limits, "effective", "plan.limits");
245
+ requireCount(value.limits, "additiveReplayAllowance", "plan.limits");
246
+ if (value.limits.totalCeiling !== undefined)
247
+ requireCount(value.limits, "totalCeiling", "plan.limits");
248
+ if (value.limits.effective === undefined && value.limits.totalCeiling !== undefined) {
249
+ fail("plan.limits.totalCeiling must be omitted when plan.limits.effective is unbounded");
250
+ }
251
+ if (value.limits.effective !== undefined &&
252
+ value.limits.totalCeiling !== value.limits.effective + value.limits.additiveReplayAllowance) {
253
+ fail("plan.limits.totalCeiling must equal plan.limits.effective + plan.limits.additiveReplayAllowance");
254
+ }
255
+ const gateNames = new Set(["profile", "cleanup", "validation", "signal", "disk", "limit"]);
256
+ if (!Array.isArray(value.gates))
257
+ fail("plan.gates must be an array");
258
+ const gateRemovedByName = new Map();
259
+ for (const gate of value.gates) {
260
+ if (!isRecord(gate))
261
+ fail("plan.gates entries must be objects");
262
+ requireExactFields(gate, new Set(["name", "removed", "reason"]));
263
+ if (typeof gate.name !== "string" || !gateNames.has(gate.name))
264
+ fail("plan.gates.name is invalid");
265
+ requireCount(gate, "removed", "plan.gates entry");
266
+ if (typeof gate.reason !== "string")
267
+ fail("plan.gates.reason must be a string");
268
+ if (gateRemovedByName.has(gate.name))
269
+ fail(`plan.gates must contain exactly one ${gate.name} gate`);
270
+ gateRemovedByName.set(gate.name, gate.removed);
271
+ }
272
+ for (const gateName of gateNames) {
273
+ if (!gateRemovedByName.has(gateName))
274
+ fail(`plan.gates must contain exactly one ${gateName} gate`);
275
+ }
276
+ const preLimitRemoved = [...gateRemovedByName]
277
+ .filter(([gateName]) => gateName !== "limit")
278
+ .reduce((total, [, removed]) => total + removed, 0);
279
+ if (preLimitRemoved !== value.candidates.rawInScope - value.candidates.selected) {
280
+ fail("plan pre-limit gate removals must equal plan.candidates.rawInScope - plan.candidates.selected");
281
+ }
282
+ if (gateRemovedByName.get("limit") !==
283
+ value.candidates.selected - value.candidates.effective) {
284
+ fail("plan limit removal must equal plan.candidates.selected - plan.candidates.effective");
285
+ }
286
+ const lanes = new Set([
287
+ "scope",
288
+ "signal-delta",
289
+ "proactive",
290
+ "high-salience",
291
+ "distill-only",
292
+ "forgetting-safety",
293
+ "replay",
294
+ "unknown",
295
+ ]);
296
+ const reasons = new Set(["scope-ref", "scope-type", "memory-cleanup", "strategy_filtered_all_passes"]);
297
+ if (!Array.isArray(value.effectiveRefs))
298
+ fail("plan.effectiveRefs must be an array");
299
+ for (const entry of value.effectiveRefs) {
300
+ if (!isRecord(entry))
301
+ fail("plan.effectiveRefs entries must be objects");
302
+ requireExactFields(entry, new Set(["ref", "lane", "reason"]));
303
+ if (typeof entry.ref !== "string")
304
+ fail("plan.effectiveRefs.ref must be a string");
305
+ if (typeof entry.lane !== "string" || !lanes.has(entry.lane))
306
+ fail("plan.effectiveRefs.lane is invalid");
307
+ if (typeof entry.reason !== "string" || !reasons.has(entry.reason))
308
+ fail("plan.effectiveRefs.reason is invalid");
309
+ }
310
+ if (value.candidates.effective !== value.effectiveRefs.length) {
311
+ fail("plan.candidates.effective must equal plan.effectiveRefs.length");
312
+ }
313
+ const effectiveRefNames = value.effectiveRefs.map((entry) => entry.ref);
314
+ if (new Set(effectiveRefNames).size !== effectiveRefNames.length) {
315
+ fail("plan.effectiveRefs must not contain duplicate refs");
316
+ }
317
+ if (plannedRefNames.length !== effectiveRefNames.length ||
318
+ plannedRefNames.some((ref, index) => ref !== effectiveRefNames[index])) {
319
+ fail("plannedRefs must contain the same refs in the same order as plan.effectiveRefs");
320
+ }
321
+ const replayCount = value.effectiveRefs.filter((entry) => entry.lane === "replay").length;
322
+ const ordinaryCount = value.effectiveRefs.length - replayCount;
323
+ if (replayCount > value.limits.additiveReplayAllowance) {
324
+ fail("plan replay refs cannot exceed plan.limits.additiveReplayAllowance");
325
+ }
326
+ if (value.limits.effective !== undefined && ordinaryCount > value.limits.effective) {
327
+ fail("plan ordinary refs cannot exceed plan.limits.effective");
328
+ }
329
+ if (value.limits.totalCeiling !== undefined && value.effectiveRefs.length > value.limits.totalCeiling) {
330
+ fail("plan.effectiveRefs cannot exceed plan.limits.totalCeiling");
331
+ }
332
+ if (value.proactive !== undefined)
333
+ validateProactivePlan(value.proactive);
334
+ validateConsolidationPlan(value.consolidation);
335
+ const stageNames = new Set(["consolidation", "extract", "graph-extraction", "memory-inference"]);
336
+ if (!Array.isArray(value.stages))
337
+ fail("plan.stages must be an array");
338
+ for (const stage of value.stages) {
339
+ if (!isRecord(stage))
340
+ fail("plan.stages entries must be objects");
341
+ requireExactFields(stage, new Set(["name", "wouldRun", "reason"]));
342
+ if (typeof stage.name !== "string" || !stageNames.has(stage.name))
343
+ fail("plan.stages.name is invalid");
344
+ if (typeof stage.wouldRun !== "boolean" || typeof stage.reason !== "string") {
345
+ fail("plan.stages entries must contain boolean wouldRun and string reason");
346
+ }
347
+ }
348
+ if (!isRecord(value.triage))
349
+ fail("plan.triage must be an object");
350
+ requireExactFields(value.triage, new Set(["enabled", "configuredMode", "mode", "maxAcceptsPerRun", "maxDiffLines"]));
351
+ if (typeof value.triage.enabled !== "boolean")
352
+ fail("plan.triage.enabled must be a boolean");
353
+ for (const field of ["configuredMode", "mode"]) {
354
+ if (value.triage[field] !== "queue" && value.triage[field] !== "promote") {
355
+ fail(`plan.triage.${field} must be queue or promote`);
356
+ }
357
+ }
358
+ requireCount(value.triage, "maxAcceptsPerRun", "plan.triage");
359
+ if (value.triage.maxDiffLines !== undefined && typeof value.triage.maxDiffLines !== "number") {
360
+ fail("plan.triage.maxDiffLines must be a number");
361
+ }
362
+ }
58
363
  function validateCommon(value) {
59
364
  if (typeof value.ok !== "boolean")
60
365
  fail("ok must be a boolean");
@@ -62,6 +367,15 @@ function validateCommon(value) {
62
367
  fail("dryRun must be a boolean");
63
368
  if (!Array.isArray(value.plannedRefs))
64
369
  fail("plannedRefs must be an array");
370
+ if (value.notices !== undefined)
371
+ validateLoweringNotices(value.notices);
372
+ const plannedRefNames = value.plannedRefs.map((entry, index) => {
373
+ if (!isRecord(entry) || typeof entry.ref !== "string")
374
+ fail(`plannedRefs[${index}].ref must be a string`);
375
+ return entry.ref;
376
+ });
377
+ if (value.plan !== undefined)
378
+ validateImprovePlan(value.plan, value.dryRun, plannedRefNames);
65
379
  if (!isRecord(value.scope))
66
380
  fail("scope must be an object");
67
381
  requireExactFields(value.scope, new Set(["mode", "value"]));
@@ -127,6 +441,7 @@ function validateCommon(value) {
127
441
  "cycleMetrics",
128
442
  "sync",
129
443
  "terminated",
444
+ "plan",
130
445
  ]) {
131
446
  if (value[field] !== undefined && !isRecord(value[field]))
132
447
  fail(`${field} must be an object`);
@@ -20,7 +20,6 @@
20
20
  * lets it run from any code path (CLI, proposal-accept, asset-spec tests)
21
21
  * without dragging in the rest of the runtime.
22
22
  */
23
- import fs from "node:fs";
24
23
  import { parseFrontmatter } from "./asset/frontmatter.js";
25
24
  function isNonEmptyString(value) {
26
25
  return typeof value === "string" && value.trim().length > 0;
@@ -66,8 +65,3 @@ export function lintLessonContent(raw, pathForMessages) {
66
65
  }
67
66
  return { path: pathForMessages, findings };
68
67
  }
69
- /** Lint a lesson file on disk. Throws if the file cannot be read. */
70
- export function lintLessonFile(filePath) {
71
- const raw = fs.readFileSync(filePath, "utf8");
72
- return lintLessonContent(raw, filePath);
73
- }
@@ -10,9 +10,9 @@ import { createLockPayload, probeLock, reclaimStaleLock, releaseLock, tryAcquire
10
10
  import { getMaintenanceBarrierPath } from "./paths.js";
11
11
  const heldBarrierContext = new AsyncLocalStorage();
12
12
  /**
13
- * Serialize restore with the short critical section that creates every
14
- * long-lived AKM lock or workflow lease. The long operation keeps its own
15
- * lock/lease; this barrier is released immediately after acquisition.
13
+ * Serialize the short critical section that creates each long-lived AKM lock,
14
+ * lease, or state activity. The operation keeps its own ownership record; this
15
+ * barrier is released immediately after acquisition.
16
16
  */
17
17
  export function tryAcquireMaintenanceBarrier() {
18
18
  const lockPath = getMaintenanceBarrierPath();
@@ -107,7 +107,7 @@ function withMaintenanceStartBarrierSyncWait(run) {
107
107
  release();
108
108
  }
109
109
  }
110
- /** Register a long-lived operation atomically with restore's blocker scan. */
110
+ /** Register a long-lived state operation atomically with other start acquisitions. */
111
111
  export async function acquireMaintenanceActivity(name) {
112
112
  return withMaintenanceStartBarrierAsync(async () => {
113
113
  const directory = path.join(path.dirname(getMaintenanceBarrierPath()), "maintenance-activities");