akm-cli 0.9.17-alpha.1 → 0.9.17-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1623 -0
- package/STABILITY.md +11 -10
- package/dist/akm +124 -193
- package/dist/akm-migrate +38 -19
- package/dist/assets/hints/cli-hints-full.md +6 -7
- package/dist/assets/improve-strategies/catchup.json +0 -3
- package/dist/assets/improve-strategies/consolidate.json +0 -1
- package/dist/assets/improve-strategies/default.json +1 -2
- package/dist/assets/improve-strategies/proactive-maintenance.json +1 -2
- package/dist/assets/improve-strategies/quick.json +1 -2
- package/dist/assets/improve-strategies/reflect-distill.json +1 -2
- package/dist/assets/improve-strategies/thorough.json +0 -3
- package/dist/assets/prompts/consolidate-pair.md +20 -0
- package/dist/assets/prompts/retrieval-relevance-judge.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +20 -20
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -2
- package/dist/assets/templates/html/health.html +3 -5
- package/dist/cli/retired-commands.js +1 -1
- package/dist/cli/shared.js +6 -2
- package/dist/cli/unknown-flags.js +24 -1
- package/dist/cli.js +68 -10
- package/dist/commands/agent/agent-dispatch.js +1 -1
- package/dist/commands/command/command-execution.js +24 -62
- package/dist/commands/feedback-cli.js +0 -1
- package/dist/commands/health/accept-rate.js +2 -2
- package/dist/commands/health/archive-usage.js +92 -0
- package/dist/commands/health/checks.js +30 -75
- package/dist/commands/health/config-skew.js +38 -0
- package/dist/commands/health/data-dir-usage.js +25 -13
- package/dist/commands/health/egress.js +54 -0
- package/dist/commands/health/html-report.js +1 -42
- package/dist/commands/health/improve-metrics.js +136 -587
- package/dist/commands/health/md-report.js +1 -6
- package/dist/commands/health/plugin-staleness.js +53 -3
- package/dist/commands/health/renderers.js +12 -4
- package/dist/commands/health/report-view-model.js +14 -119
- package/dist/commands/health/types-improve.js +4 -19
- package/dist/commands/health/windows.js +64 -74
- package/dist/commands/health.js +135 -143
- package/dist/commands/improve/consolidate/chunking.js +25 -100
- package/dist/commands/improve/consolidate/continuity-check.js +137 -0
- package/dist/commands/improve/consolidate/pair-pass.js +791 -0
- package/dist/commands/improve/consolidate/sanitize.js +54 -149
- package/dist/commands/improve/consolidate.js +573 -1124
- package/dist/commands/improve/content-hash.js +16 -24
- package/dist/commands/improve/distill/content-repair.js +18 -100
- package/dist/commands/improve/distill-guards.js +20 -81
- package/dist/commands/improve/distill-promotion-policy.js +23 -243
- package/dist/commands/improve/distill.js +608 -1075
- package/dist/commands/improve/eligibility.js +126 -400
- package/dist/commands/improve/execution.js +8 -10
- package/dist/commands/improve/extract-prompt.js +1 -2
- package/dist/commands/improve/extract.js +487 -1046
- package/dist/commands/improve/feedback-valence.js +0 -25
- package/dist/commands/improve/improve-cli.js +54 -171
- package/dist/commands/improve/improve-result-file.js +10 -66
- package/dist/commands/improve/improve-strategies.js +35 -9
- package/dist/commands/improve/improve-usage-report.js +18 -64
- package/dist/commands/improve/improve.js +451 -1082
- package/dist/commands/improve/ledger.js +119 -0
- package/dist/commands/improve/locks.js +2 -8
- package/dist/commands/improve/loop-stages.js +401 -1192
- package/dist/commands/improve/memory/derived-ref.js +12 -77
- package/dist/commands/improve/memory/memory-belief.js +16 -118
- package/dist/commands/improve/memory/memory-improve.js +266 -14
- package/dist/commands/improve/outcome-loop.js +28 -156
- package/dist/commands/improve/planner.js +5 -15
- package/dist/commands/improve/preparation.js +776 -2349
- package/dist/commands/improve/proactive-maintenance.js +34 -101
- package/dist/commands/improve/reflect-noise.js +104 -280
- package/dist/commands/improve/reflect.js +642 -1364
- package/dist/commands/improve/retrieval-gate.js +127 -0
- package/dist/commands/improve/retrieval-scope.js +92 -0
- package/dist/commands/improve/salience.js +41 -240
- package/dist/commands/improve/session-asset.js +19 -100
- package/dist/commands/improve/stage.js +322 -0
- package/dist/commands/lint/base-linter.js +37 -15
- package/dist/commands/proposal/drain.js +258 -644
- package/dist/commands/proposal/proposal-cli.js +19 -20
- package/dist/commands/proposal/proposal-types.js +27 -41
- package/dist/commands/proposal/proposal.js +38 -8
- package/dist/commands/proposal/propose.js +134 -160
- package/dist/commands/proposal/repository.js +1099 -1475
- package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
- package/dist/commands/proposal/validators/proposal-validators.js +1 -1
- package/dist/commands/proposal/validators/proposals.js +22 -89
- package/dist/commands/read/curate.js +105 -462
- package/dist/commands/read/knowledge.js +3 -2
- package/dist/commands/read/search-cli.js +16 -33
- package/dist/commands/read/search.js +17 -23
- package/dist/commands/read/show.js +57 -108
- package/dist/commands/sources/bundle-cli.js +25 -2
- package/dist/commands/sources/bundle-config-ops.js +4 -0
- package/dist/commands/sources/dangerous-env-audit.js +1 -2
- package/dist/commands/sources/info.js +127 -29
- package/dist/commands/sources/installed-stashes.js +197 -746
- package/dist/commands/sources/schema-repair.js +98 -129
- package/dist/commands/sources/source-add.js +62 -12
- package/dist/commands/sources/source-manage.js +9 -2
- package/dist/commands/sources/stash-cli.js +24 -4
- package/dist/commands/tasks/explain.js +10 -13
- package/dist/commands/tasks/tasks-cli.js +12 -13
- package/dist/commands/tasks/tasks.js +350 -936
- package/dist/commands/tasks/validate.js +26 -24
- package/dist/commands/workflow/plan.js +22 -29
- package/dist/commands/workflow-cli.js +4 -4
- package/dist/core/adapter/adapters/akm-adapter.js +2 -1
- package/dist/core/adapter/adapters/akm-lint.js +2 -3
- package/dist/core/adapter/adapters/akm-metadata.js +42 -12
- package/dist/core/adapter/adapters/akm-task-adapter.js +29 -8
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
- package/dist/core/adapter/execution-source.js +17 -29
- package/dist/core/asset/asset-placement.js +4 -13
- package/dist/core/asset/resolve-ref.js +1 -1
- package/dist/core/bundle-id.js +42 -5
- package/dist/core/bundle-rename.js +285 -0
- package/dist/core/config/config-io.js +1 -2
- package/dist/core/config/config-schema.js +9 -34
- package/dist/core/config/config-walker.js +1 -1
- package/dist/core/config/config.js +184 -111
- package/dist/core/config/engine-semantics.js +0 -2
- package/dist/core/config/legacy-source-shape-shim.js +38 -9
- package/dist/core/config/schema/embedding.js +20 -5
- package/dist/core/config/schema/engines.js +5 -0
- package/dist/core/config/schema/execution.js +1 -1
- package/dist/core/config/schema/experimental.js +1 -1
- package/dist/core/config/schema/improve-processes.js +37 -135
- package/dist/core/config/schema/improve.js +4 -42
- package/dist/core/config/schema/index-config.js +9 -48
- package/dist/core/config/schema/scheduler.js +12 -12
- package/dist/core/config/schema/search.js +6 -22
- package/dist/core/env-secret-ref.js +0 -1
- package/dist/core/errors.js +8 -9
- package/dist/core/file-change.js +13 -5
- package/dist/core/file-lock.js +76 -173
- package/dist/core/improve-result.js +26 -7
- package/dist/core/improve-types.js +0 -1
- package/dist/core/logs-db.js +2 -2
- package/dist/core/loopback.js +7 -12
- package/dist/core/non-task-input.js +20 -0
- package/dist/core/parse.js +13 -16
- package/dist/core/paths.js +0 -31
- package/dist/core/redaction.js +109 -2
- package/dist/core/run-lock.js +2 -5
- package/dist/core/spawn-env.js +1 -1
- package/dist/core/state/migrations.js +123 -61
- package/dist/core/state-db-scope.js +2 -4
- package/dist/core/state-db.js +126 -692
- package/dist/core/time.js +0 -20
- package/dist/core/type-presentation.js +1 -9
- package/dist/core/write-source.js +294 -1005
- package/dist/execution/input-contract.js +1 -1
- package/dist/execution/resolved-request.js +135 -689
- package/dist/execution/source.js +63 -257
- package/dist/execution/target-ref.js +1 -1
- package/dist/indexer/bundle-identity-guard.js +2 -2
- package/dist/indexer/db/llm-cache.js +2 -2
- package/dist/indexer/ensure-index.js +46 -87
- package/dist/indexer/index-rebuild-lock.js +3 -11
- package/dist/indexer/index-writer-lock.js +8 -17
- package/dist/indexer/index-written-assets.js +141 -154
- package/dist/indexer/indexer.js +400 -1124
- package/dist/indexer/links/declared-links.js +90 -0
- package/dist/indexer/materialize-embeddings.js +60 -397
- package/dist/indexer/passes/memory-inference.js +81 -90
- package/dist/indexer/passes/metadata.js +132 -219
- package/dist/indexer/read-preflight.js +0 -7
- package/dist/indexer/scan/doc-to-entry.js +2 -3
- package/dist/indexer/scan/drain-dir.js +1 -1
- package/dist/indexer/search/db-search.js +190 -590
- package/dist/indexer/search/fts-query.js +30 -41
- package/dist/indexer/search/ranking.js +28 -154
- package/dist/indexer/search/search-attribution.js +12 -32
- package/dist/indexer/search/search-fields.js +11 -15
- package/dist/indexer/search/search-hit-enrichers.js +54 -85
- package/dist/indexer/search/search-source.js +1 -4
- package/dist/indexer/usage/usage-events.js +36 -7
- package/dist/indexer/walk/walker.js +3 -4
- package/dist/integrations/agent/engine-fallback.js +23 -40
- package/dist/integrations/agent/engine-resolution.js +93 -183
- package/dist/integrations/agent/execution.js +507 -0
- package/dist/integrations/agent/model-map.js +28 -156
- package/dist/integrations/agent/request-lowering.js +66 -141
- package/dist/integrations/agent/runner-dispatch.js +143 -321
- package/dist/integrations/agent/runner.js +54 -14
- package/dist/integrations/lockfile.js +53 -101
- package/dist/llm/client.js +8 -10
- package/dist/llm/embedders/deterministic.js +2 -3
- package/dist/llm/embedders/profile.js +71 -0
- package/dist/llm/embedders/remote.js +11 -17
- package/dist/llm/feature-gate.js +0 -8
- package/dist/llm/index-passes.js +3 -5
- package/dist/llm/memory-infer.js +1 -2
- package/dist/llm/structured-call.js +5 -24
- package/dist/output/generic-render.js +23 -11
- package/dist/output/html-render.js +13 -10
- package/dist/output/render-registry.js +3 -32
- package/dist/output/shapes/helpers.js +25 -38
- package/dist/output/shapes/passthrough.js +1 -9
- package/dist/{indexer/graph/graph-types.js → output/text/bundle-rename.js} +4 -1
- package/dist/output/text/command-format.js +69 -31
- package/dist/output/text/helpers.js +1 -1
- package/dist/output/text/migrate.js +5 -14
- package/dist/output/text/proposal-format.js +48 -3
- package/dist/output/text/show-format.js +13 -17
- package/dist/output/text/workflow-format.js +0 -32
- package/dist/output/text.js +2 -0
- package/dist/registry/factory.js +4 -19
- package/dist/registry/network.js +66 -220
- package/dist/registry/providers/index.js +0 -2
- package/dist/registry/providers/skills-sh.js +3 -14
- package/dist/registry/providers/static-index.js +24 -26
- package/dist/registry/resolve.js +55 -131
- package/dist/scripts/akm-migrate-node.js +42938 -92375
- package/dist/scripts/akm-migrate.js +42930 -92365
- package/dist/setup/registry-stash-loader.js +4 -13
- package/dist/setup/semantic-assets.js +3 -44
- package/dist/setup/setup.js +1 -1
- package/dist/setup/steps/connection.js +5 -6
- package/dist/setup/steps/platforms.js +2 -2
- package/dist/setup/steps/tasks.js +25 -15
- package/dist/sources/provider-factory.js +17 -18
- package/dist/sources/providers/filesystem.js +2 -3
- package/dist/sources/providers/git-install.js +7 -1
- package/dist/sources/providers/git-provider.js +0 -3
- package/dist/sources/providers/git-stash.js +83 -21
- package/dist/sources/providers/npm.js +2 -4
- package/dist/sources/providers/provider-utils.js +5 -10
- package/dist/sources/providers/website.js +0 -2
- package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
- package/dist/sources/website-url.js +2 -2
- package/dist/storage/database.js +9 -35
- package/dist/storage/repositories/improve-ledger-repository.js +209 -0
- package/dist/storage/repositories/index-connection.js +39 -72
- package/dist/storage/repositories/index-entries-repository.js +88 -129
- package/dist/storage/repositories/index-entry-mapper.js +1 -2
- package/dist/storage/repositories/index-entry-schema.js +101 -268
- package/dist/storage/repositories/index-fts-repository.js +86 -256
- package/dist/storage/repositories/index-links-repository.js +143 -0
- package/dist/storage/repositories/index-llm-cache-repository.js +7 -9
- package/dist/storage/repositories/index-meta-repository.js +6 -4
- package/dist/storage/repositories/index-schema.js +257 -325
- package/dist/storage/repositories/index-utility-repository.js +8 -29
- package/dist/storage/repositories/index-vec-repository.js +133 -414
- package/dist/storage/repositories/outcome-repository.js +2 -1
- package/dist/storage/repositories/proposals-repository.js +100 -0
- package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
- package/dist/storage/repositories/salience-repository.js +1 -19
- package/dist/storage/repositories/task-history-repository.js +26 -4
- package/dist/storage/repositories/workflow-runs-repository.js +53 -244
- package/dist/storage/sqlite-migrations.js +136 -0
- package/dist/storage/sqlite-pragmas.js +11 -9
- package/dist/storage/sqlite-transaction.js +170 -0
- package/dist/storage/state-db-integrity.js +34 -27
- package/dist/tasks/activation-config.js +134 -62
- package/dist/tasks/backends/cron.js +191 -302
- package/dist/tasks/backends/exec-utils.js +2 -5
- package/dist/tasks/backends/launchd.js +141 -748
- package/dist/tasks/backends/schtasks.js +119 -623
- package/dist/tasks/prepare/prepare-support.js +5 -15
- package/dist/tasks/prepare/prepare.js +0 -2
- package/dist/tasks/resolve-akm-bin.js +20 -79
- package/dist/tasks/run/attempt-lifecycle.js +0 -1
- package/dist/tasks/run/load-task.js +1 -1
- package/dist/tasks/scheduler-binding.js +20 -238
- package/dist/tasks/scheduler-invocation.js +136 -244
- package/dist/tasks/scheduler-lock.js +53 -0
- package/dist/tasks/scheduler-sync.js +368 -679
- package/dist/tasks/source/parse-task-source.js +55 -9
- package/dist/tasks/source/task-source-v3-frozen.js +3 -4
- package/dist/tasks/source/task-to-v4.js +464 -88
- package/dist/workflows/authoring/authoring.js +3 -12
- package/dist/workflows/compile.js +211 -0
- package/dist/workflows/concurrency-policy.js +13 -74
- package/dist/workflows/exec/child-invocation.js +3 -17
- package/dist/workflows/exec/child-workflow.js +32 -141
- package/dist/workflows/exec/dispatch-redaction.js +13 -53
- package/dist/workflows/exec/environment.js +98 -0
- package/dist/workflows/exec/exec-unit.js +33 -140
- package/dist/workflows/exec/frozen-judge.js +7 -59
- package/dist/workflows/exec/native-executor.js +82 -341
- package/dist/workflows/exec/param-secrets.js +29 -47
- package/dist/workflows/exec/run-workflow.js +154 -387
- package/dist/workflows/exec/scheduler.js +9 -36
- package/dist/workflows/exec/step-work.js +127 -430
- package/dist/workflows/exec/unit-dispatch.js +11 -63
- package/dist/workflows/exec/unit-writer.js +8 -52
- package/dist/workflows/exec/worktree.js +39 -273
- package/dist/workflows/freeze/child-output-references.js +4 -15
- package/dist/workflows/freeze/environment.js +99 -92
- package/dist/workflows/freeze/freeze.js +172 -0
- package/dist/workflows/freeze/step-values.js +19 -21
- package/dist/workflows/freeze/targets/child-workflow.js +23 -92
- package/dist/workflows/freeze/targets/command.js +10 -33
- package/dist/workflows/freeze/targets/script.js +5 -12
- package/dist/workflows/freeze/targets/shell.js +3 -6
- package/dist/workflows/freeze/targets/task.js +25 -80
- package/dist/workflows/freeze/task-bindings.js +20 -67
- package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
- package/dist/workflows/ir/params.js +6 -51
- package/dist/workflows/ir/plan-hash.js +2 -34
- package/dist/workflows/parser.js +140 -43
- package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
- package/dist/workflows/renderer.js +36 -69
- package/dist/workflows/resource-limits.js +12 -120
- package/dist/workflows/runtime/agent-identity.js +8 -40
- package/dist/workflows/runtime/run-outputs.js +3 -6
- package/dist/workflows/runtime/run-plan.js +316 -0
- package/dist/workflows/runtime/runs.js +48 -200
- package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
- package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
- package/dist/workflows/validate-summary.js +2 -7
- package/docs/integration/bundling-akm.md +49 -42
- package/docs/migration/README.md +1 -0
- package/docs/migration/release-notes/0.9.17.md +43 -0
- package/docs/migration/v0.9.1-to-v0.9.2.md +23 -7
- package/docs/reference/cli.md +229 -133
- package/docs/reference/configuration.md +71 -57
- package/docs/reference/data-and-telemetry.md +19 -21
- package/docs/reference/tasks.md +105 -39
- package/docs/reference/workflow-schema.md +14 -18
- package/docs/reference/workflows.md +6 -9
- package/package.json +1 -1
- package/schemas/akm-config.json +87 -754
- package/dist/assets/improve-strategies/graph-refresh.json +0 -15
- package/dist/assets/prompts/contradiction-judge.md +0 -33
- package/dist/assets/prompts/graph-extract-system.md +0 -1
- package/dist/assets/prompts/graph-extract-user-prompt.md +0 -35
- package/dist/assets/prompts/metadata-enhance-system.md +0 -1
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +0 -4
- package/dist/commands/health/advisories.js +0 -150
- package/dist/commands/health/metrics.js +0 -329
- package/dist/commands/health/surfaces.js +0 -102
- package/dist/commands/improve/anti-collapse.js +0 -83
- package/dist/commands/improve/collapse-detector.js +0 -432
- package/dist/commands/improve/consolidate/eligibility.js +0 -48
- package/dist/commands/improve/consolidate/merge.js +0 -146
- package/dist/commands/improve/distill/promote-memory.js +0 -329
- package/dist/commands/improve/distill/quality-gate.js +0 -500
- package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
- package/dist/commands/improve/proposal-envelope.js +0 -31
- package/dist/commands/improve/run-context.js +0 -123
- package/dist/commands/improve/shared.js +0 -21
- package/dist/commands/improve/source-identity.js +0 -28
- package/dist/commands/improve/triage.js +0 -96
- package/dist/commands/proposal/drain-policies.js +0 -151
- package/dist/commands/sources/update-transaction.js +0 -220
- package/dist/core/action-contributors.js +0 -28
- package/dist/core/config/config-version-shim.js +0 -101
- package/dist/core/fs-txn.js +0 -405
- package/dist/core/lexical-score.js +0 -25
- package/dist/core/maintenance-barrier.js +0 -167
- package/dist/execution/executable-identity.js +0 -105
- package/dist/execution/guarded-source.js +0 -427
- package/dist/indexer/db/graph-db.js +0 -444
- package/dist/indexer/graph/graph-boost.js +0 -427
- package/dist/indexer/graph/graph-dedup.js +0 -95
- package/dist/indexer/graph/graph-extraction.js +0 -1182
- package/dist/indexer/search/name-match.js +0 -35
- package/dist/indexer/search/ranking-contributors.js +0 -515
- package/dist/indexer/search/ranking-types.js +0 -4
- package/dist/indexer/walk/project-context.js +0 -192
- package/dist/integrations/agent/execution-cascade.js +0 -566
- package/dist/integrations/agent/execution-definitions.js +0 -202
- package/dist/integrations/agent/execution-lowering.js +0 -841
- package/dist/integrations/agent/execution-preparation.js +0 -98
- package/dist/integrations/agent/inline-execution.js +0 -74
- package/dist/llm/graph-extract.js +0 -872
- package/dist/llm/metadata-enhance.js +0 -96
- package/dist/registry/create-provider-registry.js +0 -29
- package/dist/registry/pinned-request-helper.js +0 -247
- package/dist/registry/pinned-transport.js +0 -717
- package/dist/sources/providers/index.js +0 -14
- package/dist/storage/engines/sqlite-migrations.js +0 -271
- package/dist/storage/repositories/canaries-repository.js +0 -107
- package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
- package/dist/storage/repositories/registry-cache.js +0 -113
- package/dist/tasks/scheduler-sync-preview.js +0 -52
- package/dist/tasks/source/task-to-v3.js +0 -507
- package/dist/workflows/freeze/resolve-steps.js +0 -86
- package/dist/workflows/freeze/source-freeze.js +0 -64
- package/dist/workflows/ir/compile.js +0 -321
- package/dist/workflows/ir/environment-v4.js +0 -330
- package/dist/workflows/ir/freeze-v4.js +0 -153
- package/dist/workflows/ir/schema-v4.js +0 -745
- package/dist/workflows/ir/schema.js +0 -354
- package/dist/workflows/program/schema.js +0 -78
- package/dist/workflows/runtime/checkin.js +0 -57
- package/dist/workflows/runtime/plan-classifier.js +0 -196
- package/dist/workflows/runtime/unit-checkin.js +0 -45
- package/dist/workflows/runtime/unit-phases.js +0 -20
- package/dist/workflows/schema.js +0 -4
- package/dist/workflows/source-ir/compile.js +0 -200
- package/dist/workflows/source-ir/program.js +0 -50
- package/dist/workflows/source-ir/result.js +0 -26
- package/dist/workflows/source-ir/schema.js +0 -786
- package/dist/workflows/source-ir/triggers.js +0 -79
- package/dist/workflows/source-ir/uses.js +0 -40
- package/dist/workflows/validator.js +0 -60
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,1629 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.9.17-alpha.10] - 2026-09-29
|
|
10
|
+
|
|
11
|
+
`akm info` now behaves like a help command. It always prints a report and
|
|
12
|
+
exits 0, naming whatever it could not read: a broken config, a missing
|
|
13
|
+
bundle directory, or an index that another akm process holds or that has a
|
|
14
|
+
newer layout. `akm health` again counts every improve run: 11 of the
|
|
15
|
+
owner's last 55 runs, recorded before `plan.processes` existed, had been
|
|
16
|
+
silently dropped from the health report. A hung test shard now fails within
|
|
17
|
+
10 minutes and names itself, instead of stalling CI until the job times out.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **`scripts/test-unit.sh`/`test-integration.sh` shards fail fast and name
|
|
22
|
+
themselves on a hang.** Each process shard now runs under its own process
|
|
23
|
+
group (`exec setsid`) with a 600s ceiling — well above the ~3-minute CI
|
|
24
|
+
norm for a full shard. A shard still alive past it is killed by process
|
|
25
|
+
group (so a child process it spawned dies too, not just `bun test`
|
|
26
|
+
itself), its log tail is printed so the last test file header shows where
|
|
27
|
+
it hung, and the script fails with a clear message. Previously a hung
|
|
28
|
+
shard blocked `wait` forever, so the only thing that ever stopped it was
|
|
29
|
+
the CI job's own timeout — which kills the whole job and keeps no logs,
|
|
30
|
+
as happened during the alpha.9 release.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- **`akm health` silently excluded every improve run recorded before #947
|
|
35
|
+
added `plan.processes`.** `decodeImproveResult` called
|
|
36
|
+
`validateProcessRoutingRows` unconditionally, so a `plan` object with no
|
|
37
|
+
`processes` key — legitimately written by every release before
|
|
38
|
+
2026-09-09T09:03:19Z — failed decode with "plan.processes must be an
|
|
39
|
+
array" and dropped the run from `--window-compare`, `--group-by run`, and
|
|
40
|
+
the HTML/MD reports. On a real owner `state.db`, 11 of 55 improve runs in
|
|
41
|
+
a 30-day window were affected; all 11 decode cleanly now. `plan.processes`
|
|
42
|
+
is validated only when present, the same guard already used for
|
|
43
|
+
`plan.proactive` and the `retrieval` gate (AGENTS.md "Reading persisted
|
|
44
|
+
data"). Also: a row `akm health` cannot decode — corrupt or otherwise —
|
|
45
|
+
now logs a warning naming the row id and the decode error, instead of
|
|
46
|
+
only incrementing `improve.resultRows.skipped.invalid` with no way to
|
|
47
|
+
tell why.
|
|
48
|
+
- **`akm info` now always reports and exits 0, like a help command.**
|
|
49
|
+
Whatever else is running, and whatever state the config and databases
|
|
50
|
+
are in, it prints its report in every format (and with `--quiet`) and
|
|
51
|
+
names what it could not read:
|
|
52
|
+
- an invalid or unreadable `config.json` is reported in a new
|
|
53
|
+
`configError` field. Every other command still refuses at startup
|
|
54
|
+
(exit 78) before any side effect;
|
|
55
|
+
- a missing or unresolvable bundle directory is reported in
|
|
56
|
+
`bundleDirError`, and a fresh install reports the default location;
|
|
57
|
+
- `index.db` is opened read-only with a bound of about 1.5 s. A
|
|
58
|
+
concurrent `akm index` or `akm improve` writer (which under the DELETE
|
|
59
|
+
journal mode could make `info` wait the full 30 s busy timeout), a
|
|
60
|
+
newer layout, or a corrupt index is reported in
|
|
61
|
+
`indexStats.unavailable` instead of as unexplained zeros. An older
|
|
62
|
+
layout is served as-is, never migrated;
|
|
63
|
+
- each path field degrades on its own, with a last-resort report for
|
|
64
|
+
anything unexpected;
|
|
65
|
+
- an unrecognized flag warns instead of exiting 2, as `akm help` does.
|
|
66
|
+
|
|
67
|
+
## [0.9.17-alpha.9] - 2026-09-29
|
|
68
|
+
|
|
69
|
+
`akm improve` now forgets, reversibly and under review. A consolidation pair
|
|
70
|
+
pass compares each new or changed memory, flat knowledge file or lesson with
|
|
71
|
+
its nearest neighbours; where an LLM judge calls a pair duplicate, subsumed or
|
|
72
|
+
superseding, it mints a retire proposal that a person reviews (`akm proposal
|
|
73
|
+
list --generator consolidate-pair`). Accepting one archives the older or
|
|
74
|
+
contained copy, and `akm proposal revert` restores it exactly; an accepted
|
|
75
|
+
promotion now retires its source memory, so promotion no longer leaves a
|
|
76
|
+
duplicate. A continuity check flags a retirement whose survivor does not rank
|
|
77
|
+
where the retired asset did for its own past searches, and a flagged proposal
|
|
78
|
+
is never bulk-accepted. Archived files are purged 30 days after retirement,
|
|
79
|
+
only when git holds them unmodified. The per-run forgetting-safety lane, LLM
|
|
80
|
+
metadata enrichment and LLM entity-graph extraction are removed: each measured
|
|
81
|
+
no benefit. Decide pending retire proposals before downgrading to
|
|
82
|
+
0.9.17-alpha.8.
|
|
83
|
+
|
|
84
|
+
### Added
|
|
85
|
+
|
|
86
|
+
- **Consolidate pair pass: duplicate, subsumed and superseding memories are
|
|
87
|
+
now retired, review-gated.** A second pass inside `akmConsolidate`,
|
|
88
|
+
alongside the existing promote pass. It walks memory-tier assets (a
|
|
89
|
+
memory, base or `.derived`; a flat `knowledge/` asset; or a lesson) in the
|
|
90
|
+
retrieval scope that are new to the pass or whose body has changed since
|
|
91
|
+
their last full attempt — tracked by content hash, not a time window, so
|
|
92
|
+
an initiator the nightly cap or a pending-proposal collision leaves out
|
|
93
|
+
stays eligible rather than being marked settled, and the pass's own
|
|
94
|
+
ledger rows are never retrieval-scope evidence for the other improve
|
|
95
|
+
lanes — takes each one's nearest neighbours by stored vector (fetching 20,
|
|
96
|
+
keeping the first 5 that clear every filter; same bundle, memory tier
|
|
97
|
+
only — structured knowledge in subfolders is excluded), and judges every
|
|
98
|
+
pair at cosine >= `T_pair` (0.93) with one LLM call using the calibrated
|
|
99
|
+
relation prompt (`src/assets/prompts/consolidate-pair.md`, six labels:
|
|
100
|
+
`duplicate`, `subsumed`, `supersedes`, `contradicts`, `overlap`,
|
|
101
|
+
`unrelated`). An initiator with no prior attempt is held to a higher
|
|
102
|
+
`T_pair` >= 0.95, unless it is new material (git first-added within the
|
|
103
|
+
last 7 days), which judges at the ordinary 0.93. "Older"/"newer" for the
|
|
104
|
+
judge's own A/B labelling comes from one `git log` per run over the
|
|
105
|
+
bundle (first-add time, following renames so a moved or renamed file
|
|
106
|
+
keeps its original date), not frontmatter or file mtime — mtime is only
|
|
107
|
+
the fallback for a file git does not know, or a bundle with no `.git` at
|
|
108
|
+
all. At most 300 pairs are judged a night, admitted a whole initiator at
|
|
109
|
+
a time rather than by flat cosine rank: new-or-changed initiators first,
|
|
110
|
+
then the existing backlog by its own best cosine, each admitted only if
|
|
111
|
+
every one of its candidate pairs fits in what remains of the 300 — so an
|
|
112
|
+
initiator blocked on another pending decision never spends a slot doing
|
|
113
|
+
nothing, and a smaller initiator further down still fits when a larger
|
|
114
|
+
one ahead of it does not. `duplicate`, `subsumed` and `supersedes` mint a
|
|
115
|
+
reviewed `retire` proposal for the losing side (owner-calibrated
|
|
116
|
+
precision 20/22 = 0.91 [0.72, 0.97] against a second-rater baseline of
|
|
117
|
+
0.17 for `supersedes` alone); `contradicts` is counted but stays a human
|
|
118
|
+
decision, and `overlap`/`unrelated` get no proposal. Guards: never a
|
|
119
|
+
`captureMode: hot` memory, never a `.derived` memory whose parent still
|
|
120
|
+
exists, never a pair where either side already has a pending retire
|
|
121
|
+
proposal (as the retired ref or its successor), and never retiring or
|
|
122
|
+
reusing as a successor an asset already spent earlier in the same run.
|
|
123
|
+
(`src/commands/improve/consolidate/pair-pass.ts`,
|
|
124
|
+
`src/commands/improve/retrieval-scope.ts`,
|
|
125
|
+
`src/storage/repositories/improve-ledger-repository.ts`,
|
|
126
|
+
`src/assets/prompts/consolidate-pair.md`)
|
|
127
|
+
- **Retire proposals.** A pair-pass retire proposal mints under its own
|
|
128
|
+
source, `consolidate-pair` — kept apart from the promote pass's
|
|
129
|
+
`consolidate` proposals, so a bulk `accept`/`reject --generator
|
|
130
|
+
consolidate` never sweeps a retirement, and the reverse; a bare `akm
|
|
131
|
+
proposal accept <ref>` never resolves to one either (it matches the
|
|
132
|
+
newest non-retire proposal for the ref, if any — a retire is reached by
|
|
133
|
+
its own proposal id, or the bulk `--generator consolidate-pair` form),
|
|
134
|
+
and retention expiry never drops a pending one for age alone. A pair the owner
|
|
135
|
+
rejected or reverted is not proposed again while both sides are unchanged. Its primary
|
|
136
|
+
change deletes its target instead of writing content. Accepting one first
|
|
137
|
+
confirms the decision is still fresh — the successor still exists, and
|
|
138
|
+
both sides' recorded body hashes still match their current files, not
|
|
139
|
+
just the retired side's, so a decision a later accept elsewhere made
|
|
140
|
+
stale (an A->B/B->C chain, or A->B/B->A both minted) is refused cleanly
|
|
141
|
+
rather than partially applied — then archives the asset (and its
|
|
142
|
+
`.derived` twin, if one exists) through a generalized
|
|
143
|
+
`archiveCleanupCandidate` (now usable on any memory, knowledge or lesson
|
|
144
|
+
file, not only `.derived` memories): the same
|
|
145
|
+
`.akm/memory-cleanup/archive/` encoding memory cleanup already used,
|
|
146
|
+
never the dead `.akm/archive/`. A `supersedes` judgement first writes the
|
|
147
|
+
`supersededBy` edge on the older asset, then archives it. Triage never
|
|
148
|
+
auto-accepts a retire proposal, whatever `applyMode` says — it waits for
|
|
149
|
+
a direct `akm proposal accept`, reviewed the same way as any other
|
|
150
|
+
proposal (`akm proposal list`, `show`, `diff`, bulk `accept --generator
|
|
151
|
+
consolidate-pair`; `--max-diff-lines` counts a retire by its target's own
|
|
152
|
+
line count). `accept` is crash-safe: it records its full intent —
|
|
153
|
+
`backupContent` and which file is about to move — durably before moving
|
|
154
|
+
anything, so a crash partway through, including between a primary and its
|
|
155
|
+
`.derived` twin, resumes and finishes from what was recorded rather than
|
|
156
|
+
leaving an asset stranded or the decision unrecorded. `revert` needs no
|
|
157
|
+
intent of its own — it resumes from what `accept` already recorded, and
|
|
158
|
+
refuses instead of overwriting a path that was reused by an unrelated file
|
|
159
|
+
since (its current content no longer matching what was retired). `revert`
|
|
160
|
+
restores the archived file(s) byte-for-byte from the bytes recorded at
|
|
161
|
+
accept, even a file that had no trailing newline — YAML comments, key
|
|
162
|
+
order and any human-written edge all survive the round trip. A ref to a retired asset keeps resolving to its tombstone
|
|
163
|
+
(`isArchivedRelPath`) — fixed along the way: that resolver assumed only
|
|
164
|
+
memories are ever archived, so an xref to a retired knowledge or lesson
|
|
165
|
+
asset was wrongly reported `missing-ref` by `akm lint` until now. Pending
|
|
166
|
+
retire proposals must be accepted or rejected before downgrading to
|
|
167
|
+
0.9.17-alpha.8 or earlier — that release predates the retire shape
|
|
168
|
+
entirely and exits 70 on one in `show`/`diff`/`drain`, and drain's own
|
|
169
|
+
nightly pre-pass failing on the first one it meets stops that run's
|
|
170
|
+
auto-promotion for the whole stash. Downgrading also revives the
|
|
171
|
+
new-material starvation this same branch fixed forward-only: 0.9.17-alpha.8
|
|
172
|
+
counts a pair-pass ledger row as retrieval-scope evidence again, so its own
|
|
173
|
+
nightly attempts crowd new material back out of every other improve lane —
|
|
174
|
+
measured, two nights left only 341 of 2,183 new-only assets still in scope
|
|
175
|
+
once read under alpha.8
|
|
176
|
+
(`docs/architecture/persisted-data-compat.md`).
|
|
177
|
+
(`src/commands/proposal/repository.ts`,
|
|
178
|
+
`src/commands/improve/memory/memory-improve.ts`,
|
|
179
|
+
`src/commands/lint/base-linter.ts`)
|
|
180
|
+
- **A promotion retires its source memory (O1).** When `akm proposal accept`
|
|
181
|
+
promotes a consolidate `promote` proposal — by a person or by triage
|
|
182
|
+
auto-promotion — it now archives the source memory (and its `.derived`
|
|
183
|
+
twin) through the same retire-archive path, tombstoned `reason: promoted`,
|
|
184
|
+
provided the source's body still matches the hash recorded when the
|
|
185
|
+
promotion was minted; an edit since then leaves the source alone (a
|
|
186
|
+
proposal minted before this hash existed is never archived, for the same
|
|
187
|
+
reason). A promotion no longer leaves a memory/knowledge duplicate behind.
|
|
188
|
+
Best-effort: a failure to archive the source only warns; the promotion
|
|
189
|
+
itself is not undone. (`src/commands/improve/consolidate.ts`,
|
|
190
|
+
`src/commands/proposal/repository.ts`)
|
|
191
|
+
- **Retirement continuity check (rule R3).** Before the pair pass mints a
|
|
192
|
+
`retire` proposal, it replays up to five of the retired asset's own past
|
|
193
|
+
`search`/`curate` queries through akm's own search, in-process — the
|
|
194
|
+
ranking a user actually gets, no LLM. For every query where the retired
|
|
195
|
+
asset ranked in the top 10, the successor must too; compared directly,
|
|
196
|
+
since search itself returns at most the top 10 hits. A failing
|
|
197
|
+
query never blocks the mint — the
|
|
198
|
+
proposal's `retirement.continuityRisk` records the failing query count
|
|
199
|
+
and, per failing query, the retired asset's rank and the successor's
|
|
200
|
+
(`null` when the successor did not rank in the top 10 at all). A proposal
|
|
201
|
+
carrying `continuityRisk` is excluded from every bulk accept path (`accept
|
|
202
|
+
--generator …`, with or without `--yes`) but not from bulk reject —
|
|
203
|
+
declining a flagged proposal is always the safe direction; a person can
|
|
204
|
+
always accept one by id. An asset with no recorded queries is not checked
|
|
205
|
+
at all. A query that never ran (the search call threw) or that fell back
|
|
206
|
+
to keyword-only ranking (an unreachable embedding endpoint, most often)
|
|
207
|
+
is never silently trusted or silently dropped either: it counts as
|
|
208
|
+
"unverified" and, on its own, is enough to flag `continuityRisk` — an
|
|
209
|
+
endpoint outage reads as "risk unknown," never as "no risk found," for
|
|
210
|
+
every proposal checked while it stays down, not just the first. The
|
|
211
|
+
first fallback in a pair-pass run forces every later query in that same
|
|
212
|
+
run to skip the semantic attempt entirely, so a dead endpoint costs one
|
|
213
|
+
failed attempt total, not one per remaining query. Two fixes against false
|
|
214
|
+
flags, measured on a real night-1 admission (300 pairs, 6 flags, 3
|
|
215
|
+
spurious): replayed queries are the same cleaned set the retrieval
|
|
216
|
+
regression gate uses (`loadRetrievalQueries`) — stash-README boilerplate,
|
|
217
|
+
harness/tool envelopes, pastes over 2,000 characters, and near-duplicate
|
|
218
|
+
queries (equal once whitespace is collapsed) are dropped before replay,
|
|
219
|
+
not just capped at five raw entries; and the check does not run at all
|
|
220
|
+
when the retired and successor bodies are content-identical once
|
|
221
|
+
whitespace is collapsed — search's own content-dedupe already hides the
|
|
222
|
+
successor behind the retired asset for every such query, so a "successor
|
|
223
|
+
missing" finding would not be a real risk.
|
|
224
|
+
(`src/commands/improve/consolidate/continuity-check.ts`,
|
|
225
|
+
`src/commands/proposal/proposal-types.ts`,
|
|
226
|
+
`src/commands/proposal/proposal.ts`)
|
|
227
|
+
- **Continuity-risk visibility, and a `--generator` filter for `proposal
|
|
228
|
+
list`.** A retire proposal carrying `retirement.continuityRisk` now
|
|
229
|
+
shows `⚠ continuity-risk` inline in the default `akm proposal list`
|
|
230
|
+
output (and `--format text`), not just in `proposal show`. `proposal
|
|
231
|
+
show`'s text output now lists the actual failing query text and rank per
|
|
232
|
+
query, not just a count, and separately reports `unverifiedQueries` when
|
|
233
|
+
the risk is (also, or only) an unverified query rather than a rank
|
|
234
|
+
failure. `akm proposal accept --generator … --dry-run` (and a real bulk
|
|
235
|
+
run) now reports `skippedForContinuityRisk`, the count of otherwise
|
|
236
|
+
matching proposals excluded specifically for this reason, apart from an
|
|
237
|
+
ordinary `--max-diff-lines`/`--older-than` miss. `akm proposal list` gains
|
|
238
|
+
a `--generator <name>` filter, the same value `accept`/`reject
|
|
239
|
+
--generator` already take, so the (potentially large) backlog of one
|
|
240
|
+
generator's retire proposals can be reviewed as its own list.
|
|
241
|
+
(`src/commands/proposal/proposal.ts`, `src/commands/proposal/proposal-cli.ts`,
|
|
242
|
+
`src/output/text/proposal-format.ts`, `src/output/shapes/helpers.ts`)
|
|
243
|
+
- **Archive purge sweep.** Deterministic, no LLM, run once at the very start
|
|
244
|
+
of every `akm improve` invocation, ahead of index bootstrap and triage.
|
|
245
|
+
For a git-backed bundle, deletes the archived asset file(s) of a
|
|
246
|
+
retirement — never its `cleanup.md` tombstone — once `retiredAt` is more
|
|
247
|
+
than 30 days old (`RETIRE_GRACE_DAYS`) AND every file under that
|
|
248
|
+
retirement's archive directory is git-tracked, clean (`git ls-files` plus
|
|
249
|
+
`git status --porcelain -uall`), and verifiable (`git ls-files -v`: a
|
|
250
|
+
file marked `assume-unchanged` or `skip-worktree` hides its own edits
|
|
251
|
+
from `git status`, so it is never trusted as clean either) — each checked
|
|
252
|
+
once per sweep; git history keeps the bytes. `.git` presence alone is not
|
|
253
|
+
enough: `proposal accept` only commits for a `kind: "git"` write target,
|
|
254
|
+
and improve's own auto-sync stages only the paths its own run wrote, so a
|
|
255
|
+
filesystem-kind bundle can carry archived retirements that were never
|
|
256
|
+
committed — the tracked/clean/verifiable check is what keeps the sweep
|
|
257
|
+
from deleting the only surviving copy of those. A directory with even one
|
|
258
|
+
untracked, modified, or unverifiable file (tombstone included) is left
|
|
259
|
+
whole for a later sweep — and so is the ENTIRE archive for that sweep if
|
|
260
|
+
the underlying `git status` or `git ls-files` call itself fails (a broken
|
|
261
|
+
submodule, for instance, can fail `git status` while `git ls-files`
|
|
262
|
+
still succeeds): an empty result from a failed check is never treated as
|
|
263
|
+
"nothing to protect", and the sweep warns once rather than silently
|
|
264
|
+
purging nothing. A memory-cleanup family-prune archive carries no
|
|
265
|
+
`retiredAt`, so this sweep never touches that older archive class. Every
|
|
266
|
+
deleted file is journaled individually, so the end-of-run auto-sync
|
|
267
|
+
commits the removal the same way it commits the archive move itself.
|
|
268
|
+
(`src/commands/improve/memory/memory-improve.ts`,
|
|
269
|
+
`src/sources/providers/git-stash.ts`, `src/commands/improve/improve.ts`)
|
|
270
|
+
- **`akm health`'s `memory-cleanup-archive` advisory now covers every
|
|
271
|
+
bundle**, not just one with no `.git` at all. A bundle with no `.git` of
|
|
272
|
+
its own keeps every retirement's archived bytes forever (there is no
|
|
273
|
+
history to fall back on, so the purge sweep never runs there), and its
|
|
274
|
+
size and file count are reported as before. A git-backed bundle can ALSO
|
|
275
|
+
carry archived bytes the purge sweep will never remove — `.git` presence
|
|
276
|
+
alone never proved a retirement was committed — so this now runs the same
|
|
277
|
+
tracked/clean/verifiable check the purge sweep itself uses and reports
|
|
278
|
+
how many files and bytes of the archive cannot currently be purged
|
|
279
|
+
(untracked, modified, or unverifiable), alongside the total. Silent
|
|
280
|
+
whenever there is nothing to say: the archive is empty or absent, or (for
|
|
281
|
+
a git-backed bundle) every byte in it is purgeable once it ages out.
|
|
282
|
+
(`src/commands/health/archive-usage.ts`, `src/commands/health/data-dir-usage.ts`)
|
|
283
|
+
|
|
284
|
+
### Removed
|
|
285
|
+
|
|
286
|
+
- **LLM metadata enrichment (`index.metadataEnhance`) is retired.** On 49
|
|
287
|
+
stratified queries, with every eligible candidate enriched (1,968
|
|
288
|
+
entries): search nDCG@10 moved −0.0092 [−0.0324, +0.0165], curate P@5
|
|
289
|
+
+0.000 [−0.037, +0.045], and long prompts lost −0.054 [−0.093, −0.012]. A
|
|
290
|
+
Doc2Query-- filter made it worse (P@5 −0.020 [−0.045, −0.004]). The pass
|
|
291
|
+
replaced authored descriptions on 89% of the entries it rewrote, and a
|
|
292
|
+
full pass costs about 27 B70-hours (RS-D, owner ruling 2026-09-28). It was
|
|
293
|
+
already off by default and off in the maintainer's config. The LLM call
|
|
294
|
+
(`src/llm/metadata-enhance.ts`), its `akm index` dispatch, and the
|
|
295
|
+
`metadata_enhance` feature-gate key are gone; the deterministic metadata
|
|
296
|
+
pass, `quality: "generated"`, and memory inference are unaffected. A
|
|
297
|
+
config that still sets `index.metadataEnhance` loads, named once by the
|
|
298
|
+
same unknown-config-key path every other retired key uses: kept in
|
|
299
|
+
memory, round-trips through ordinary writes, and is dropped only by
|
|
300
|
+
`akm migrate apply`. The pass's `llm_enrichment_cache` rows (the default
|
|
301
|
+
`cache_variant`; graph and memory inference use their own named variants)
|
|
302
|
+
are deleted on the next writable open of `index.db`. An index built while
|
|
303
|
+
enrichment was on keeps its entries' LLM-written descriptions on
|
|
304
|
+
incremental runs — nothing rewrites an unchanged row; run
|
|
305
|
+
`akm index --full` once to replace them with the deterministic ones.
|
|
306
|
+
(`src/indexer/indexer.ts`, `src/llm/feature-gate.ts`,
|
|
307
|
+
`src/core/config/config.ts`, `src/core/config/schema/index-config.ts`,
|
|
308
|
+
`src/storage/repositories/index-schema.ts`)
|
|
309
|
+
- **The LLM entity-graph extraction pass.** `akm improve`'s per-file
|
|
310
|
+
entity/relation extraction, its persisted tables (`graph_meta`,
|
|
311
|
+
`graph_files`, `graph_file_entities`, `graph_file_relations`), and `akm
|
|
312
|
+
show`'s `related` list are gone. On the navigation eval, vector kNN beat
|
|
313
|
+
the LLM `related` list by 0.157 P@5 [0.051, 0.260]; the ranking boost it
|
|
314
|
+
once fed was already removed in 0.9.17-alpha.4. Declared links (#935,
|
|
315
|
+
alpha.8) are the only navigation surface `akm show` has now, and curate's
|
|
316
|
+
support refs already came from them, not the graph. index.db is a
|
|
317
|
+
regenerable cache, so the graph tables are dropped unconditionally on the
|
|
318
|
+
next writable open — nothing migrates or backs them up.
|
|
319
|
+
- **The `graph-refresh` improve strategy** and the `akm-graph-refresh-weekly`
|
|
320
|
+
task template are deleted. Naming `graph-refresh` via `--strategy` or a task
|
|
321
|
+
now fails with a message naming the retirement, unconditionally — even when
|
|
322
|
+
`improve.strategies["graph-refresh"]` still has a leftover override block
|
|
323
|
+
from customizing the built-in (the message names it; `akm migrate apply`
|
|
324
|
+
drops it). `defaults.improveStrategy: "graph-refresh"` still loads config
|
|
325
|
+
successfully — the refusal happens lazily, when the strategy is actually
|
|
326
|
+
resolved, not at every command's config load.
|
|
327
|
+
- **Retired config keys:** `index.graph.*` and every strategy's
|
|
328
|
+
`processes.graphExtraction.*`. An old config that still sets them keeps
|
|
329
|
+
loading and the keys are unread, but `index.graph` is now also named once
|
|
330
|
+
by the unknown-config-key warning (it previously validated silently
|
|
331
|
+
against the generic per-pass catchall) and, like any other retired key,
|
|
332
|
+
is dropped only by `akm migrate apply` — not by an ordinary config write.
|
|
333
|
+
- **`akm health` drops every graph metric** — the KPI card, summary-table
|
|
334
|
+
rows, per-run duration/entity/relation columns, and the
|
|
335
|
+
`improve.graphExtraction.failures` window-compare delta. `--window-compare`
|
|
336
|
+
and `--group-by run` still count every run, including a pre-alpha.9 one:
|
|
337
|
+
its stored `graphExtraction`/`graphExtractionDurationMs` result fields and
|
|
338
|
+
its `graph-extraction` `plan.stages` / `graphExtraction` `plan.processes`
|
|
339
|
+
entries still decode, read-only, same as any other retired field (AGENTS.md
|
|
340
|
+
"Reading persisted data") — they are just no longer rendered. The
|
|
341
|
+
`improve_completed` event's `graphExtractionExtractedFiles`,
|
|
342
|
+
`graphExtractionDurationMs`, `graphCoverage`, `graphDensity`, and
|
|
343
|
+
`graphEntities` metadata fields are no longer emitted.
|
|
344
|
+
- **The per-run forgetting-safety lane.** `scoreSalience`'s stash-wide
|
|
345
|
+
salience-rank comparison, `applyForgettingSafety`, and the
|
|
346
|
+
`improve_salience_rank_change` event are gone. It was a one-time WS-1
|
|
347
|
+
cutover guard from the June 2026 ranking-formula change that had kept
|
|
348
|
+
running on every improve run since; the last 30 days of events
|
|
349
|
+
(2026-08-30 to 2026-09-29: 47 `improve_salience_rank_change` events, 5
|
|
350
|
+
refs flagged across 4 runs — 09-05, 09-08 x2, 09-19, 09-28) showed no
|
|
351
|
+
marginal pick over the signal-delta lane and the retrieval scope: 4 of
|
|
352
|
+
the 5 flagged refs were also picked that same run by signal-delta
|
|
353
|
+
(adjacent event ids/timestamps, 2–26 minutes after the rank-change
|
|
354
|
+
event), and the 5th (`workflows/create-github-issues-from-spec`, flagged
|
|
355
|
+
09-05) has no `reflect_invoked` or `distill_invoked` event in the
|
|
356
|
+
retained history, but that run's `improve_runs.plannedRefs` shows it,
|
|
357
|
+
too, was planned under `signal-delta` — just not reflected (a
|
|
358
|
+
dispatch/budget limit that run, not a lane-exclusive pick). All 5
|
|
359
|
+
flagged refs were signal-delta picks; zero were forgetting-safety-only.
|
|
360
|
+
It also protected
|
|
361
|
+
`asset_salience.rank_score`, which only improve itself ever read — a rank
|
|
362
|
+
drop could not hide anything from search. `buildRankChangeReport` (its
|
|
363
|
+
comparator) is also gone: the new retirement continuity check (see
|
|
364
|
+
Added) compares ranks directly instead, and nothing else called it.
|
|
365
|
+
`forgetting-safety` stays a valid `eligibilitySource`/event-type
|
|
366
|
+
value so old proposals and events still decode, but nothing assigns or
|
|
367
|
+
emits it any more. (`src/commands/improve/preparation.ts`,
|
|
368
|
+
`src/commands/improve/salience.ts`, `src/core/events.ts`,
|
|
369
|
+
`src/storage/repositories/salience-repository.ts`)
|
|
370
|
+
- **`improve.strategies.<name>.processes.consolidate.incrementalSince` and
|
|
371
|
+
`.neighborsPerChanged`.** The consolidate pair pass is now the candidate
|
|
372
|
+
generator, narrowing per initiator through the improve ledger rather than
|
|
373
|
+
a global time window; neither key was set anywhere in the owner's config.
|
|
374
|
+
`narrowToIncrementalCandidates` goes with them, along with its
|
|
375
|
+
now-orphaned `parseSinceToIsoLenient` helper. A config that still sets
|
|
376
|
+
either key keeps loading under the retired-key contract: named once as
|
|
377
|
+
unknown, it survives an ordinary config write, and only `akm migrate
|
|
378
|
+
apply` drops it. (`src/core/config/schema/improve-processes.ts`,
|
|
379
|
+
`src/commands/improve/consolidate.ts`, `src/core/time.ts`,
|
|
380
|
+
`docs/reference/configuration.md`)
|
|
381
|
+
|
|
382
|
+
### Fixed
|
|
383
|
+
|
|
384
|
+
- **Stale "advisory merge/delete/contradict" documentation.** Consolidation
|
|
385
|
+
stopped executing its merge/delete/contradict operations at `e82eec811`
|
|
386
|
+
(2026-07, #732; they had run in production until then, not "never
|
|
387
|
+
executed" as a couple of doc comments and `improve-workflow.md` claimed),
|
|
388
|
+
and 0.9.17-alpha.1 (`f4ebd763a`) dropped them from the prompt and schema,
|
|
389
|
+
which have offered `promote` only since. But `docs/architecture/
|
|
390
|
+
improvement.md`, `STABILITY.md` and `docs/architecture/internals/
|
|
391
|
+
improve-workflow.md` still described them as advisory planned output.
|
|
392
|
+
Corrected, and `improve-workflow.md` gains a section documenting the pair
|
|
393
|
+
pass, retire proposals and O1. Also corrected: the `default` strategy's
|
|
394
|
+
"advisory consolidation" description, two comments that still credited a
|
|
395
|
+
`beliefState` ranking boost alpha.4 removed (`memory-belief.ts`,
|
|
396
|
+
`knowledge.ts`), and D27's stale `archiveMemory` naming in the
|
|
397
|
+
architecture decision history. Deleted the unused
|
|
398
|
+
`src/assets/prompts/contradiction-judge.md` (no reader since
|
|
399
|
+
`e82eec811`).
|
|
400
|
+
|
|
401
|
+
## [0.9.17-alpha.8] - 2026-09-28
|
|
402
|
+
|
|
403
|
+
`akm index` now records the links a bundle already declares (`xrefs`,
|
|
404
|
+
`supersededBy`, a `.derived` memory's parent, wiki sources, page links, and
|
|
405
|
+
workflow and task targets) as typed links, with no model. `akm show` lists
|
|
406
|
+
them, and curate's support refs come from them instead of the LLM entity
|
|
407
|
+
graph. The index moves to layout 26 in place on its first writable open;
|
|
408
|
+
0.9.17-alpha.4 through alpha.7 cannot open it. `index.graph.enabled: false`
|
|
409
|
+
now stops graph extraction in `akm improve`, and a partly failed extraction is
|
|
410
|
+
retried. `akm migrate` converts a v2 or v3 task file to v4 in one pass, and
|
|
411
|
+
the launchers no longer lose a signal that arrives before their child starts.
|
|
412
|
+
|
|
413
|
+
### Added
|
|
414
|
+
|
|
415
|
+
- **Declared links (#935).** The relations a bundle already declares are now
|
|
416
|
+
stored as typed links: `xrefs:` (`xref`), `supersededBy:`
|
|
417
|
+
(`superseded_by`), `contradictedBy:` (`contradicted_by`),
|
|
418
|
+
`currentBeliefRefs:` (`belief_peer`), a `.derived` memory's parent
|
|
419
|
+
(`derived_from`), wiki `sources:` that name an asset (`cites`), the page
|
|
420
|
+
links an llm-wiki or OKF bundle resolves (`links_to`), and a workflow step's
|
|
421
|
+
or a task's target (`uses`). `akm index` reads them from what it already
|
|
422
|
+
parses, with no model, so an install without an LLM engine gets them. They
|
|
423
|
+
live in their own table (`asset_links`), apart from the LLM entity graph:
|
|
424
|
+
each link belongs to the entry that declares it and is written, replaced
|
|
425
|
+
and deleted with it, including on incremental and write-path (`akm
|
|
426
|
+
remember`) indexing. A target resolves when it is indexed, not when its
|
|
427
|
+
citer was, so a note that cites something added later links to it without
|
|
428
|
+
being reindexed. Retired spellings convert in memory (`memory:<name>`,
|
|
429
|
+
`wiki:<wiki>/<page>`, a `.md` suffix), and, as lint already allows (#882),
|
|
430
|
+
a memory whose own file is gone resolves to its `.derived` child.
|
|
431
|
+
`akm show` lists an asset's links grouped by kind: `outgoing`, `incoming`
|
|
432
|
+
and the `unresolved` tokens it names, at most 10 per kind with a `total`.
|
|
433
|
+
`akm info` reports links per kind with how many are unresolved. Links do
|
|
434
|
+
not change search ranking, and `related` is unchanged: on the retrieval
|
|
435
|
+
suite, against 0.9.17-alpha.7 on the same index with two runs per arm,
|
|
436
|
+
search nDCG@10 moved +0.002 [−0.005, +0.009] (a rerun of alpha.7 alone
|
|
437
|
+
moved +0.006) and curate P@5 +0.000 [−0.001, +0.001].
|
|
438
|
+
On the retrieval snapshot of the maintainer's 21 bundles (23,979 entries)
|
|
439
|
+
there are 14,917 links: 7,393 `contradicted_by`, 4,401 `xref`, 2,577
|
|
440
|
+
`derived_from`, 540 `cites` and 6 `superseded_by`. 1,801 are unresolved;
|
|
441
|
+
1,744 of those are `.derived` memories whose parent memory no longer
|
|
442
|
+
exists. (`src/indexer/links/declared-links.ts`,
|
|
443
|
+
`src/storage/repositories/index-links-repository.ts`,
|
|
444
|
+
`src/commands/read/show.ts`, `src/commands/sources/info.ts`)
|
|
445
|
+
|
|
446
|
+
### Changed
|
|
447
|
+
|
|
448
|
+
- **`akm migrate` converts a v2 or v3 task file straight to v4 in one pass.**
|
|
449
|
+
The chain that read every file as v2, converted it to an intermediate v3
|
|
450
|
+
shape, then converted that to v4 (`src/tasks/source/task-to-v3.ts` ->
|
|
451
|
+
`task-to-v4.ts`, composed by `scripts/akm-migrate/migrate/task-files.ts`)
|
|
452
|
+
is now one planner: `task-to-v4.ts` reads a file once and, for v2, builds
|
|
453
|
+
the v3-shape record in memory — never written to disk or reported as its
|
|
454
|
+
own outcome — before hoisting it to v4 through the same code path a real
|
|
455
|
+
v3 file goes through. `akm migrate status`/`apply` output shapes, and
|
|
456
|
+
every blocked/changed reason code, are unchanged, checked fixture by
|
|
457
|
+
fixture against the prior two-hop chain's actual output (one exception:
|
|
458
|
+
a v3 document that fails only the typed pre-check's own "exactly one
|
|
459
|
+
scheduling source" rule — unreachable through the real chain, which
|
|
460
|
+
always ran that same pre-check first — now reports `invalid-v3-task`
|
|
461
|
+
instead of the raw hoist stage's own `ambiguous-scheduling-source`,
|
|
462
|
+
matching what `akm migrate apply` already returned end to end). The
|
|
463
|
+
`already-v3` and `pending-v2-to-v3-migration` intermediate states are
|
|
464
|
+
gone with the generation split that produced them.
|
|
465
|
+
`src/tasks/source/task-to-v3.ts` (500 lines) is deleted; its logic moved
|
|
466
|
+
into `task-to-v4.ts`, which also drops the duplicate raw-YAML reader and
|
|
467
|
+
outcome-base helpers the two files each carried their own copy of.
|
|
468
|
+
(`src/tasks/source/task-to-v4.ts`, `scripts/akm-migrate/migrate/task-files.ts`)
|
|
469
|
+
- **Curate's support refs come from declared links.** Each curated item's
|
|
470
|
+
support refs (at most two) are now assets its declared links name: what
|
|
471
|
+
it links to, then what links to it, in the order `akm show` lists them,
|
|
472
|
+
skipping assets curate already selected. They no longer come from the LLM
|
|
473
|
+
entity graph's `related` list. On the retrieval snapshot, `related` offered
|
|
474
|
+
support refs for 47 of 867 curate items (5.4%) and declared links for 257
|
|
475
|
+
(29.6%). The retrieval judge graded 157 of those items, asking whether each
|
|
476
|
+
support ref is worth opening next: 56% of declared support refs were useful
|
|
477
|
+
against 68% of `related`'s, so curate attaches about 24 useful support refs
|
|
478
|
+
per 100 items instead of 7. Curate's items are unchanged.
|
|
479
|
+
(`src/commands/read/curate.ts`)
|
|
480
|
+
- **Index layout 26.** The first writable open of an older index derives
|
|
481
|
+
every entry's links from its stored `document_json` in place, reading no
|
|
482
|
+
file: on the 23,979-entry snapshot index that takes 0.8 s and adds 3.5 MB.
|
|
483
|
+
Earlier layouts never stored a workflow's or a task's targets, so only the
|
|
484
|
+
directories holding workflows and tasks re-read on the next `akm index`.
|
|
485
|
+
The open leaves `index_meta.vacuumPending` like every layout migration.
|
|
486
|
+
0.9.17-alpha.4 through alpha.7 refuse an index at layout 26
|
|
487
|
+
(`INDEX_SCHEMA_INCOMPATIBLE`, naming the upgrade), for writing as well as
|
|
488
|
+
reading, so going back to one of them needs a new index: move `index.db`
|
|
489
|
+
aside and run `akm index` under that release (the LLM graph and enrichment
|
|
490
|
+
cache it held are not rebuilt by `akm index`).
|
|
491
|
+
(`src/storage/repositories/index-schema.ts`,
|
|
492
|
+
`src/storage/repositories/index-entry-schema.ts`)
|
|
493
|
+
|
|
494
|
+
### Fixed
|
|
495
|
+
|
|
496
|
+
- **`index.graph.enabled: false` stops graph extraction in `akm improve`.**
|
|
497
|
+
The switch was read only when improve had no strategy plan, which is never
|
|
498
|
+
the case in a real run, so the nightly and weekly graph tasks kept
|
|
499
|
+
extracting with it set. Improve now skips its graph extraction stage
|
|
500
|
+
whenever `index.graph.enabled` is `false`, whatever the strategy enables.
|
|
501
|
+
This also makes the graph ablation harness's "graph off" arm, which sets
|
|
502
|
+
exactly this key, turn extraction off. Improve still does not read
|
|
503
|
+
`index.defaults` when it picks the engine for graph extraction.
|
|
504
|
+
(`src/commands/improve/loop-stages.ts`)
|
|
505
|
+
- **Graph extraction never has more calls in flight than the run's
|
|
506
|
+
concurrency.** Batches run side by side up to the runner's concurrency, and
|
|
507
|
+
each batch also sent its per-file calls (long bodies, a non-array response)
|
|
508
|
+
up to that limit at once, so at a concurrency of 2 four calls could be in
|
|
509
|
+
flight. A batch now makes its per-file calls one at a time. At the default
|
|
510
|
+
concurrency of 1 nothing changes. (`src/llm/graph-extract.ts`)
|
|
511
|
+
- **A long document whose extraction partly failed is extracted again.** A
|
|
512
|
+
body over 1,600 characters is extracted in chunks. When some chunks failed
|
|
513
|
+
(a timeout, an error, an empty response) and others found entities, the
|
|
514
|
+
file was recorded and cached as extracted, so the failed chunks were never
|
|
515
|
+
retried. Such a file is now recorded as failed and not cached, and the next
|
|
516
|
+
run extracts it again, every chunk: partial failures are rare outside
|
|
517
|
+
provider outages, and keeping per-chunk results to skip the chunks that
|
|
518
|
+
succeeded would need a second cache. Until then the entities the other
|
|
519
|
+
chunks found are stored when the file had no graph rows yet; a file with
|
|
520
|
+
rows keeps them. (`src/llm/graph-extract.ts`)
|
|
521
|
+
- **`scripts/node-runtime/akm` could die from a raw signal instead of
|
|
522
|
+
forwarding it to its child.** The launcher registered its
|
|
523
|
+
SIGTERM/SIGINT/SIGHUP forwarding listeners only after spawning the child;
|
|
524
|
+
under load, a signal could arrive in that window and fall through to the
|
|
525
|
+
runtime's default (process-terminating) disposition, killing the launcher
|
|
526
|
+
before the child ever saw it. The listeners now go up before anything else
|
|
527
|
+
runs, with a small queue for a signal that arrives before the child exists.
|
|
528
|
+
- **`scripts/node-runtime/akm-migrate` carried the same pre-spawn signal
|
|
529
|
+
race** as `scripts/node-runtime/akm` above, for the same reason (listeners
|
|
530
|
+
registered only after `spawn()`), with no test covering it. Fixed the same
|
|
531
|
+
way, and added `tests/integration/akm-migrate-signal-forwarding.test.ts`
|
|
532
|
+
(modelled on `launcher-signal-forwarding.test.ts`) for its forwarding.
|
|
533
|
+
- **The launcher signal tests failed intermittently because of their own
|
|
534
|
+
fixture.** The fake child wrote its ready file before it registered its
|
|
535
|
+
signal handler, so a forwarded signal could reach it in between and kill it,
|
|
536
|
+
and the launcher then reported that signal. Each fixture now registers its
|
|
537
|
+
handler first. The launcher's pre-spawn window above could not cause this:
|
|
538
|
+
the tests signal only after the child is running.
|
|
539
|
+
|
|
540
|
+
## [0.9.17-alpha.7] - 2026-09-28
|
|
541
|
+
|
|
542
|
+
A scheduled task is now just a command and a schedule. Each native row
|
|
543
|
+
carries its own `AKM_BUNDLE_DIR` instead of pointing at a descriptor file,
|
|
544
|
+
and the runtime reads only v4 task files; older files convert once with
|
|
545
|
+
`akm migrate apply`. The first `akm task sync` after upgrading rewrites each
|
|
546
|
+
row once, keeping every task and schedule. `akm improve` reworks only assets
|
|
547
|
+
that retrieval returned or that are new, and reflect refuses a rewrite that
|
|
548
|
+
grades worse on the asset's own searches. `--require-engines` no longer
|
|
549
|
+
skips a run because the LLM endpoint is busy.
|
|
550
|
+
|
|
551
|
+
### Changed
|
|
552
|
+
|
|
553
|
+
- **akm reads only task source v4.** A `version: 2` or `version: 3` task
|
|
554
|
+
file, or a `version: 4` file that still carries 0.9.15's retired
|
|
555
|
+
`schedule[].enabled`, now fails on its own with a message naming
|
|
556
|
+
`akm migrate apply`, which converts it once, under a backup (`akm upgrade`
|
|
557
|
+
runs it after an install). Until now every read converted such a file in
|
|
558
|
+
memory. `akm task sync` reports each one as a failure, leaves its installed
|
|
559
|
+
row as it is, and keeps reconciling every other task; `akm task run`,
|
|
560
|
+
`akm lint` and `akm task validate` report it the same way, and
|
|
561
|
+
`akm task validate`'s `converts` outcome is gone (such a file is
|
|
562
|
+
`blocked`). `akm migrate apply` now also converts the tasks of the stash
|
|
563
|
+
`AKM_BUNDLE_DIR` selects when no configured bundle names it, since the
|
|
564
|
+
runtime reads those too, and a root whose top-level task files are all
|
|
565
|
+
v2/v3 is still detected as an `akm-task` bundle, so they are found and
|
|
566
|
+
converted. A host whose task files are all v4 (`akm migrate status`
|
|
567
|
+
reports `current`) sees no difference.
|
|
568
|
+
(`src/tasks/source/parse-task-source.ts`, `src/commands/tasks/validate.ts`,
|
|
569
|
+
`scripts/akm-migrate/task-migrate.ts`,
|
|
570
|
+
`src/core/adapter/adapters/akm-task-adapter.ts`)
|
|
571
|
+
- **A scheduled row is its command plus its schedule, and carries its own
|
|
572
|
+
context.** Rows no longer name a `--scheduler-context` descriptor file;
|
|
573
|
+
they set what it held themselves. Every row sets `AKM_BUNDLE_DIR` to the
|
|
574
|
+
working stash of the shell that ran `akm task sync`, plus any
|
|
575
|
+
`AKM_CONFIG_DIR`, `AKM_DATA_DIR`, `AKM_CACHE_DIR` or `AKM_STATE_DIR` that
|
|
576
|
+
shell set explicitly: a `VAR=value` prefix in the crontab, an
|
|
577
|
+
`EnvironmentVariables` entry in a launchd plist, a `$env:VAR='value';`
|
|
578
|
+
assignment ahead of the command in Task Scheduler (its action has no
|
|
579
|
+
environment of its own). Scheduled runs see the same environment as
|
|
580
|
+
before, and sync still tells installations sharing a crontab apart by
|
|
581
|
+
that path (#846).
|
|
582
|
+
**What hosts see:** the first `akm task sync` after upgrading rewrites
|
|
583
|
+
every akm row once. `akm task sync --dry-run` lists each one as an update,
|
|
584
|
+
never an add or a remove; each keeps its launcher and its schedule, and
|
|
585
|
+
the `--scheduler-context <file>` argument becomes an inline
|
|
586
|
+
`AKM_BUNDLE_DIR=<working stash>`. On a host whose working stash is
|
|
587
|
+
`/home/u/akm` a row changes from
|
|
588
|
+
|
|
589
|
+
```text
|
|
590
|
+
30 8 * * * /home/u/.bun/bin/bun /home/u/.bun/lib/node_modules/akm-cli/dist/akm --scheduler-context /home/u/.local/share/akm/tasks/context/e898….json task run capture --bundle akm --scheduled > /home/u/.cache/akm/tasks/logs/capture.log 2>&1
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
to
|
|
594
|
+
|
|
595
|
+
```text
|
|
596
|
+
30 8 * * * AKM_BUNDLE_DIR=/home/u/akm /home/u/.bun/bin/bun /home/u/.bun/lib/node_modules/akm-cli/dist/akm task run capture --bundle akm --scheduled > /home/u/.cache/akm/tasks/logs/capture.log 2>&1
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
Rows written by 0.9.0 through 0.9.17-alpha.6 keep firing until that sync:
|
|
600
|
+
the CLI still accepts `--scheduler-context <file>` and applies the file's
|
|
601
|
+
environment (PATH included, for a 0.9.16 row). The files under
|
|
602
|
+
`$DATA/tasks/context/` are no longer written, and the uid, mode, symlink
|
|
603
|
+
and content-hash checks made on every scheduled run are gone. A sync
|
|
604
|
+
leaves some rows as they are (one whose task file failed to load, one a
|
|
605
|
+
`--bundle` sync did not cover), and those still name their file: once
|
|
606
|
+
`akm task doctor` lists no binding with a `contextPath`, nothing reads
|
|
607
|
+
them and they can be deleted. `akm task prune` now
|
|
608
|
+
finds rows whose `AKM_BUNDLE_DIR` names a directory that is gone, and older
|
|
609
|
+
rows whose descriptor cannot be read; `akm task doctor` lists `contextPath`
|
|
610
|
+
only for an older row. (`src/tasks/scheduler-invocation.ts`,
|
|
611
|
+
`src/tasks/backends/cron.ts`, `src/tasks/backends/launchd.ts`,
|
|
612
|
+
`src/tasks/backends/schtasks.ts`, `src/tasks/scheduler-sync.ts`,
|
|
613
|
+
`src/commands/tasks/tasks.ts`)
|
|
614
|
+
- **`akm improve` reworks only what gets read (#986).** An asset with fresh
|
|
615
|
+
feedback, or one you name (`akm improve skills/x`), is handled as before.
|
|
616
|
+
Every other pick must now be in the retrieval scope. That covers the
|
|
617
|
+
proactive-maintenance, high-salience and forgetting-safety lanes, and the
|
|
618
|
+
memories consolidation judges. An asset is in scope if a user `search`,
|
|
619
|
+
`curate` or `show` returned it, or user `feedback` named it, in the last 90
|
|
620
|
+
days, which is the usage log's retention. A hit on a `.derived` memory counts
|
|
621
|
+
for its parent. New material that no improve stage has processed yet is also
|
|
622
|
+
in scope. There is no new config key.
|
|
623
|
+
|
|
624
|
+
Measured with `akm improve --dry-run` on a copy of the maintainer's bundle
|
|
625
|
+
(19,870 assets), against 0.9.17-alpha.6:
|
|
626
|
+
- The fallback lanes pick from 6,450 assets instead of 15,686, and 9,236
|
|
627
|
+
refs are left out. No lane setting can reach the unread tail any more. In
|
|
628
|
+
July the proactive lane rewrote 3,069 assets, and 3,059 of them had not
|
|
629
|
+
been retrieved since the usage log began on 1 July.
|
|
630
|
+
- Consolidation judges 59 memories instead of 69.
|
|
631
|
+
- The high-salience lane no longer admits distill outputs nobody has read (2
|
|
632
|
+
today).
|
|
633
|
+
- Under the scheduled caps, today's nightly work is unchanged. The default
|
|
634
|
+
strategy selects the same 50 feedback-driven refs, and weekly proactive
|
|
635
|
+
maintenance selects the same 25, because salience ranking already puts
|
|
636
|
+
retrieved assets first.
|
|
637
|
+
|
|
638
|
+
`akm improve --dry-run` and the run result report the left-out refs as a new
|
|
639
|
+
`retrieval` gate. Health reports them under the skip reason `not_retrieved`.
|
|
640
|
+
Improve results stored by earlier releases, which have no such gate, still
|
|
641
|
+
decode. (`src/commands/improve/retrieval-scope.ts`,
|
|
642
|
+
`src/commands/improve/preparation.ts`, `src/commands/improve/consolidate.ts`)
|
|
643
|
+
|
|
644
|
+
- **Reflect refuses a rewrite that makes an asset worse for its own searches
|
|
645
|
+
(#722).** Before reflect proposes a rewrite of an existing asset, it grades
|
|
646
|
+
the old and the new content on up to five of the queries that actually
|
|
647
|
+
retrieved the asset (user `search` and `curate`). It uses the retrieval
|
|
648
|
+
eval's relevance prompt, which agrees with human grades at kappa 0.83. When
|
|
649
|
+
the new content grades lower on average, the rewrite is refused the same way
|
|
650
|
+
a quality-judge rejection is: `quality_rejected`, with the 14-day reflect
|
|
651
|
+
window. An asset without retrieval queries is not graded.
|
|
652
|
+
|
|
653
|
+
This was measured before it was built. Of 60 accepted rewrites since July,
|
|
654
|
+
judged this way, 14 graded lower (23%, 95% CI 14–35%) and 12 graded higher.
|
|
655
|
+
The gate was built because the lower bound cleared the 10% threshold set
|
|
656
|
+
before any judging. It costs two judge calls per query on the engine that
|
|
657
|
+
already runs the quality judge. On the maintainer's 2026-09-28 nightly run,
|
|
658
|
+
whose 30 rewrites had 102 usable queries, that is 204 calls, about 6 more
|
|
659
|
+
minutes on a 73-minute run. (`src/commands/improve/retrieval-gate.ts`,
|
|
660
|
+
`src/commands/improve/reflect.ts`)
|
|
661
|
+
|
|
662
|
+
### Fixed
|
|
663
|
+
|
|
664
|
+
- **A cron row too long for one line is seen by `akm task sync` again.** A
|
|
665
|
+
command over 1,000 bytes runs from a wrapper script, and sync could not
|
|
666
|
+
read which task such a row ran: every sync, and every `--dry-run`, showed
|
|
667
|
+
it as an add and wrote it again. Sync now reads the script, so the row is
|
|
668
|
+
unchanged or an update like any other. (`src/tasks/backends/cron.ts`)
|
|
669
|
+
- **A `$` or a backslash in a scheduled row's value is kept.** launchd and
|
|
670
|
+
Task Scheduler rows passed their values through a string replacement that
|
|
671
|
+
read `$'`, `$&` and `$$` as patterns, so a path such as a Windows admin
|
|
672
|
+
share (`\\nas\share\akm$`) came out corrupted; reading a crontab row
|
|
673
|
+
back dropped a backslash inside a single-quoted value.
|
|
674
|
+
(`src/tasks/backends/launchd.ts`, `src/tasks/backends/schtasks.ts`,
|
|
675
|
+
`src/tasks/backends/cron.ts`)
|
|
676
|
+
- **`akm improve --require-engines` no longer skips a run because the LLM
|
|
677
|
+
endpoint is busy.** Its reachability probe, one short completion, gave up
|
|
678
|
+
after 3 seconds, so a local server busy with another job looked
|
|
679
|
+
unreachable and the whole scheduled run failed (all four scheduled runs on
|
|
680
|
+
2026-09-27). The probe now waits up to the engine's own `timeoutMs`, at
|
|
681
|
+
most two minutes, so a busy server can answer while a hung one still fails
|
|
682
|
+
fast. The error's hint now says to check the endpoint rather than to run
|
|
683
|
+
`akm setup`.
|
|
684
|
+
(`src/commands/improve/improve-cli.ts`)
|
|
685
|
+
|
|
686
|
+
## [0.9.17-alpha.6] - 2026-09-27
|
|
687
|
+
|
|
688
|
+
Graph extraction stops losing and wasting work. A timed-out extraction is
|
|
689
|
+
retried instead of cached as empty. Long documents are extracted once, and
|
|
690
|
+
per-file calls respect the run's concurrency. `akm improve` honors
|
|
691
|
+
`index.graph`. `akm curate` returns nothing for harness and tool envelopes,
|
|
692
|
+
and search and curate show identical content once. Lazy graph extraction,
|
|
693
|
+
which never ran under Bun, is removed.
|
|
694
|
+
|
|
695
|
+
### Changed
|
|
696
|
+
|
|
697
|
+
- **`akm curate` returns nothing, on purpose, for input that is not a task.**
|
|
698
|
+
A harness or tool envelope (input that starts with an XML-style tag and
|
|
699
|
+
contains a closing tag, such as `<task-notification>…</task-notification>`,
|
|
700
|
+
`<system-reminder>…` or `<cross-session-message …>…`) and the stash README
|
|
701
|
+
line each used to get `--limit` unrelated assets. Every caller of
|
|
702
|
+
`akm curate` (the CLI, the OpenCode plugin, other harnesses) now gets an
|
|
703
|
+
empty `items` list with a `summary` that starts with `Curate abstained` and
|
|
704
|
+
names the reason, and a `tip`. On the retrieval suite curate abstains on 57
|
|
705
|
+
of 60 recorded non-task inputs and on none of the 221 real queries (nor on
|
|
706
|
+
any of 5,725 mined task queries). Length is not a reason to abstain: the
|
|
707
|
+
other 3 are task prompts of 2,431–5,531 characters, and in a judged sample
|
|
708
|
+
of 30 inputs over 2,000 characters the top 5 held a relevant asset for 24
|
|
709
|
+
of them (P@5 0.42, against 0.46 for prompts of 400–2,000 characters).
|
|
710
|
+
(`src/commands/read/curate.ts`)
|
|
711
|
+
- **Search and curate return identical content once.** Of entries whose
|
|
712
|
+
indexed content is identical (the same body saved under another name, as
|
|
713
|
+
both a memory and a knowledge doc, or in another bundle), only the
|
|
714
|
+
highest-ranked is kept, and the next candidate takes the freed slot. On the
|
|
715
|
+
retrieval suite such copies filled 7.5% of curate's top 5. Unique
|
|
716
|
+
precision@5, where a copy of a higher-ranked result earns nothing, rises
|
|
717
|
+
from 0.467 to 0.514 (+0.046, 95% CI [+0.028, +0.067]), and the share of
|
|
718
|
+
top-5 slots that repeat a higher-ranked result falls from 0.131 to 0.055.
|
|
719
|
+
Plain P@5 (0.553 → 0.551) and nDCG@10 stay within noise: they counted each
|
|
720
|
+
copy as another relevant result. Latency is unchanged.
|
|
721
|
+
(`src/indexer/search/db-search.ts`)
|
|
722
|
+
|
|
723
|
+
### Removed
|
|
724
|
+
|
|
725
|
+
- **The unused `utility_scores_scoped` index table is gone.** It shipped in
|
|
726
|
+
0.9.17-alpha.5 for per-project scoped utility scores, but no code ever read
|
|
727
|
+
or wrote a row. An index database drops it on its next writable open, the
|
|
728
|
+
same way other retired derived tables are dropped, with no layout-version
|
|
729
|
+
change. (`src/storage/repositories/index-schema.ts`)
|
|
730
|
+
- **Lazy graph extraction in `akm show` and `akm curate`.** With
|
|
731
|
+
`index.graph.lazyGraphExtraction: true`, `show` extracted an asset's graph
|
|
732
|
+
after building its response, so only the next `show` saw it. `curate`
|
|
733
|
+
queued assets for a later pass, which drained only the working bundle's
|
|
734
|
+
queue, and extractions made this way wrote no cache entry. Under Bun neither
|
|
735
|
+
path ever ran: the "already has a graph" check read a missing row as
|
|
736
|
+
present. Graph extraction now runs only in `akm improve`. The
|
|
737
|
+
`graph_extraction_queue` table is dropped the next time the index is opened
|
|
738
|
+
for writing. A config that still sets the key loads, and the key is named
|
|
739
|
+
once as unknown. (`src/commands/read/show.ts`,
|
|
740
|
+
`src/commands/read/curate.ts`, `src/indexer/graph/graph-extraction.ts`,
|
|
741
|
+
`src/storage/repositories/index-schema.ts`)
|
|
742
|
+
|
|
743
|
+
### Fixed
|
|
744
|
+
|
|
745
|
+
- **`index.metadataEnhance`'s default is no longer contradicted by dead
|
|
746
|
+
code.** Metadata enhancement has always defaulted to off
|
|
747
|
+
(`isLlmFeatureEnabled`); a second, unreachable code path in
|
|
748
|
+
`isProcessEnabled` claimed the opposite default and had no caller. Removed,
|
|
749
|
+
so one default remains. (`src/llm/feature-gate.ts`)
|
|
750
|
+
- **Eval tooling and docs catch up to the current config and index shape.**
|
|
751
|
+
`scripts/akm-eval/src/curate-bench.ts` wrote the retired `sources` config
|
|
752
|
+
key and called a nonexistent `akm index --dir`; it now seeds its sandbox
|
|
753
|
+
the same way the other akm-eval scripts and integration tests do, and
|
|
754
|
+
drops `--dir`. The graph A/B ablation harness
|
|
755
|
+
(`scripts/akm-eval/src/graph-ablation.ts`) planted its "graph off" config
|
|
756
|
+
where the sandboxed `akm` never read it, with config keys that didn't gate
|
|
757
|
+
anything (one of them a type error); it now writes
|
|
758
|
+
`index.graph.enabled: false` to the sandbox's actual `AKM_CONFIG_DIR`.
|
|
759
|
+
Updated `scripts/akm-eval/README.md` and `docs/maintainers/eval.md` to
|
|
760
|
+
match, and corrected stale `docs/architecture/architecture.md` references
|
|
761
|
+
to `db-backup`, `staleness-detect`, and `src/commands/graph/`.
|
|
762
|
+
- **Scheduled graph extraction reads `index.graph`.** `akm improve` passed
|
|
763
|
+
graph extraction a batch size of 4 and the `memory` and `knowledge` types
|
|
764
|
+
whenever the strategy's `processes.graphExtraction` did not set them, so
|
|
765
|
+
`index.graph.graphExtractionBatchSize` and `graphExtractionIncludeTypes`
|
|
766
|
+
never applied. It did not read `index.graph`'s `engine`, `model`,
|
|
767
|
+
`timeoutMs` or `llm` either, so a setting such as
|
|
768
|
+
`index.graph.llm.enableThinking: false` had no effect on improve runs. A
|
|
769
|
+
value in the strategy's `processes.graphExtraction` still wins. A setting it
|
|
770
|
+
leaves unset now comes from `index.graph`, then from the built-in default.
|
|
771
|
+
Where `index.graph` asks for something improve did not use before, the
|
|
772
|
+
extractor changes and cached extractions stop applying, so those files are
|
|
773
|
+
extracted again. (`src/commands/improve/loop-stages.ts`,
|
|
774
|
+
`src/commands/improve/execution.ts`,
|
|
775
|
+
`src/commands/improve/improve-strategies.ts`)
|
|
776
|
+
- **A graph extraction that times out is retried, not cached as empty.** A
|
|
777
|
+
call that ran past the engine's `timeoutMs` was recorded as "no entities"
|
|
778
|
+
and cached, so the file was never extracted again. It is now recorded as
|
|
779
|
+
failed, and the next run retries it; timeouts also count toward the run's
|
|
780
|
+
failure-rate abort. A batch that times out fails its files without then
|
|
781
|
+
calling the model once per file. An empty response is likewise recorded as
|
|
782
|
+
failed. (`src/llm/graph-extract.ts`)
|
|
783
|
+
- **Long bodies are extracted once after batching turns itself off.** Two
|
|
784
|
+
non-array batch responses turn batching off for the rest of a run. From
|
|
785
|
+
then on, a body over 1,600 characters was extracted on its own and then
|
|
786
|
+
again with the rest of its batch. Each body is now extracted once.
|
|
787
|
+
(`src/llm/graph-extract.ts`)
|
|
788
|
+
- **A batch's per-file calls respect the run's concurrency.** When a batch
|
|
789
|
+
fell back to one call per file (long bodies, a non-array response, batching
|
|
790
|
+
turned off), those calls all went out at once, up to the batch size. Local
|
|
791
|
+
endpoints serve one or two requests at a time. The calls now run within the
|
|
792
|
+
limit the run applies to its batches, one at a time by default.
|
|
793
|
+
(`src/llm/graph-extract.ts`)
|
|
794
|
+
- **`related` counts a shared entity once.** `akm show`'s `related` list,
|
|
795
|
+
and curate's support refs taken from it, ranked files by the number of
|
|
796
|
+
matching entity rows. A file holding two case forms of one entity, as rows
|
|
797
|
+
from older extractors can, counted it twice and could outrank a file that
|
|
798
|
+
shared two entities. `related` now counts distinct entities. Extraction also
|
|
799
|
+
keeps one form of each entity before writing. The stored key `related`
|
|
800
|
+
matches on is now the one extraction deduplicates on, which also drops
|
|
801
|
+
surrounding quotes and backticks.
|
|
802
|
+
(`src/indexer/graph/graph-related.ts`,
|
|
803
|
+
`src/indexer/graph/graph-extraction.ts`, `src/indexer/db/graph-db.ts`)
|
|
804
|
+
- **A config change that re-extracts the graph says so.** Cached graph
|
|
805
|
+
extractions are keyed by extractor: model, batch size, included asset types
|
|
806
|
+
and prompt version. Changing any of them made every cached file extract
|
|
807
|
+
again without a word. The first run after such a change now warns once,
|
|
808
|
+
naming the change and the number of cached files it will extract again, and
|
|
809
|
+
records the warning in the run's result.
|
|
810
|
+
(`src/indexer/graph/graph-extraction.ts`)
|
|
811
|
+
- **Graph extraction reports what its parser filtered.** A run's graph
|
|
812
|
+
telemetry, part of `akm improve`'s result, now carries
|
|
813
|
+
`filteredGenericEntities`, `filteredInvalidRelations`,
|
|
814
|
+
`filteredLowConfidenceRelations` and `contextBatchRetries`. The pass
|
|
815
|
+
computed them and dropped them, and did not count batch responses at all.
|
|
816
|
+
(`src/indexer/graph/graph-extraction.ts`, `src/llm/graph-extract.ts`)
|
|
817
|
+
- **An unknown key under `index.<pass>` is kept and named once.** It was
|
|
818
|
+
dropped from the loaded config and named twice. It is now handled like an
|
|
819
|
+
unknown key anywhere else in config. (`src/core/config/schema/index-config.ts`)
|
|
820
|
+
|
|
821
|
+
## [0.9.17-alpha.5] - 2026-09-27
|
|
822
|
+
|
|
823
|
+
`akm show` works again for a memory that has a `.derived.md` child (835 of them
|
|
824
|
+
in one real bundle), and `akm bundle add --provider … --name` holds to the same
|
|
825
|
+
`--name` contract as every other add.
|
|
826
|
+
|
|
827
|
+
### Fixed
|
|
828
|
+
|
|
829
|
+
- **`akm show` works for a memory that has a `.derived.md` child.** When
|
|
830
|
+
`memories/X.md` and `memories/X.derived.md` both existed, `akm show
|
|
831
|
+
memories/X`, with or without a `#fragment`, failed with
|
|
832
|
+
`RESOURCE_ALREADY_EXISTS` ("multiple physical owners"); `akm curate`
|
|
833
|
+
previewed such a memory from its description alone, and `akm curate --pack`
|
|
834
|
+
left it out. The index gives the derived child its own ref,
|
|
835
|
+
`memories/X.derived`, but the ref lookup also counted `X.derived.md` as a
|
|
836
|
+
file for `memories/X`. The lookup now follows the index: `memories/X` is
|
|
837
|
+
`X.md` and `memories/X.derived` is `X.derived.md`. A derived child whose
|
|
838
|
+
parent file is gone no longer answers for the parent's ref either, so it
|
|
839
|
+
cannot hide a real `X.md` in a lower-priority bundle. `akm lint` and
|
|
840
|
+
`--xref` / `--supersedes` validation still accept a ref to `memories/X`
|
|
841
|
+
when only `X.derived.md` remains. Broken since 0.9.7.
|
|
842
|
+
(`src/core/asset/asset-placement.ts`, `src/commands/lint/base-linter.ts`)
|
|
843
|
+
- **`akm bundle add --provider … --name` keeps the `--name` contract too.**
|
|
844
|
+
Since 0.9.17-alpha.4 an explicit `--name` that is not a legal bundle slug,
|
|
845
|
+
or is taken by another bundle, fails with exit 2, and re-adding a source
|
|
846
|
+
under a different name points at `akm bundle rename`. A declarative add
|
|
847
|
+
(`akm bundle add <target> --provider npm|git|website`) still replaced such
|
|
848
|
+
a name with a derived one and exited 0. It now fails the same way, before
|
|
849
|
+
any write. (`src/commands/sources/source-manage.ts`)
|
|
850
|
+
|
|
851
|
+
## [0.9.17-alpha.4] - 2026-09-27
|
|
852
|
+
|
|
853
|
+
Search and curate are rebuilt on measured evidence. On a 221-query suite of real
|
|
854
|
+
akm queries judged for relevance, search nDCG@10 goes from 0.346 to 0.556 and
|
|
855
|
+
curate precision@5 from 0.350 to 0.551, with search p50 falling from 787 ms to
|
|
856
|
+
about 400 ms and a fresh index shrinking from 560 MB to 340 MB.
|
|
857
|
+
|
|
858
|
+
Upgrades stop breaking because the machinery that broke them is gone, not
|
|
859
|
+
because more was added: this release removes the scheduler-grant layer, the
|
|
860
|
+
filesystem transaction journals, the maintenance barrier and lock mutex, the
|
|
861
|
+
strict config schemas and their retired-key registry, and every per-key
|
|
862
|
+
config migration, and it lands with fewer lines in `src/` than 0.9.17-alpha.3.
|
|
863
|
+
|
|
864
|
+
### Changed
|
|
865
|
+
|
|
866
|
+
- **Search ranks by reciprocal rank fusion of BM25 and document vectors.**
|
|
867
|
+
Two candidate lists, 100 each, are fused with equal weights (k = 60): BM25
|
|
868
|
+
over whole documents (`entries_fts`) matching any of the query's
|
|
869
|
+
non-stopword words (every word when the query has nothing else), and the
|
|
870
|
+
document vectors nearest to the query embedding. Equal scores are ordered by
|
|
871
|
+
ref, so the ranking depends only on the index and the query's embedding
|
|
872
|
+
(keyword-only runs of the suite reproduce exactly). Filters (`--type`,
|
|
873
|
+
`--from`, `--filter`, `--belief`, the default session exclusion, proposed
|
|
874
|
+
quality) and one-hit-per-file deduplication narrow the fused list without
|
|
875
|
+
reordering it. A hit's `score` is its fused score (at most 2/61 ≈ 0.033),
|
|
876
|
+
and `--detail full`'s `whyMatched` lists its rank in each list
|
|
877
|
+
(`lexical rank 3`, `vector rank 12`). On the retrieval suite (221 real
|
|
878
|
+
queries over a 23k-document snapshot, LLM-judged) nDCG@10 rises from 0.347
|
|
879
|
+
to 0.566 and P@5 from 0.347 to 0.558, level with the lab reference design;
|
|
880
|
+
every query class improves, questions (0.20 → 0.44) and long prompts
|
|
881
|
+
(0.24 → 0.52) most. End to end, process start included, search p50/p95
|
|
882
|
+
fell from 787/4130 ms to 341/830 ms. BM25 weighs the five columns
|
|
883
|
+
equally: the previous 10/5/3/2/1 weights measured 0.011 lower P@5.
|
|
884
|
+
(`src/indexer/search/db-search.ts`,
|
|
885
|
+
`src/indexer/search/ranking.ts`, `src/indexer/search/fts-query.ts`,
|
|
886
|
+
`src/storage/repositories/index-fts-repository.ts`.)
|
|
887
|
+
- **Queries are embedded the way the embedding model expects.** An embedding
|
|
888
|
+
profile picks query and document templates by model name: Qwen3-Embedding
|
|
889
|
+
gets its retrieval instruction on queries, nomic-embed `search_query: ` /
|
|
890
|
+
`search_document: `, the BGE English, mxbai and arctic models the
|
|
891
|
+
"Represent this sentence for searching relevant passages: " query prefix,
|
|
892
|
+
E5 `query: ` / `passage: `, and other models none; `embedding.queryTemplate`
|
|
893
|
+
and `embedding.documentTemplate` override the preset (`""` turns it off).
|
|
894
|
+
The document template is part of the embedding fingerprint, so a nomic or
|
|
895
|
+
E5 index re-embeds on the next `akm index`; Qwen3, BGE and the default local
|
|
896
|
+
model keep their vectors. Text reaches the embedder with its case: the query
|
|
897
|
+
is no longer lowercased, and an entry's embedded text keeps its case once
|
|
898
|
+
the entry is next re-indexed (`akm index --reembed` refreshes every vector
|
|
899
|
+
at once). The query embedding is requested before the keyword query runs,
|
|
900
|
+
and a search waits for it at most `embedding.queryTimeoutMs` (default 3000)
|
|
901
|
+
before serving keyword ranking alone with one warning — a hung endpoint
|
|
902
|
+
used to hold a search for up to 120 s. (`src/llm/embedders/profile.ts`,
|
|
903
|
+
`src/indexer/materialize-embeddings.ts`.)
|
|
904
|
+
- **Curate is one search.** `akm curate` takes the top `--limit` hits of the
|
|
905
|
+
fused search in order and enriches each with its preview, run details and
|
|
906
|
+
up to two graph-related support refs, so curate's items are search's top
|
|
907
|
+
hits (P@5 0.350 → 0.556 on the retrieval suite; p50/p95 1082/4148 ms →
|
|
908
|
+
433/888 ms). The optional reranker (`search.curateRerank`, still off by
|
|
909
|
+
default) now reorders the top 30 fused candidates (`topN`, previously 8 but
|
|
910
|
+
applied only to the final `limit` items) and sends each as its name,
|
|
911
|
+
description and the start of its indexed content (2,000 characters in all)
|
|
912
|
+
instead of name and description. (`src/commands/read/curate.ts`.)
|
|
913
|
+
- **Vectors are stored once (index layout 25).** Each entry's vector lives
|
|
914
|
+
only in `embeddings`, and search scores every current-model row by cosine
|
|
915
|
+
similarity in JavaScript. The sqlite-vec mirror `entries_vec` is gone with
|
|
916
|
+
its repair pass, readiness flag and width bookkeeping: sqlite-vec cannot
|
|
917
|
+
load in the standalone binaries (`bun build --compile` does not bundle the
|
|
918
|
+
optional package), under Bun on macOS (the system SQLite refuses
|
|
919
|
+
extensions) or wherever the optional dependency is missing, so those
|
|
920
|
+
installs always searched the BLOB rows anyway, and a mirror that fell out
|
|
921
|
+
of step returned wrong neighbours without an error. The scan now reads
|
|
922
|
+
float32 views of the rows instead of copying each into an array: 85 ms per
|
|
923
|
+
query for 24k 1,024-dimension vectors in a fresh process, against 460 ms for
|
|
924
|
+
the old fallback and 41 ms for sqlite-vec. The first writable open drops
|
|
925
|
+
`entries_vec` (100 MB on that index) and the `embeddingDim` and
|
|
926
|
+
`vecFastPathReady` meta keys; dropping a vec0 table needs the extension, so
|
|
927
|
+
sqlite-vec stays an optional dependency for that alone, and an install
|
|
928
|
+
without it leaves the unread table in place. `semanticStatus` is `ready-js`
|
|
929
|
+
whenever every entry has a vector (`ready-vec` is gone), the `vecAvailable`
|
|
930
|
+
field leaves `akm index` and `akm info` output, setup no longer probes for
|
|
931
|
+
sqlite-vec, and `embedding.dimension` loses its 4,096 cap, which only the
|
|
932
|
+
vec0 column needed. (`src/storage/repositories/index-vec-repository.ts`,
|
|
933
|
+
`src/storage/repositories/index-schema.ts`,
|
|
934
|
+
`src/indexer/materialize-embeddings.ts`.)
|
|
935
|
+
- **The fragment full-text table is gone (index layout 25).** Nothing has
|
|
936
|
+
read `entry_fragments_fts` since fragments stopped competing as search
|
|
937
|
+
candidates, so an upsert no longer splits the body into fragment rows and
|
|
938
|
+
the first writable open drops the table (39 MB on a 24k-entry index).
|
|
939
|
+
`entry_fragments` stays: `akm show <ref>#akm-fragment-…` (#937) resolves the
|
|
940
|
+
selector from its stored safe Markdown.
|
|
941
|
+
(`src/storage/repositories/index-fts-repository.ts`,
|
|
942
|
+
`src/storage/repositories/index-schema.ts`.)
|
|
943
|
+
- **The embedding input is derived, not stored (index layout 25).**
|
|
944
|
+
`entries.search_text` held a third copy of every body (74 MB on a
|
|
945
|
+
24k-entry index) only to feed the embedder and to notice when an entry's
|
|
946
|
+
vector went stale. The embedding pass now derives the text from
|
|
947
|
+
`document_json` when it embeds an entry, and `entries.embed_hash` keeps its
|
|
948
|
+
SHA-256: an upsert whose hash differs deletes the vector, exactly as a
|
|
949
|
+
changed `search_text` did. The first writable open hashes each stored
|
|
950
|
+
`search_text` before dropping the column, so every vector stays attached
|
|
951
|
+
until its entry's text really changes, and nothing is re-embedded by the
|
|
952
|
+
upgrade. (`src/storage/repositories/index-entries-repository.ts`,
|
|
953
|
+
`src/storage/repositories/index-vec-repository.ts`,
|
|
954
|
+
`src/storage/repositories/index-schema.ts`.)
|
|
955
|
+
- **`akm index` reclaims index.db's free pages.** Nothing ever VACUUMed
|
|
956
|
+
`index.db`, so every table an upgrade rebuilt or dropped stayed on disk as
|
|
957
|
+
free pages: a 24k-entry index measured 979 MB, 427 MB of it free, against
|
|
958
|
+
560 MB for a fresh build of the same content. The run now ends with a
|
|
959
|
+
VACUUM when the writable open migrated the layout (it leaves
|
|
960
|
+
`index_meta.vacuumPending` for the next `akm index`, since the open may sit
|
|
961
|
+
inside a caller's transaction) and whenever more than half the pages are
|
|
962
|
+
free, the threshold and pass improve already apply to `state.db`
|
|
963
|
+
(`vacuumIfReclaimable`, formerly `vacuumStateDbIfReclaimable`). A busy
|
|
964
|
+
database skips the VACUUM instead of failing the run; each VACUUM prints
|
|
965
|
+
its page counts and appends an `index_db_vacuumed` event. With the three
|
|
966
|
+
layout-25 removals above, a fresh build of the 24k-entry retrieval snapshot
|
|
967
|
+
is 340 MB instead of 560 MB, and a copy of the 601 MB layout-24 build
|
|
968
|
+
migrates in 0.8 s with all 23,979 vectors kept byte for byte, then VACUUMs
|
|
969
|
+
to 377 MB. Retrieval is unchanged on the suite (search nDCG@10 0.5562 →
|
|
970
|
+
0.5560, Δ −0.0002 [−0.0014, +0.0009]; P@5 0.5507 → 0.5517; curate P@5
|
|
971
|
+
identical), and search p50/p95 moved from 374/912 ms to 401/870 ms.
|
|
972
|
+
(`src/indexer/indexer.ts`, `src/storage/state-db-integrity.ts`.)
|
|
973
|
+
- **An index a newer akm wrote is refused, naming the upgrade.** Readers
|
|
974
|
+
used to serve a newer layout "as far as they could" and the writable open
|
|
975
|
+
continued at its own layout, setting the marker back so the two releases
|
|
976
|
+
alternated. A newer layout can lack a column an older reader selects —
|
|
977
|
+
layout 25 drops `entries.search_text` — so every opener now refuses it with
|
|
978
|
+
`INDEX_SCHEMA_INCOMPATIBLE` ("Upgrade akm to use this index.") and leaves
|
|
979
|
+
the file untouched; an older layout is still served as-is and migrated by
|
|
980
|
+
the next writable open, and `akm improve --dry-run` reports the refusal as
|
|
981
|
+
an incompatible snapshot. (`src/storage/repositories/index-connection.ts`,
|
|
982
|
+
`src/storage/repositories/index-schema.ts`.)
|
|
983
|
+
Downgrading to 0.9.17-alpha.3 or earlier is not supported for the index:
|
|
984
|
+
those releases select columns layout 25 dropped, so rebuild it with
|
|
985
|
+
`akm index --full` under the older release.
|
|
986
|
+
- **Scheduled rows no longer freeze the syncing shell's directories or PATH.**
|
|
987
|
+
A `--scheduler-context` descriptor now carries the resolved bundle path
|
|
988
|
+
(sync's ownership signal, #846) plus only the `AKM_CONFIG_DIR`,
|
|
989
|
+
`AKM_DATA_DIR`, `AKM_CACHE_DIR` and `AKM_STATE_DIR` values the process that
|
|
990
|
+
ran `task sync` had set explicitly; resolved defaults are left to resolve at
|
|
991
|
+
fire time, exactly as they do for an interactive command. It used to capture
|
|
992
|
+
every resolved directory and the whole PATH: one host's `task sync`, run from
|
|
993
|
+
inside a desktop app whose environment pointed `$STATE` at the app's own
|
|
994
|
+
config directory, froze that directory into eight cron rows on 2026-08-06,
|
|
995
|
+
every later sync preserved it, and the nightly improve run then held its
|
|
996
|
+
locks where no interactive command could see them. PATH moves into the
|
|
997
|
+
native artifact, where it is visible and editable: a `PATH=` line inside a
|
|
998
|
+
`# akm:env BEGIN`/`END` section written directly above the first akm task
|
|
999
|
+
block (cron applies it to the rows that follow it; akm rewrites the line on
|
|
1000
|
+
every crontab write and removes it with the last task block), and an
|
|
1001
|
+
`EnvironmentVariables` entry in each launchd plist. Task Scheduler runs a
|
|
1002
|
+
task with the account's own environment and carries no PATH. Plain
|
|
1003
|
+
`akm task sync` recomputes the descriptor on every run — an installed row
|
|
1004
|
+
whose descriptor no longer matches is updated while its launcher is kept as
|
|
1005
|
+
before (only `--rebind` moves that) — so one sync after upgrading rewrites
|
|
1006
|
+
every row written under the old policy. Descriptors an older release wrote
|
|
1007
|
+
still load, PATH included, until that sync. (`src/tasks/scheduler-invocation.ts`,
|
|
1008
|
+
`src/tasks/backends/cron.ts`, `src/tasks/backends/launchd.ts`,
|
|
1009
|
+
`src/tasks/scheduler-sync.ts`.)
|
|
1010
|
+
- **The write path keeps only what its callers use** (`src/core/write-source.ts`,
|
|
1011
|
+
1,485 → 602 lines). The git transaction chain — publication identity capture,
|
|
1012
|
+
path, worktree and commit snapshot validation, base-HEAD assertions,
|
|
1013
|
+
transaction-commit discovery, a per-repo pending-mutation registry and a
|
|
1014
|
+
plan/begin/publish API — lost its last callers when the proposal
|
|
1015
|
+
transaction journals were removed and survived only because one test
|
|
1016
|
+
imported it; its 15 exports and their private helpers are gone. A write to a
|
|
1017
|
+
git-backed bundle now writes the file atomically inside the bundle root,
|
|
1018
|
+
records the exact path, and the boundary commits exactly those paths and
|
|
1019
|
+
pushes with `--force-with-lease`. A dirty or gitignored destination is no
|
|
1020
|
+
longer refused: an ignored path stays local with a warning instead of the
|
|
1021
|
+
command throwing after the file had already landed, and an upstream that
|
|
1022
|
+
cannot be inspected during preparation warns instead of aborting. Path
|
|
1023
|
+
containment, the symlink-escape refusal and the detached-HEAD refusal stay.
|
|
1024
|
+
- **Readers tolerate everything older releases wrote.** No config object is
|
|
1025
|
+
strict any more: a key this release does not know — retired, misspelled,
|
|
1026
|
+
or written by a newer release — is kept in memory and named once
|
|
1027
|
+
(`unknownConfigKeyPaths`, `src/core/config/config.ts`, found by walking
|
|
1028
|
+
the schema), round-trips through ordinary writes so a newer release's
|
|
1029
|
+
settings survive a downgrade, and is dropped only by `akm migrate apply`.
|
|
1030
|
+
The retired-keys registry, its read shim, and the schema-compatibility
|
|
1031
|
+
lint are removed; nothing needs registering for a key to be tolerated.
|
|
1032
|
+
- **`akm migrate apply` has one config step.** `configFile`
|
|
1033
|
+
(`normalizeConfigFile`) reads config.json through the same pipeline every
|
|
1034
|
+
load runs (`configVersion` read, legacy source shape, `extraParams` lift)
|
|
1035
|
+
and writes the current shape back under a backup, dropping unknown keys.
|
|
1036
|
+
It replaces the per-key `configLegacySourceShape`, `configExtraParams`,
|
|
1037
|
+
`configRetiredKeys` and `configSchedulerSourceIds` steps, the
|
|
1038
|
+
`schedulerActivation` and `staleTxns` steps, and the `--host-local` mode. A
|
|
1039
|
+
pending config lift is now `ready`, never a blocker for the other steps.
|
|
1040
|
+
The `deadResidue` step also removes the transaction-journal,
|
|
1041
|
+
maintenance-barrier, lock-mutex and version-stamp files older releases left
|
|
1042
|
+
under `$DATA`, `$STATE` and `$CONFIG`, and runs whether or not a bundle is
|
|
1043
|
+
configured.
|
|
1044
|
+
- **Scheduling is one list.** `scheduler.enabled` holds the fully-qualified
|
|
1045
|
+
refs this host schedules (`bundle//tasks/x`). It is still written in the
|
|
1046
|
+
`{kind, ref, sourceId}` shape 0.9.16 reads, so that release keeps working
|
|
1047
|
+
against a config this one wrote; either shape is read as the ref.
|
|
1048
|
+
A config with no list at all (every release before 0.9.17) means "keep
|
|
1049
|
+
what is installed": the first `akm task sync` (or `setup`, `task enable`,
|
|
1050
|
+
`task disable`, `task add`) takes the akm-written rows already in the
|
|
1051
|
+
native scheduler as the host's choice, writes the list, and says so;
|
|
1052
|
+
`task sync --dry-run` reports it without writing. An explicit list, empty
|
|
1053
|
+
or not, is never second-guessed. Grants, source identities, the
|
|
1054
|
+
carry-forward, the scheduler-activation and source-id migrations and the
|
|
1055
|
+
fire-time re-check are gone (`src/tasks/activation-config.ts`).
|
|
1056
|
+
- **Proposal accept and revert write directly.** The asset file is written
|
|
1057
|
+
(temp file + rename), committed through the ordinary write-target
|
|
1058
|
+
boundary, then the proposal row and its event are recorded in one
|
|
1059
|
+
state.db transaction and the file is indexed best-effort. A crash in
|
|
1060
|
+
between leaves a re-acceptable pending proposal, nothing corrupt. The
|
|
1061
|
+
filesystem transaction journals (`src/core/fs-txn.ts`) with their
|
|
1062
|
+
recovery, quarantine, deferral and fencing are removed, along with the
|
|
1063
|
+
`txn-quarantine`/`txn-awaiting-recovery` health advisories.
|
|
1064
|
+
- **`akm bundle update` publishes, records the lock entry, then reindexes —
|
|
1065
|
+
with no rollback transaction.** An update still fetches into a staging
|
|
1066
|
+
directory beside the cache and audits the staged bytes for dangerous env
|
|
1067
|
+
keys before anything goes live; a blocked or failed audit changes nothing.
|
|
1068
|
+
It then publishes with one rename (a fast-forward for a writable Git
|
|
1069
|
+
checkout), writes the lock entry, and reindexes. If the reindex fails, the
|
|
1070
|
+
new content and lock entry stay for the next `akm index`, and the previous
|
|
1071
|
+
install directory is kept. The config, staged-content, lockfile-byte and
|
|
1072
|
+
checkout-HEAD fences and the lockfile compare-and-swap restore are gone, so
|
|
1073
|
+
an update no longer fails with "changed concurrently" or "changed after its
|
|
1074
|
+
staged bytes were audited": it already runs under the asset-mutation lease,
|
|
1075
|
+
and Git refuses a fast-forward that would overwrite local work. A website
|
|
1076
|
+
source refreshes through its mirror's own snapshot staging, so a killed
|
|
1077
|
+
refresh still keeps the previous mirror.
|
|
1078
|
+
- **A lock file is one `O_EXCL` create** (`src/core/file-lock.ts`). The
|
|
1079
|
+
SQLite lock-operation mutex, the maintenance barrier (a lock guarding lock
|
|
1080
|
+
registration) and its per-open activity registry — the source of the
|
|
1081
|
+
lock-sidecar leak that grew `$STATE` by hundreds of megabytes — are
|
|
1082
|
+
removed. `MAINTENANCE_BARRIER_BUSY` no longer exists; contention is
|
|
1083
|
+
reported as `INDEX_DB_CONTENDED`, `STATE_DB_CONTENDED` or
|
|
1084
|
+
`IMPROVE_LOCK_HELD`, as before.
|
|
1085
|
+
- **Removed from 0.9.17-alpha:** startup version reconciliation
|
|
1086
|
+
(`version-reconcile.json`), the akm-install enumerator and `akm upgrade
|
|
1087
|
+
--version`/`--tag` with its other-install mover, the `version-reconcile`,
|
|
1088
|
+
`scheduler-grants`, `scheduled-startup-failures` and `akm-installs` health
|
|
1089
|
+
advisories, and the `akm info` `compat` manifest with its
|
|
1090
|
+
`PLUGIN_PROTOCOL_VERSION`. `akm upgrade` is what it was in 0.9.16.
|
|
1091
|
+
- **Documented the persisted-data compatibility contract.** Added
|
|
1092
|
+
`docs/architecture/persisted-data-compat.md`: the four-sentence contract a
|
|
1093
|
+
reader owes data an earlier release wrote, plus a per-format table (config,
|
|
1094
|
+
`state.db`, `index.db`, task source, workflow IR, native scheduler rows,
|
|
1095
|
+
proposal and task-history metadata, lock payloads, `.akm` residue) naming
|
|
1096
|
+
where each is written, its version marker, its older/newer-data behavior,
|
|
1097
|
+
and which gate covers it — with explicit `Gap:` notes where the code does
|
|
1098
|
+
not meet the contract yet. Registered in `docs/architecture/README.md`.
|
|
1099
|
+
`AGENTS.md`'s "Reading persisted data" section now points at this doc
|
|
1100
|
+
instead of a deleted file.
|
|
1101
|
+
|
|
1102
|
+
- **Scheduler writes hold one lock and apply row by row.** `akm task sync`,
|
|
1103
|
+
`add`, `enable`, `disable` and `prune --yes` hold one `O_EXCL` lock,
|
|
1104
|
+
`$STATE/locks/scheduler.lock` (`src/tasks/scheduler-lock.ts`), for the
|
|
1105
|
+
whole read–plan–write; a second scheduler command exits 75
|
|
1106
|
+
(`SCHEDULER_LOCK_HELD`), and a lock left by a dead process is reclaimed.
|
|
1107
|
+
Under it, sync reads the installed rows once and diffs by native id: a
|
|
1108
|
+
missing row is installed, a changed row rewritten, a row whose source is
|
|
1109
|
+
gone or no longer enabled removed. A row that fails to install or remove,
|
|
1110
|
+
or two sources claiming one native id, is reported in `failures` (exit 1)
|
|
1111
|
+
while every other row applies; the per-row compare-and-swap expectations
|
|
1112
|
+
and whole-set rollback are gone. A task whose source stops parsing keeps
|
|
1113
|
+
its installed row instead of being unscheduled by a YAML typo.
|
|
1114
|
+
(`src/tasks/scheduler-sync.ts`, `src/commands/tasks/tasks.ts`)
|
|
1115
|
+
- **`akm task add` is "write, enable, sync".** It validates the task and
|
|
1116
|
+
refuses an id already scheduled from another bundle before writing
|
|
1117
|
+
anything, then writes the source, adds the ref to `scheduler.enabled`
|
|
1118
|
+
(unless `--disabled`) and syncs the bundle. When the row cannot be
|
|
1119
|
+
installed, add fails naming the cause and the task stays written and
|
|
1120
|
+
enabled for the next `akm task sync` to retry; it no longer restores the
|
|
1121
|
+
prior source and rows byte-for-byte. `--force` with fewer schedules removes
|
|
1122
|
+
the dropped schedules' rows through the same sync, and `--rebind` means
|
|
1123
|
+
what it means for `task sync`.
|
|
1124
|
+
- **Improve records what it tried in one ledger** (`improve_ledger`,
|
|
1125
|
+
`src/storage/repositories/improve-ledger-repository.ts`). One row per
|
|
1126
|
+
stash, ref and stage holds the last attempt, its outcome and when the ref
|
|
1127
|
+
is next eligible, from one cadence table:
|
|
1128
|
+
|
|
1129
|
+
| Outcome | Next eligible | Lifted early by newer feedback? |
|
|
1130
|
+
| --- | --- | --- |
|
|
1131
|
+
| rejected, quality_rejected | 14 d reflect, 30 d distill, 7 d other stages | no |
|
|
1132
|
+
| expired | 1 d | no |
|
|
1133
|
+
| proposed, review_needed, unchanged, judged_no_action | 7 d | yes |
|
|
1134
|
+
| accepted, failed | immediately | — |
|
|
1135
|
+
|
|
1136
|
+
Every stage reads it before any LLM call. It replaces proposal
|
|
1137
|
+
fingerprints, the per-stage cooldowns, the distill reject files and the
|
|
1138
|
+
event-timestamp cursors, which disagreed with one another (quality
|
|
1139
|
+
rejections never reached the fingerprints; consolidate re-judged promoted
|
|
1140
|
+
memories). Distill and consolidate now key by their input refs, so each
|
|
1141
|
+
such input may be attempted once more after upgrading. Schema repair paces
|
|
1142
|
+
itself with the ledger too, replacing its private 7-day cooldown and
|
|
1143
|
+
3-attempts-per-30-days cap. Every stage — reflect, distill, consolidate,
|
|
1144
|
+
extract, triage, memory inference, graph extraction — runs through one
|
|
1145
|
+
shared path (`src/commands/improve/stage.ts`): pick the runner, call the
|
|
1146
|
+
model, judge the output, mint the proposal, record the usage.
|
|
1147
|
+
- **`akm proposal drain` has one rule.** A proposal the quality judge passed
|
|
1148
|
+
(a `staged` gate decision whose content hash still matches) is accepted, an
|
|
1149
|
+
empty diff is rejected, and everything else goes to the judgment tier
|
|
1150
|
+
(`processes.triage.judgment`) or waits for review. Extract and consolidate
|
|
1151
|
+
proposals, which the `personal-stash` policy auto-accepted on size alone,
|
|
1152
|
+
carry no judge stamp, so they now go to the judgment tier — or wait for
|
|
1153
|
+
review when none is configured — instead of being accepted. The policies
|
|
1154
|
+
and their flags are retired (see Removed). `--dry-run` now predicts what a
|
|
1155
|
+
real drain does: a proposal whose target already holds its content (an
|
|
1156
|
+
accept that wrote the file but was interrupted before recording it) is
|
|
1157
|
+
reported as promoted, as the real drain finishes it, instead of as a
|
|
1158
|
+
stale-target rejection.
|
|
1159
|
+
- **State migration `028-improve-ledger` creates the ledger and drops six
|
|
1160
|
+
tables.** It backfills the ledger from each ref's latest proposal and drops
|
|
1161
|
+
`proposal_fingerprints`, `improve_gate_thresholds`, `proposal_fs_imports`,
|
|
1162
|
+
`consolidation_judged`, `improve_cycle_metrics` and `canary_queries`.
|
|
1163
|
+
Because it drops schema, the first open after upgrading copies the
|
|
1164
|
+
database to `state.db.pre-028-improve-ledger.bak` before it runs.
|
|
1165
|
+
- **Upgrading no longer rebuilds or re-embeds the search index (index layout
|
|
1166
|
+
24).** The first writable open applies a layout change in place — added
|
|
1167
|
+
columns, and a one-time rebuild of the two full-text tables from the stored
|
|
1168
|
+
entries (about 2–3 s for 24k entries); embeddings, utility scores, the
|
|
1169
|
+
enrichment cache and the graph are never dropped, and only a corrupt file
|
|
1170
|
+
is rebuilt from scratch (#865). Both FTS5 tables are contentless, so
|
|
1171
|
+
indexed text is stored once (153 MB of a 980 MB index on a 23.9k-entry
|
|
1172
|
+
stash; a SQLite older than 3.43 keeps the previous layout). Each vector
|
|
1173
|
+
records its model (`embeddings.model`): a model change re-embeds only the
|
|
1174
|
+
entries missing a vector for the configured model, per batch and
|
|
1175
|
+
resumably, replacing the purge, the #955 re-embed canary and
|
|
1176
|
+
`embedding_salvage`. `akm index --full` keeps unchanged entries' vectors,
|
|
1177
|
+
and a one-file change in a large directory re-persists only that file.
|
|
1178
|
+
Readers serve an older layout as-is and say so once on stderr. An akm
|
|
1179
|
+
older than this release refuses a layout-24 index and asks to be upgraded.
|
|
1180
|
+
- **`--verbose` embedding output lists each document's size without a
|
|
1181
|
+
predicted batch number.** The per-batch lines already report every
|
|
1182
|
+
provider request's document and token counts, and skipped documents are
|
|
1183
|
+
listed at the end of the pass.
|
|
1184
|
+
- **Workflow runs are never refused for their plan's version or hash.**
|
|
1185
|
+
Markdown and the GitHub-shaped YAML subset compile straight to one plan
|
|
1186
|
+
type, and new runs record plan `irVersion` 6. A stored plan that decodes
|
|
1187
|
+
runs whatever release froze it — irVersion 4 and 5 plans are read
|
|
1188
|
+
tolerantly, and a key this release does not know is ignored instead of
|
|
1189
|
+
abandoning the run; one that does not decode is marked abandoned and `akm
|
|
1190
|
+
workflow run <ref>` starts afresh; only a plan a newer akm froze is
|
|
1191
|
+
refused, with "Upgrade akm" (`WORKFLOW_IR_VERSION_UNSUPPORTED` is gone).
|
|
1192
|
+
One driver per run is a lock file,
|
|
1193
|
+
`<data dir>/workflow-run-locks/<run id>.lock`: a second `akm workflow run`
|
|
1194
|
+
exits 75 (`RUN_LEASE_HELD`) naming the holder's pid, and a dead pid's lock
|
|
1195
|
+
is reclaimed at once — the database run lease, its heartbeat and the
|
|
1196
|
+
check-ins are gone. Resume reuses every completed unit whatever its
|
|
1197
|
+
recorded input hash, and warns once when the workflow file's sha256
|
|
1198
|
+
differs from the one recorded at freeze, then continues on the frozen
|
|
1199
|
+
plan. Executable identity (realpath, inode and hash captured at freeze,
|
|
1200
|
+
checked at dispatch) is gone, so upgrading `claude` mid-run no longer
|
|
1201
|
+
strands a run.
|
|
1202
|
+
- **Every execution goes through three plain functions:** `resolveExecution`
|
|
1203
|
+
→ `buildExecution` → `runExecution` (`src/integrations/agent/execution.ts`,
|
|
1204
|
+
`runner-dispatch.ts`), replacing a 12-hop pipeline across 14 modules — the
|
|
1205
|
+
cascade planner, authorized-plan and provenance checks, lowerer registry
|
|
1206
|
+
and dispatch lease. Two behaviour changes: credentials are read at each
|
|
1207
|
+
dispatch, so a key rotated mid-run is used on the next call instead of a
|
|
1208
|
+
snapshot taken at the start; and an explicit `engine: null` in a task,
|
|
1209
|
+
workflow or command layer means "no preference here" and falls through to
|
|
1210
|
+
`defaults.engine` instead of forcing the `opencode-sdk` fallback.
|
|
1211
|
+
- **`state.db` opens on one connection.** The open creates the parent
|
|
1212
|
+
directory, opens the file, applies the pragmas, reads the migration ledger
|
|
1213
|
+
and runs every pending migration in one `BEGIN IMMEDIATE`; the read-only
|
|
1214
|
+
preflight connection, the `/proc/self/fd` alias and the refusal of an empty
|
|
1215
|
+
"unversioned" file are gone. Before a migration that drops schema runs on
|
|
1216
|
+
an existing database, it is copied to `state.db.pre-<id>.bak`. Since any
|
|
1217
|
+
open applies pending migrations, `akm health`'s `state-db-migrations` check
|
|
1218
|
+
now reports what its own open applied (`evidence.applied`,
|
|
1219
|
+
`evidence.backupPath`) and fails only when a migration could not be
|
|
1220
|
+
applied.
|
|
1221
|
+
- **`akm health` drops checks nothing acted on.** Removed: the
|
|
1222
|
+
`task-log-backing` hard check, the `pool-saturation` advisory, the six
|
|
1223
|
+
research advisories (`outcome-proxy-adequacy`, `outcome-proxy-dead`,
|
|
1224
|
+
`salience-uniformity-collapse`, `enrichment-lane-minting`,
|
|
1225
|
+
`improve-churn-ratio`, `collapse-churn-detector`) and the report's
|
|
1226
|
+
coverage, degradation and minting rollups. The HTML report's embedded
|
|
1227
|
+
`RUNS` data drops 11 per-run counters no chart or table read (scope mode,
|
|
1228
|
+
consolidation `processed`/`failedChunks`/`totalChunks`, memory-inference
|
|
1229
|
+
`considered`/`yieldRate`, graph-extraction `failures`, distill
|
|
1230
|
+
`skipped`/`queued`/`llmFailed`, `orphansPurged`); `--group-by run` and
|
|
1231
|
+
`--format md` are unchanged.
|
|
1232
|
+
- **`configVersion` is read, never gated on.** A missing field or `"0.9.0"`
|
|
1233
|
+
loads silently; any other value is named once and read as `0.9.0`.
|
|
1234
|
+
`UNSUPPORTED_CONFIG_VERSION` and `src/core/config/config-version-shim.ts`
|
|
1235
|
+
are gone.
|
|
1236
|
+
- **`akm migrate` converts a task file in one step, whatever its version.**
|
|
1237
|
+
One planner (`scripts/akm-migrate/migrate/task-files.ts`) takes a v2, v3 or
|
|
1238
|
+
v4 file still carrying `schedule[].enabled` to v4 in one pass, with one
|
|
1239
|
+
backup directory per run (`$DATA/backups/tasks/<ts>-<uuid>`); `akm migrate
|
|
1240
|
+
status` reports one `taskFiles` section instead of
|
|
1241
|
+
`taskV3Migration`/`taskV4Migration`. The per-generation steps, their
|
|
1242
|
+
convergence checks and backup pruning, and the writer-relocation step are
|
|
1243
|
+
gone.
|
|
1244
|
+
- **Registry requests use plain `fetch()`.** DNS pinning — a Node child
|
|
1245
|
+
process per request that resolved each registry host, rejected private
|
|
1246
|
+
addresses and pinned the connection — is removed: a registry URL is the
|
|
1247
|
+
built-in one or one an operator configured. `src/registry/network.ts`
|
|
1248
|
+
retries network failures, timeouts, 429 and 5xx with backoff, caps the
|
|
1249
|
+
body, and reports every failure as a classified error, never exit 70:
|
|
1250
|
+
`REGISTRY_NOT_FOUND` and `REGISTRY_RESPONSE_INVALID` exit 1,
|
|
1251
|
+
`REGISTRY_UNREACHABLE` exits 75, `REGISTRY_URL_INVALID` exits 78. A static
|
|
1252
|
+
index whose `version` is not 2 or 3 is read with one warning instead of
|
|
1253
|
+
refused.
|
|
1254
|
+
|
|
1255
|
+
### Added
|
|
1256
|
+
|
|
1257
|
+
- **Upgrade rehearsal gate** (`tests/integration/upgrade-rehearsal/`,
|
|
1258
|
+
`AKM_UPGRADE_REHEARSAL=1`): installs the previous published `akm-cli`
|
|
1259
|
+
release as a real global npm package, drives it to build a realistic home
|
|
1260
|
+
(a filesystem, git, website, and npm bundle; scheduled and manual tasks; a
|
|
1261
|
+
synced fake crontab), then installs the candidate build OVER it in place —
|
|
1262
|
+
the same prefix a real `npm i -g`/`bun add -g` upgrade replaces — and runs
|
|
1263
|
+
the candidate against that home — `migrate status`/`apply`, `bundle list`
|
|
1264
|
+
with every bundle confirmed enabled, `search`, `show`, plain `task sync`
|
|
1265
|
+
(dry-run and real, no `--rebind`, as an upgrading user actually runs it),
|
|
1266
|
+
executing the generated cron command and confirming it ran the candidate,
|
|
1267
|
+
`health`, `improve --plan` — and finally installs a separate untouched copy
|
|
1268
|
+
of the previous release and runs it back against the candidate-written
|
|
1269
|
+
home. Wired into CI (`.github/workflows/ci.yml`'s new `upgrade-rehearsal`
|
|
1270
|
+
job) and `tests/release-check.sh` (right after packing the release
|
|
1271
|
+
candidate). `.github/workflows/ci.yml` also now runs on pushes to
|
|
1272
|
+
`release/*` branches, which previously had no CI coverage at all.
|
|
1273
|
+
It also proves the fix for the defect above (Fixed, below) two
|
|
1274
|
+
ways: a new first assertion in the "previous"-origin suite runs
|
|
1275
|
+
scheduled-a's generated cron command BEFORE any `migrate` call and
|
|
1276
|
+
confirms `akm-migrate status --host-local` then reports `current` with no
|
|
1277
|
+
manual step in between; and a second, dedicated origin,
|
|
1278
|
+
`KNOWN_UPGRADE_ORIGINS`' fixed `"0.9.15"` (the last release before
|
|
1279
|
+
source-bound scheduler grants), builds a minimal home whose crontab row
|
|
1280
|
+
carries no host-local grant at all — the exact 2026-09-24 shape — and
|
|
1281
|
+
confirms the candidate carries the grant forward and a plain `task sync`
|
|
1282
|
+
afterward does not remove it.
|
|
1283
|
+
- **`akm bundle rename <old> <new>`.** Renaming a bundle used to mean
|
|
1284
|
+
hand-editing the `bundles` key in `config.json`, which stranded every
|
|
1285
|
+
durable ref the tool had minted under the old id — the index and state
|
|
1286
|
+
databases kept the old `<old>//` prefix while config named the new one
|
|
1287
|
+
(the exact hand-rename signature `warnOnBundleRenameDrift` already
|
|
1288
|
+
detected and warned about, with "there is no rekey command in 0.9.0").
|
|
1289
|
+
`akm bundle rename` is that command: under the config lock it rewrites the
|
|
1290
|
+
`bundles` key, `defaultBundle`/`defaultWriteTarget` when they name the old
|
|
1291
|
+
id, and every `scheduler.enabled[].ref` with the old `//` prefix; then it
|
|
1292
|
+
renames the lockfile entry, re-keys every indexed entry's
|
|
1293
|
+
`bundle_id`/`item_ref` and the metadata-enrichment LLM cache's
|
|
1294
|
+
`asset_ref` (in the same `index.db` write, so a rename can't land between
|
|
1295
|
+
the two and strand the cache — the next `akm index` would otherwise treat
|
|
1296
|
+
every renamed asset as stale and re-enrich it through the LLM from
|
|
1297
|
+
scratch), and rewrites this tool's own state rows that name the old bundle
|
|
1298
|
+
(`proposals.ref`, a pending proposal's `proposedTarget.source`, and
|
|
1299
|
+
workflow `task_history.target_ref`). It then re-syncs native scheduler
|
|
1300
|
+
rows under the new name (`akmTasksSync`, run from the command handler and
|
|
1301
|
+
reported in the result's `taskSync` field, never thrown, since
|
|
1302
|
+
config/index/state are already renamed by then), so a scheduled task or
|
|
1303
|
+
workflow stops invoking `<old>//…` the moment the rename applies instead of
|
|
1304
|
+
waiting on a manual `akm task sync`. `taskSync.ok` is `false` both when
|
|
1305
|
+
the sync call itself fails and when it comes back with one or more
|
|
1306
|
+
`taskSync.result.failures` — a binding that failed to prepare has already
|
|
1307
|
+
lost its old native row and is not scheduled again until a retry, so
|
|
1308
|
+
`akm bundle rename` never reports a partial re-sync as a clean one. Refs
|
|
1309
|
+
inside the bundle's own CONTENT
|
|
1310
|
+
(cross-references, a task's `uses:`, `supersededBy`) are reported, never
|
|
1311
|
+
rewritten — the result's `contentRefs` lists the indexed files that still
|
|
1312
|
+
spell the old prefix. `--dry-run` shows the full plan (row counts,
|
|
1313
|
+
scheduler refs, content files, and the installed native scheduler rows a
|
|
1314
|
+
real run's sync would replace) without writing anything.
|
|
1315
|
+
|
|
1316
|
+
### Removed
|
|
1317
|
+
|
|
1318
|
+
- **Every ranking signal besides the two fused lists.** Search no longer
|
|
1319
|
+
applies exact-name tiers, type, belief-state, tag, search-hint, alias,
|
|
1320
|
+
description, metadata, graph, capture-mode, lesson-strength, pinned-fact or
|
|
1321
|
+
project-context boosts, the utility multiplier, the relaxed-query score
|
|
1322
|
+
ceiling, or the cosine floor on vector-only hits, and it no longer loads
|
|
1323
|
+
the graph snapshot. On the retrieval suite plain whole-document BM25 alone
|
|
1324
|
+
beat the boosted pipeline by 0.156 nDCG@10, and applying the belief-state
|
|
1325
|
+
weights to the fused score lowered nDCG@10 by 0.010 [−0.020, −0.001], so
|
|
1326
|
+
`--belief current` is the way to leave out contradicted or superseded
|
|
1327
|
+
entries. Usage events and utility scores are still recorded (improve's
|
|
1328
|
+
salience and graph extraction read them), and the graph still backs
|
|
1329
|
+
`akm show`'s `related` list and curate's support refs.
|
|
1330
|
+
- **The require-every-word keyword ladder and prefix matching.** The strict
|
|
1331
|
+
AND query, its prefix-AND retry and the OR recovery behind them are gone
|
|
1332
|
+
(OR matching measured 0.108 nDCG@10 better), so a word fragment such as
|
|
1333
|
+
`dock` no longer matches `docker`.
|
|
1334
|
+
- **Fragment hits in search.** Markdown fragments no longer compete as search
|
|
1335
|
+
candidates (whole documents measured 0.059 nDCG@10 better), so search
|
|
1336
|
+
returns whole-document refs and its hits drop `selectedRef`, `parentRef`,
|
|
1337
|
+
`fragmentOrdinal`, `fragmentCount`, the fragment line and size fields and
|
|
1338
|
+
`matchStage`; `akm show <ref>#<fragment>` still selects a section.
|
|
1339
|
+
- **Curate's second-guessing of search:** the per-keyword fallback searches
|
|
1340
|
+
and their max-score merge, the intent and type nudges, skill-family
|
|
1341
|
+
collapse (and the family support refs it produced), and the close-score
|
|
1342
|
+
comparator.
|
|
1343
|
+
- **Retired options.** `akm search --no-project-context` now fails as an
|
|
1344
|
+
unknown flag (exit 2). The config keys `search.minScore`,
|
|
1345
|
+
`search.graphBoost.*` and `improve.utilityDecay.*` have no effect and are
|
|
1346
|
+
kept as unknown keys. Search hits no longer carry the `graph` field, and
|
|
1347
|
+
usage events no longer record `graphExtraction` attribution.
|
|
1348
|
+
- **Guarded source reads around workflow runs.** `akm workflow run` no
|
|
1349
|
+
longer records a read set of every source it touched or re-checks those
|
|
1350
|
+
sources before publishing the run, so editing a command, task, script or
|
|
1351
|
+
env file while a run is being created no longer fails creation; a source
|
|
1352
|
+
that resolves outside its bundle is still refused. `akm workflow plan` no
|
|
1353
|
+
longer prints a `read set:` block, and its JSON drops `sourceReadSet`. At
|
|
1354
|
+
dispatch an env file is re-read from its recorded path (a changed key set
|
|
1355
|
+
is still refused), so replacing or re-cloning the bundle directory no
|
|
1356
|
+
longer fails a unit with "environment owner root physical identity
|
|
1357
|
+
changed". The resume check that refused a run whose stored params row had
|
|
1358
|
+
been edited is gone.
|
|
1359
|
+
- **Drain policies.** `processes.triage.policy` and
|
|
1360
|
+
`processes.triage.maxDiffLines` (config) and `akm proposal drain --policy`
|
|
1361
|
+
/ `--max-diff-lines` are retired with `drain-policies.ts`; the flags now
|
|
1362
|
+
fail as unknown (exit 2) and the keys are kept as unknown config keys.
|
|
1363
|
+
- **Improve machinery with no remaining reader:** the collapse detector with
|
|
1364
|
+
its canary set (`scripts/refresh-canary-set.ts`) and cycle metrics, replay
|
|
1365
|
+
selection, the outcome-proxy events, and the never-called anti-collapse
|
|
1366
|
+
merge guards. Retired config keys (kept as unknown keys):
|
|
1367
|
+
`processes.consolidate.antiCollapse.{maxGeneration, lexicalDiversityCheck,
|
|
1368
|
+
mergeInformationFloor, minSpecificityRetention}`,
|
|
1369
|
+
`processes.consolidate.contradictionDetection`,
|
|
1370
|
+
`improve.salience.replayBudget` and `improve.collapseDetector`. Retired
|
|
1371
|
+
events: `improve_salience_first_run`, `improve_replay_selected`,
|
|
1372
|
+
`collapse_detector_alert`, `improve_cycle_metrics_purged`,
|
|
1373
|
+
`outcome_proxy_dead` and `outcome_proxy_inverted`.
|
|
1374
|
+
|
|
1375
|
+
### Fixed
|
|
1376
|
+
|
|
1377
|
+
- **Re-extracting an unchanged note now replaces its stored graph rows.**
|
|
1378
|
+
`replaceStoredGraph` refreshed only a file's status, reason and run id when
|
|
1379
|
+
its body hash was unchanged, so an extraction of the same body — after a
|
|
1380
|
+
model or prompt change, or after a failed first attempt — never reached
|
|
1381
|
+
`graph_file_entities` or `graph_file_relations`. One install had 1,389 files
|
|
1382
|
+
marked `extracted` with no entity rows while `llm_enrichment_cache` held
|
|
1383
|
+
their extractions. A file's rows are now rewritten whenever its entities or
|
|
1384
|
+
relations differ from the stored ones, so the next graph pass refills such
|
|
1385
|
+
files from the cache without a model call. (`src/indexer/db/graph-db.ts`)
|
|
1386
|
+
- **A graph pass that stops early no longer shrinks the stored graph.** A
|
|
1387
|
+
full scan wrote back only the files it reached, so a budget abort, a
|
|
1388
|
+
failure-rate abort or `processes.graphExtraction.topN` deleted the stored
|
|
1389
|
+
rows of every other file: the 2026-09-26 backfill hit its 4 h budget after
|
|
1390
|
+
3,358 of 15,165 eligible files, and that prefix became the whole graph. The
|
|
1391
|
+
pass now keeps the rows of every eligible file it did not reach, and of a
|
|
1392
|
+
file whose extraction attempt failed. It drops rows only for a file that
|
|
1393
|
+
left the eligible set — deleted, emptied, now `inferred: true`, or of a type
|
|
1394
|
+
no longer included — which candidate-scoped runs never did; a scan that
|
|
1395
|
+
could not read part of the stash drops nothing. Because kept rows can come
|
|
1396
|
+
from an older extractor, the sweep no longer reuses a stored graph node as a
|
|
1397
|
+
cache hit: only `llm_enrichment_cache`, keyed by extractor, answers for the
|
|
1398
|
+
current one. (`src/indexer/graph/graph-extraction.ts`)
|
|
1399
|
+
- **`graph_meta` counts describe the stored rows.** The extraction pass
|
|
1400
|
+
wrote counts from its in-memory graph (22,304 entities reported against
|
|
1401
|
+
15,833 stored on one install), and deleting entries overwrote them with raw
|
|
1402
|
+
row counts. Each write now derives them from the stored rows, one meaning
|
|
1403
|
+
each: stored files, files with entity rows, distinct case-folded entities and
|
|
1404
|
+
distinct case-folded relations. The pass result, and with it the
|
|
1405
|
+
`akm improve` summary, reports the same counts. The entries-delete recompute
|
|
1406
|
+
and the in-memory graph deduplicator (`src/indexer/graph/graph-dedup.ts`)
|
|
1407
|
+
are gone. (`src/indexer/db/graph-db.ts`,
|
|
1408
|
+
`src/indexer/graph/graph-extraction.ts`,
|
|
1409
|
+
`src/storage/repositories/index-entries-repository.ts`)
|
|
1410
|
+
- **`akm health` counts graph-extracted files per run.** Its
|
|
1411
|
+
`graphExtraction.extractedFiles` added the whole stored graph's file count
|
|
1412
|
+
once per improve run in the window; it now adds the files each run
|
|
1413
|
+
extracted, as `entities` and `relations` already did.
|
|
1414
|
+
(`src/commands/health/improve-metrics.ts`)
|
|
1415
|
+
- **`akm show`'s `related` refs no longer depend on index row order.** When
|
|
1416
|
+
two entries index the same file, the ref shown for it was whichever row
|
|
1417
|
+
SQLite returned last; the lowest concept id now wins, and shared entity
|
|
1418
|
+
names are read in a fixed order. The ranking itself (most shared entities,
|
|
1419
|
+
then path) was already deterministic. (`src/indexer/graph/graph-related.ts`)
|
|
1420
|
+
- **`akm search`/`akm curate` no longer store a pasted credential verbatim in
|
|
1421
|
+
`state.db`.** The Claude Code hook curates every user prompt, so a
|
|
1422
|
+
credential pasted into a prompt (`PASSWORD=…`, `TOKEN=…`, `SECRET=…`, a
|
|
1423
|
+
`Bearer` header, a JWT, a `ghp_…`/`xox…`/`AKIA…` token, a PEM private key, a
|
|
1424
|
+
`user:pass@` URL, …) flowed straight into the query text and was persisted
|
|
1425
|
+
as-is in both `usage_events.query` and the `events` table's
|
|
1426
|
+
`metadata_json` — a scan of mined queries found 22 credential-like values
|
|
1427
|
+
stored this way. `logSearchEvent`/`logCurateEvent` now redact the query
|
|
1428
|
+
with `redactCredentialPatterns` (extended with the shapes above, plus a
|
|
1429
|
+
`NAME=value`/`NAME: value` pass for names containing password, passwd,
|
|
1430
|
+
secret, token, auth, credential(s), or an api/private key — the value is
|
|
1431
|
+
replaced with `[REDACTED]`, the name is kept so queries stay useful for
|
|
1432
|
+
evaluation) before either write, so a `show`/`select` event tracing back to
|
|
1433
|
+
the search — which copies the search event's already-persisted `query`
|
|
1434
|
+
metadata — inherits the same redacted text. Existing rows already written
|
|
1435
|
+
are not rewritten. (`src/core/redaction.ts`, `src/commands/read/search.ts`,
|
|
1436
|
+
`src/commands/read/curate.ts`)
|
|
1437
|
+
- **`engines.<name>.supportsJsonSchema` on a `kind: "llm"` engine is a known
|
|
1438
|
+
key again.** `LlmConnectionConfigSchema` declares it and `llm/client.ts`
|
|
1439
|
+
reads it, but the named-engine object (`LlmEngineSchema`) never listed it,
|
|
1440
|
+
so this release's unknown-key walk named it on every load and `akm migrate
|
|
1441
|
+
apply` would have deleted a live setting from config.json.
|
|
1442
|
+
(`src/core/config/schema/engines.ts`)
|
|
1443
|
+
- **`akm migrate` finds the leaked activity registry where earlier releases
|
|
1444
|
+
actually wrote it.** The `deadResidue` step looked for
|
|
1445
|
+
`maintenance-activities/` under `$STATE`; the maintenance barrier created it
|
|
1446
|
+
next to its own lock under `$DATA`, so the directory that had grown to
|
|
1447
|
+
229,943 four-kilobyte sidecars (927 MB) on one host was never reported or
|
|
1448
|
+
removed. Both roots are checked, the registry is reported as one entry rather
|
|
1449
|
+
than once per sidecar, and a directory already listed whole is not descended
|
|
1450
|
+
into by the sidecar scan. (`scripts/akm-migrate/migrate/dead-residue.ts`)
|
|
1451
|
+
- **`akm bundle add`'s `--name` is now a contract on every add path (local,
|
|
1452
|
+
website, registry), not a hint.** An explicit `--name` that is not a legal
|
|
1453
|
+
bundle slug, or that is already taken by a different bundle, used to fall
|
|
1454
|
+
back silently — `deriveBundleId` minted a derived name, or a `-<hash>`
|
|
1455
|
+
suffix — so `akm bundle add ... --name my.bundle` installed under a name
|
|
1456
|
+
the caller never asked for, without saying so. It now fails with a
|
|
1457
|
+
`UsageError` (exit 2) naming the rule, before any write (config, lock, or
|
|
1458
|
+
network sync). Re-adding an already-installed ref under a *different*
|
|
1459
|
+
`--name` than it already carries used to keep the existing key and say
|
|
1460
|
+
nothing; it now fails the same way, naming the existing key and
|
|
1461
|
+
`akm bundle rename <old> <new>`. A DERIVED name (no `--name` given) is
|
|
1462
|
+
unaffected and keeps `deriveBundleId`'s forgiving `-<hash>` uniqueness
|
|
1463
|
+
fallback. Every `akm bundle add` result (local, website, and registry) now
|
|
1464
|
+
also carries `bundleId` (the resolved bundle key), and a registry add's
|
|
1465
|
+
result always carries `registryId` (the registry install id) rather than
|
|
1466
|
+
only when it happens to differ from `bundleId`, so a caller no longer has
|
|
1467
|
+
to reconstruct the key from `sourceAdded`/`installed`.
|
|
1468
|
+
- **`akm bundle add <registry ref> --name <name>` now keys the bundle by
|
|
1469
|
+
`<name>`.** For npm, `github:` and Git refs, `--name` was accepted and then
|
|
1470
|
+
dropped before the bundle key was derived, so the bundle was keyed by the
|
|
1471
|
+
basename of its materialized cache directory instead — `extracted`, or
|
|
1472
|
+
`extracted-<hash>` once that was taken — and its assets were only
|
|
1473
|
+
addressable as `extracted//…`. The name now goes through the same
|
|
1474
|
+
slug-legality and uniqueness rules as a local or website add. The install's
|
|
1475
|
+
registry id is still recorded as `registryId`, so `akm bundle update` and
|
|
1476
|
+
`akm bundle remove` keep resolving the original ref. Re-adding a ref that is
|
|
1477
|
+
already installed keeps its existing key, as local and website re-adds do.
|
|
1478
|
+
- **A registry bundle added without `--name` is keyed by its package or repo
|
|
1479
|
+
name instead of `extracted`.** `akm bundle add npm:<pkg>` now creates bundle
|
|
1480
|
+
`<pkg>` (`npm:@scope/pkg` → `pkg`), and `github:owner/repo` or a Git URL
|
|
1481
|
+
ending in `/repo` creates `repo` — the mapping the bundle schema already
|
|
1482
|
+
documented for `registryId`. The key used to come from the basename of the
|
|
1483
|
+
cache directory the package was unpacked into, which is always `extracted`,
|
|
1484
|
+
so every registry bundle after the first was `extracted-<hash>`. A dotted
|
|
1485
|
+
or mixed-case name is slugged like a directory name (`Foo.js` → `foo-js`).
|
|
1486
|
+
Bundles that are already installed keep their current key, including
|
|
1487
|
+
`extracted`, because every recorded `extracted//…` ref depends on it.
|
|
1488
|
+
- **A one-file change in a large directory no longer costs `akm index` half
|
|
1489
|
+
an hour.** Both full-text tables keyed their per-entry deletes on
|
|
1490
|
+
`entry_id`, an unindexed FTS5 column, so every upsert scanned the whole
|
|
1491
|
+
full-text index, twice per entry per run. On a 23.9k-entry index, one
|
|
1492
|
+
touched file in a flat `knowledge/` directory of 13.7k entries took 26
|
|
1493
|
+
minutes (task run `2026-09-24T20-30-01-663Z`); on backup copies of that
|
|
1494
|
+
index the same rescan took 31 minutes before this change and takes 38 s
|
|
1495
|
+
after it. FTS rows are keyed by rowid, and the
|
|
1496
|
+
first writable open after upgrading realigns an existing index in place,
|
|
1497
|
+
about 10 s and ~1.1 GB peak memory at that size, with no index-generation
|
|
1498
|
+
bump, so an older binary keeps reading it. A writable open realigns again
|
|
1499
|
+
if an older binary sharing the generation has written rows since.
|
|
1500
|
+
- **One bad scheduler-sync item, or one bad migration step, no longer fails
|
|
1501
|
+
the whole operation.** `akm task sync` used to throw and abort the entire
|
|
1502
|
+
reconciliation over one binding it could not reconcile or one bundle whose
|
|
1503
|
+
sources failed to read; that binding or bundle is now reported in the sync
|
|
1504
|
+
result's `failures: [{path, ref?, reason}]` (documented in
|
|
1505
|
+
`docs/reference/cli.md`) while every other one still syncs (see Changed).
|
|
1506
|
+
`akm-migrate`'s
|
|
1507
|
+
`runMigration` (`scripts/akm-migrate/run-migrate.ts`) now runs every step
|
|
1508
|
+
under its own catch too: a step's own throw (or, under `apply`, its
|
|
1509
|
+
read-only fallback failing as well) is recorded in the plan's new
|
|
1510
|
+
`failedSteps: [{step, error}]` and forces `status: "blocked"` instead of
|
|
1511
|
+
ending the run with no plan at all — the remaining steps still run in
|
|
1512
|
+
order. `akm migrate status|apply` (`scripts/akm-migrate/main.ts`) already
|
|
1513
|
+
exits 1 for any blocked plan, so a poisoned step no longer exits the
|
|
1514
|
+
internal-error code 70 with nothing printed.
|
|
1515
|
+
- **The legacy `stashDir`/`sources[]`/`installed` config shape is persisted
|
|
1516
|
+
by `akm migrate apply`, and an empty one no longer fails every command**
|
|
1517
|
+
(#863). `migrateLegacySourceShape`
|
|
1518
|
+
(`src/core/config/legacy-source-shape-shim.ts`) has always converted a
|
|
1519
|
+
usable `stashDir`/`sources[]`/`installed` in memory on every load and told
|
|
1520
|
+
the user to run `akm migrate apply` to make that stick, but nothing on disk
|
|
1521
|
+
ever did; the migrator's `configFile` step now writes that current shape
|
|
1522
|
+
back once, under a backup. Separately, through 0.9.16 and 0.9.17-alpha.3 a
|
|
1523
|
+
config whose `sources` was `[]` (what 0.8.9's `akm source remove` writes
|
|
1524
|
+
after the last source is removed) or whose `stashDir` was empty or
|
|
1525
|
+
unusable failed every command with exit 78; it now loads, with the shim's
|
|
1526
|
+
one-time warning.
|
|
1527
|
+
- **A `version: 2` or `version: 3` task source reads and runs again instead
|
|
1528
|
+
of failing closed on upgrade.** `e413af024` deleted the in-memory
|
|
1529
|
+
v2/v3 -> v4 read shim on the argument that "untrusted source cannot carry
|
|
1530
|
+
obsolete activation semantics" — but activation had already moved to
|
|
1531
|
+
host-local `scheduler.enabled` in that same commit, so the shim never
|
|
1532
|
+
carried activation in the first place, and deleting it just reintroduced
|
|
1533
|
+
the exact upgrade break 0.9.4 originally shipped the shim to fix ("would
|
|
1534
|
+
have broken every pre-0.9.4 scheduled task headlessly on upgrade").
|
|
1535
|
+
`parseTaskSource` (`src/tasks/source/parse-task-source.ts`) once again
|
|
1536
|
+
routes `version: 2`/`version: 3` through the SAME pure planners
|
|
1537
|
+
`akm migrate apply` uses, entirely in memory, with a one-line stderr
|
|
1538
|
+
deprecation warning (once per file per process) and no disk write; the
|
|
1539
|
+
parsed document never carries a source-owned `enabled` field, since the
|
|
1540
|
+
v3->v4 planner already never hoists `akm.enabled` or a schedule entry's
|
|
1541
|
+
`enabled` key. Only a v2/v3 document the deterministic conversion itself
|
|
1542
|
+
cannot resolve still fails with `TASK_SCHEMA_VERSION_UNSUPPORTED`, naming
|
|
1543
|
+
the specific blocked reason. The same in-memory shim now also tolerates a
|
|
1544
|
+
declared `version: 4` document whose `schedule[]` still carries a
|
|
1545
|
+
per-entry `enabled` key — 0.9.15's v4 grammar accepted it (`akm task add
|
|
1546
|
+
--disabled` wrote it), this release's does not, and without this the
|
|
1547
|
+
upgrade break above recurs for every 0.9.15-authored scheduled task. The
|
|
1548
|
+
key is stripped without ever being read — `enabled: false` cannot
|
|
1549
|
+
suppress a granted task and `enabled: true` cannot schedule an ungranted
|
|
1550
|
+
one, since activation stays host-local `scheduler.enabled`. `akm task
|
|
1551
|
+
validate` reports such a file `converts` (`sourceVersion` still `4`)
|
|
1552
|
+
instead of `valid`, since it read through the shim rather than the direct
|
|
1553
|
+
v4 path.
|
|
1554
|
+
- **Lock contention exits 75, like `state.db` contention.** Another process
|
|
1555
|
+
holding `akm.lock`'s write sentinel (`LOCKFILE_CONTENDED`, was a config
|
|
1556
|
+
error, exit 78) or the asset-mutation writer lease past its wait
|
|
1557
|
+
(`ASSET_MUTATION_LEASE_HELD`, was an unclassified error, exit 70) is now a
|
|
1558
|
+
retry-shortly `TransientError`, exit 75.
|
|
1559
|
+
- **`akm health`'s `state.db` repair steps no longer corrupt the rebuilt
|
|
1560
|
+
file.** `state-db-integrity` used to suggest `.dump` into a new file with
|
|
1561
|
+
no writer stop; it now says to back up `state.db`, `.recover` it into
|
|
1562
|
+
`state.new.db`, confirm that passes `quick_check`, stop every akm process,
|
|
1563
|
+
delete `state.db-wal` and `state.db-shm`, then swap the new file in — a
|
|
1564
|
+
leftover WAL replays onto the new database and corrupts it.
|
|
1565
|
+
- **The package launcher (`dist/akm`) passes `--scheduler-context` through to
|
|
1566
|
+
the CLI** instead of re-validating the descriptor with a stale copy of its
|
|
1567
|
+
schema, which rejected every descriptor 0.9.17 writes.
|
|
1568
|
+
(`scripts/node-runtime/akm`)
|
|
1569
|
+
- **A `task_history` row with a malformed `engine` value decodes.** The
|
|
1570
|
+
decoder used to reject the whole row when `engine` was present but not a
|
|
1571
|
+
string or `null`; it now drops the bad value and decodes the rest, the
|
|
1572
|
+
tolerance it already applied to every other unrecognized field.
|
|
1573
|
+
(`src/storage/repositories/task-history-repository.ts`)
|
|
1574
|
+
- **The LLM enrichment budget warning prints for every index run.** When the
|
|
1575
|
+
metadata-enrichment pass ran out of its wall-clock budget during an index
|
|
1576
|
+
another command started (`akm bundle update`, `akm setup`, `akm bundle
|
|
1577
|
+
add`, improve's preflight, a read command's auto-index), it stopped
|
|
1578
|
+
silently; it now prints the same "LLM enrichment budget exceeded" warning
|
|
1579
|
+
`akm index` does.
|
|
1580
|
+
|
|
1581
|
+
## [0.9.17-alpha.3] - 2026-09-24
|
|
1582
|
+
|
|
1583
|
+
### Fixed
|
|
1584
|
+
|
|
1585
|
+
- **Unscoped `akm task sync` no longer aborts the whole host on the first
|
|
1586
|
+
bundle root that happens to contain any symlink.** `captureGuardedDirectoryManifest`
|
|
1587
|
+
threw for every symbolic directory entry it listed, even one the scheduler
|
|
1588
|
+
never reads (e.g. a third-party skill repo's `CLAUDE.md -> AGENTS.md`) —
|
|
1589
|
+
`SchedulerSourceCollector` manifests every scanned bundle's root, so one
|
|
1590
|
+
such bundle among many enabled ones failed sync entirely, dry-run included.
|
|
1591
|
+
A symlink that stays inside its bundle root is now recorded in the guarded
|
|
1592
|
+
directory manifest as its own `"symlink"` kind, identified without
|
|
1593
|
+
following it (its `readlink` text plus its no-follow `lstat` identity), so
|
|
1594
|
+
change detection still works; it is never read or descended into. A symlink
|
|
1595
|
+
sitting exactly where a task or workflow source lives (a `.yml` under
|
|
1596
|
+
`tasks/`, any `.yml` under an `akm-task` bundle, or a workflow-named file
|
|
1597
|
+
under `workflows/`) is reported as its own per-source failure — "is a
|
|
1598
|
+
symbolic source; guarded reads require a regular no-follow owner" — and its
|
|
1599
|
+
ref is not scheduled, even when a real sibling file shares that ref, while
|
|
1600
|
+
every other task and workflow still reconciles. A
|
|
1601
|
+
symlink that resolves outside the bundle root, or one that is broken and
|
|
1602
|
+
cannot be identified safely, is still refused, and a bundle root whose
|
|
1603
|
+
`tasks` or `workflows` entry is itself a symlink still refuses loudly,
|
|
1604
|
+
since that is a schedulable source location.
|
|
1605
|
+
|
|
1606
|
+
## [0.9.17-alpha.2] - 2026-09-24
|
|
1607
|
+
|
|
1608
|
+
### Fixed
|
|
1609
|
+
|
|
1610
|
+
- **A config carrying the retired `experimental.workflowEngine` key no longer
|
|
1611
|
+
fails to load.** `ExperimentalConfigSchema` moved from `.passthrough()` to
|
|
1612
|
+
`.strict()` in 0.9.16 (`cc6152e02`), after `workflowEngine` had already been
|
|
1613
|
+
removed from it in `e0655d13c`; a real config a 0.9.15 install wrote (whose
|
|
1614
|
+
passthrough still accepted the key) then failed every command with
|
|
1615
|
+
`Invalid config: experimental: Unrecognized key(s) in object: 'workflowEngine'`.
|
|
1616
|
+
The config loader now strips known-retired `experimental.*` keys in memory
|
|
1617
|
+
before validation, warning once and naming `akm migrate apply`; a genuinely
|
|
1618
|
+
unknown/misspelled key (e.g. `improveAutonomyy`) still fails closed.
|
|
1619
|
+
`akm migrate apply` removes the retired key from `config.json` on disk
|
|
1620
|
+
(with the usual backup), and `--dry-run` reports the pending removal.
|
|
1621
|
+
- **Unscoped `akm task sync` no longer crashes when an enabled website or npm
|
|
1622
|
+
bundle is configured.** The sync plan loop resolved every active source
|
|
1623
|
+
through the write-target resolver, which rejects any kind other than
|
|
1624
|
+
`filesystem`/`git` outright (writes, and therefore scheduler state, are
|
|
1625
|
+
undefined for those kinds — the same rejection `akm task enable` already
|
|
1626
|
+
hit). Unscoped sync now skips non-filesystem/git bundles when building
|
|
1627
|
+
install operations — they never carried schedulable tasks — while
|
|
1628
|
+
inactive-bundle removal/revocation still sees them. A scoped
|
|
1629
|
+
`akm task sync --bundle <website-or-npm-bundle>` now fails with a clear
|
|
1630
|
+
usage error instead of the write-target `ConfigError`.
|
|
1631
|
+
|
|
9
1632
|
## [0.9.17-alpha.1] - 2026-09-24
|
|
10
1633
|
|
|
11
1634
|
### Added
|