akm-cli 0.9.17-alpha.8 → 0.9.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +277 -1694
  2. package/STABILITY.md +9 -8
  3. package/dist/assets/hints/cli-hints-full.md +6 -7
  4. package/dist/assets/improve-strategies/catchup.json +0 -3
  5. package/dist/assets/improve-strategies/consolidate.json +0 -1
  6. package/dist/assets/improve-strategies/default.json +1 -2
  7. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -2
  8. package/dist/assets/improve-strategies/quick.json +1 -2
  9. package/dist/assets/improve-strategies/reflect-distill.json +1 -2
  10. package/dist/assets/improve-strategies/thorough.json +0 -3
  11. package/dist/assets/prompts/consolidate-pair.md +20 -0
  12. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +17 -19
  13. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -2
  14. package/dist/assets/templates/html/health.html +3 -5
  15. package/dist/cli/retired-commands.js +1 -1
  16. package/dist/cli/unknown-flags.js +24 -1
  17. package/dist/cli.js +46 -1
  18. package/dist/commands/health/archive-usage.js +92 -0
  19. package/dist/commands/health/data-dir-usage.js +25 -13
  20. package/dist/commands/health/html-report.js +1 -4
  21. package/dist/commands/health/improve-metrics.js +25 -37
  22. package/dist/commands/health/md-report.js +1 -6
  23. package/dist/commands/health/report-view-model.js +4 -14
  24. package/dist/commands/health/windows.js +0 -1
  25. package/dist/commands/health.js +13 -0
  26. package/dist/commands/improve/consolidate/continuity-check.js +137 -0
  27. package/dist/commands/improve/consolidate/pair-pass.js +791 -0
  28. package/dist/commands/improve/consolidate.js +38 -63
  29. package/dist/commands/improve/extract-prompt.js +1 -2
  30. package/dist/commands/improve/improve-cli.js +1 -1
  31. package/dist/commands/improve/improve-strategies.js +23 -5
  32. package/dist/commands/improve/improve.js +19 -30
  33. package/dist/commands/improve/ledger.js +3 -2
  34. package/dist/commands/improve/loop-stages.js +5 -85
  35. package/dist/commands/improve/memory/memory-belief.js +3 -1
  36. package/dist/commands/improve/memory/memory-improve.js +262 -11
  37. package/dist/commands/improve/planner.js +0 -5
  38. package/dist/commands/improve/preparation.js +20 -135
  39. package/dist/commands/improve/retrieval-scope.js +19 -4
  40. package/dist/commands/improve/salience.js +1 -14
  41. package/dist/commands/improve/stage.js +0 -1
  42. package/dist/commands/lint/base-linter.js +19 -11
  43. package/dist/commands/proposal/drain.js +8 -1
  44. package/dist/commands/proposal/proposal-cli.js +16 -2
  45. package/dist/commands/proposal/proposal-types.js +7 -0
  46. package/dist/commands/proposal/proposal.js +37 -6
  47. package/dist/commands/proposal/repository.js +613 -4
  48. package/dist/commands/proposal/validators/proposals.js +9 -0
  49. package/dist/commands/read/knowledge.js +3 -2
  50. package/dist/commands/read/show.js +0 -14
  51. package/dist/commands/sources/info.js +122 -18
  52. package/dist/commands/sources/stash-cli.js +23 -3
  53. package/dist/core/bundle-rename.js +1 -7
  54. package/dist/core/config/config-schema.js +8 -1
  55. package/dist/core/config/config.js +23 -48
  56. package/dist/core/config/engine-semantics.js +0 -2
  57. package/dist/core/config/schema/improve-processes.js +17 -42
  58. package/dist/core/config/schema/index-config.js +5 -25
  59. package/dist/core/file-change.js +13 -5
  60. package/dist/core/improve-result.js +22 -6
  61. package/dist/core/improve-types.js +0 -1
  62. package/dist/core/loopback.js +7 -12
  63. package/dist/core/parse.js +13 -16
  64. package/dist/core/state/migrations.js +15 -0
  65. package/dist/core/time.js +0 -20
  66. package/dist/indexer/db/llm-cache.js +2 -2
  67. package/dist/indexer/ensure-index.js +2 -2
  68. package/dist/indexer/index-written-assets.js +2 -3
  69. package/dist/indexer/indexer.js +18 -418
  70. package/dist/indexer/passes/metadata.js +0 -19
  71. package/dist/indexer/walk/walker.js +3 -4
  72. package/dist/llm/client.js +8 -10
  73. package/dist/llm/embedders/remote.js +1 -2
  74. package/dist/llm/feature-gate.js +0 -5
  75. package/dist/output/shapes/helpers.js +20 -4
  76. package/dist/output/text/command-format.js +9 -8
  77. package/dist/output/text/proposal-format.js +47 -1
  78. package/dist/output/text/show-format.js +0 -20
  79. package/dist/scripts/akm-migrate-node.js +923 -950
  80. package/dist/scripts/akm-migrate.js +923 -950
  81. package/dist/setup/steps/connection.js +5 -6
  82. package/dist/setup/steps/platforms.js +2 -2
  83. package/dist/sources/providers/git-stash.js +83 -4
  84. package/dist/storage/repositories/improve-ledger-repository.js +48 -7
  85. package/dist/storage/repositories/index-connection.js +5 -2
  86. package/dist/storage/repositories/index-entries-repository.js +4 -7
  87. package/dist/storage/repositories/index-entry-schema.js +4 -2
  88. package/dist/storage/repositories/index-llm-cache-repository.js +7 -26
  89. package/dist/storage/repositories/index-schema.js +55 -104
  90. package/dist/storage/repositories/proposals-repository.js +61 -0
  91. package/dist/storage/repositories/salience-repository.js +1 -19
  92. package/docs/migration/README.md +1 -1
  93. package/docs/migration/release-notes/0.9.17.md +130 -41
  94. package/docs/migration/release-notes/README.md +7 -0
  95. package/docs/reference/cli.md +27 -21
  96. package/docs/reference/configuration.md +21 -12
  97. package/docs/reference/data-and-telemetry.md +0 -1
  98. package/package.json +1 -1
  99. package/schemas/akm-config.json +0 -342
  100. package/dist/assets/improve-strategies/graph-refresh.json +0 -15
  101. package/dist/assets/prompts/contradiction-judge.md +0 -33
  102. package/dist/assets/prompts/graph-extract-system.md +0 -1
  103. package/dist/assets/prompts/graph-extract-user-prompt.md +0 -35
  104. package/dist/assets/prompts/metadata-enhance-system.md +0 -1
  105. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +0 -4
  106. package/dist/indexer/db/graph-db.js +0 -399
  107. package/dist/indexer/graph/graph-extraction.js +0 -809
  108. package/dist/indexer/graph/graph-related.js +0 -131
  109. package/dist/indexer/graph/graph-types.js +0 -4
  110. package/dist/llm/graph-extract.js +0 -892
  111. package/dist/llm/metadata-enhance.js +0 -95
@@ -168,6 +168,50 @@ function validatePresentMetadata(meta) {
168
168
  if (Object.hasOwn(meta, "eligibilitySource") && typeof meta.eligibilitySource !== "string") {
169
169
  invalidPresentField("eligibilitySource");
170
170
  }
171
+ if (Object.hasOwn(meta, "promotionSource") && typeof meta.promotionSource !== "string") {
172
+ invalidPresentField("promotionSource");
173
+ }
174
+ if (Object.hasOwn(meta, "promotionSourceHash") && typeof meta.promotionSourceHash !== "string") {
175
+ invalidPresentField("promotionSourceHash");
176
+ }
177
+ if (Object.hasOwn(meta, "retirement")) {
178
+ const retirement = meta.retirement;
179
+ if (typeof retirement !== "object" ||
180
+ retirement === null ||
181
+ typeof retirement.retiredRef !== "string" ||
182
+ typeof retirement.successorRef !== "string" ||
183
+ typeof retirement.cosine !== "number" ||
184
+ !Number.isFinite(retirement.cosine) ||
185
+ (retirement.judgeLabel !== "duplicate" &&
186
+ retirement.judgeLabel !== "subsumed" &&
187
+ retirement.judgeLabel !== "supersedes") ||
188
+ typeof retirement.judgeReason !== "string" ||
189
+ typeof retirement.retiredContentHash !== "string" ||
190
+ typeof retirement.successorContentHash !== "string" ||
191
+ (retirement.reason !== "duplicate" && retirement.reason !== "subsumed" && retirement.reason !== "superseded")) {
192
+ invalidPresentField("retirement");
193
+ }
194
+ }
195
+ if (Object.hasOwn(meta, "retiredArchive")) {
196
+ const archive = meta.retiredArchive;
197
+ if (typeof archive !== "object" ||
198
+ archive === null ||
199
+ !Array.isArray(archive.dirs) ||
200
+ archive.dirs.length === 0 ||
201
+ archive.dirs.some((dir) => typeof dir !== "string" || dir.length === 0)) {
202
+ invalidPresentField("retiredArchive");
203
+ }
204
+ }
205
+ if (Object.hasOwn(meta, "retireAcceptIntent")) {
206
+ const intent = meta.retireAcceptIntent;
207
+ if (typeof intent !== "object" ||
208
+ intent === null ||
209
+ typeof intent.assetPath !== "string" ||
210
+ intent.assetPath.length === 0 ||
211
+ typeof intent.backupContent !== "string") {
212
+ invalidPresentField("retireAcceptIntent");
213
+ }
214
+ }
171
215
  }
172
216
  /**
173
217
  * Convert a raw `ProposalRow` to the public `Proposal` shape.
@@ -233,6 +277,13 @@ export function proposalRowToProposal(row) {
233
277
  ...(typeof meta.eligibilitySource === "string"
234
278
  ? { eligibilitySource: meta.eligibilitySource }
235
279
  : {}),
280
+ ...(typeof meta.promotionSource === "string" ? { promotionSource: meta.promotionSource } : {}),
281
+ ...(typeof meta.promotionSourceHash === "string" ? { promotionSourceHash: meta.promotionSourceHash } : {}),
282
+ ...(meta.retirement !== undefined ? { retirement: meta.retirement } : {}),
283
+ ...(meta.retiredArchive !== undefined ? { retiredArchive: meta.retiredArchive } : {}),
284
+ ...(meta.retireAcceptIntent !== undefined
285
+ ? { retireAcceptIntent: meta.retireAcceptIntent }
286
+ : {}),
236
287
  };
237
288
  }
238
289
  /**
@@ -291,6 +342,16 @@ export function proposalToRowValues(proposal, stashDir) {
291
342
  metaObj.acceptedTarget = proposal.acceptedTarget;
292
343
  if (proposal.eligibilitySource !== undefined)
293
344
  metaObj.eligibilitySource = proposal.eligibilitySource;
345
+ if (proposal.promotionSource !== undefined)
346
+ metaObj.promotionSource = proposal.promotionSource;
347
+ if (proposal.promotionSourceHash !== undefined)
348
+ metaObj.promotionSourceHash = proposal.promotionSourceHash;
349
+ if (proposal.retirement !== undefined)
350
+ metaObj.retirement = proposal.retirement;
351
+ if (proposal.retiredArchive !== undefined)
352
+ metaObj.retiredArchive = proposal.retiredArchive;
353
+ if (proposal.retireAcceptIntent !== undefined)
354
+ metaObj.retireAcceptIntent = proposal.retireAcceptIntent;
294
355
  validatePresentMetadata(metaObj);
295
356
  return {
296
357
  id: proposal.id,
@@ -58,23 +58,6 @@ export function getAssetSalience(db, ref) {
58
58
  // Bun SQLite returns null (not undefined) when no row found.
59
59
  return row == null ? undefined : row;
60
60
  }
61
- /**
62
- * Load ALL rank scores from the asset_salience table (full-stash query).
63
- *
64
- * Used by the forgetting-safety report (plan §WS-1 step 7) to compute stash-wide
65
- * rank positions rather than pool-relative positions. Returns an empty Map when the
66
- * table is empty (first WS-1 run = no pre-existing rows).
67
- *
68
- * Order is unspecified; callers must sort before assigning 1-indexed positions.
69
- */
70
- export function getAllRankScores(db) {
71
- const rows = db.prepare("SELECT asset_ref, rank_score FROM asset_salience").all();
72
- const result = new Map();
73
- for (const row of rows) {
74
- result.set(row.asset_ref, row.rank_score);
75
- }
76
- return result;
77
- }
78
61
  // ── Plasticity helpers ────────────────────────────────────────────────────────
79
62
  /**
80
63
  * Increment `consecutive_no_ops` for an asset. Called after a no-op reflect/distill.
@@ -84,8 +67,7 @@ export function getAllRankScores(db) {
84
67
  * Invariant: recordNoOp must never originate rank_score semantics. If the asset has
85
68
  * no salience row yet (persistence's best-effort try/catch may have swallowed an
86
69
  * error), we do nothing — a no-op counter is meaningless without a rank_score row,
87
- * and a synthetic INSERT would fabricate a rank_score=0 entry that could produce
88
- * false catastrophic-forgetting signals in buildRankChangeReport.
70
+ * and a synthetic INSERT would fabricate a rank_score=0 entry with nothing behind it.
89
71
  */
90
72
  export function recordNoOp(db, ref) {
91
73
  db.prepare(`UPDATE asset_salience SET consecutive_no_ops = consecutive_no_ops + 1, updated_at = ? WHERE asset_ref = ?`).run(Date.now(), ref);
@@ -5,7 +5,7 @@ Upgrade guides and per-release migration notes.
5
5
  - [v0.9.1 -> v0.9.2 migration guide](v0.9.1-to-v0.9.2.md) -- Task-v2/task-v3 to task source v4 conversion, the durable-v4-family workflow boundary at executable `irVersion: 5`, and release behavior changes
6
6
  - [v0.9.2 release note](release-notes/0.9.2.md) -- Self-contained terminal upgrade summary shipped for `akm help migrate 0.9.2`
7
7
  - [v0.9.16 release note](release-notes/0.9.16.md) -- Source-bound scheduler grants, local execution authority, and split unsafe overrides
8
- - [v0.9.17 release note](release-notes/0.9.17.md) -- Tolerant readers, one config migration step, a plain scheduler list, less machinery, and the upgrade rehearsal gate
8
+ - [v0.9.17 release note](release-notes/0.9.17.md) -- Consolidate's retire proposals and index layout 26, replacing the LLM entity graph with declared links, and `akm improve` scoped to what retrieval actually returns
9
9
  - [v0.8 -> current v0.9 migration guide](v0.8-to-v0.9.md) -- Package upgrade with fresh current config/state and explicit task conversion
10
10
  - [v0.7 -> v0.8 migration guide](v0.7-to-v0.8.md) -- Task schema and 0.8-era changes
11
11
  - [v0.5 -> v0.6 migration guide](https://github.com/itlackey/akm/blob/main/docs/migration/v0.5-to-v0.6.md) -- Terminology cut, registry schema v3, publisher changes
@@ -1,43 +1,132 @@
1
1
  Migration notes for akm v0.9.17
2
2
 
3
- Upgrading no longer needs a manual step to keep working, and there is less
4
- machinery to break.
5
-
6
- Readers tolerate what older releases wrote, except task sources. A
7
- `version: 2` or `version: 3` task source fails on its own with
8
- `TASK_SCHEMA_VERSION_UNSUPPORTED`, naming `akm migrate apply`, which converts
9
- it to `version: 4` once; the other tasks keep running. A config key this
10
- release does not know -- retired, misspelled, or written by a newer release --
11
- is kept in memory and named once, never a reason to refuse the config;
12
- ordinary writes round-trip it, and `akm migrate apply` drops it.
13
- `akm migrate apply` has one config step (`configFile`): read config.json
14
- through the same pipeline every load runs and write the current shape back,
15
- under a backup.
16
-
17
- Scheduling is one list. `scheduler.enabled` in config.json holds the
18
- fully-qualified refs this host schedules (`bundle//tasks/x`). The
19
- 0.9.17-alpha `{kind, ref, sourceId}` grant objects are read as their ref. A
20
- config with no list at all -- every release before 0.9.17 -- means "keep
21
- what is installed": the first `akm task sync` (or `setup`, `task enable`,
22
- `task disable`, `task add`) after the upgrade takes the akm-written rows
23
- already in the native scheduler as the host's choice, writes the list, and
24
- says so. An explicit list, empty or not, is never second-guessed. There are
25
- no grants, no source identities, no carry-forward and no fire-time gate.
26
-
27
- Removed machinery: filesystem transaction journals for proposal accept/revert
28
- (the asset is written, then the proposal row and its event are recorded in
29
- one state.db transaction; a crash in between leaves a re-acceptable pending
30
- proposal); the maintenance barrier, its per-process activity registry (the
31
- source of the lock-sidecar leak) and the SQLite lock-operation mutex (a lock
32
- file is one `O_EXCL` create); startup version reconciliation and
33
- `--host-local`; the akm-install enumerator and `akm upgrade --version/--tag`;
34
- the upgrade and transaction health advisories; the `akm info` compat
35
- manifest. `akm migrate apply` removes the files those left under `$DATA`,
36
- `$STATE` and `$CONFIG`.
37
-
38
- Every historical shape akm has written is exercised by the upgrade rehearsal
39
- (`tests/integration/upgrade-rehearsal/`) before each release: a real previous
40
- release builds a home, the candidate installs over it in place, and the
41
- rehearsal drives migrate, bundle list, search, show, task sync, health and a
42
- scheduled task's generated cron command, then confirms the previous release
43
- still runs against the candidate-written home.
3
+ Run this after upgrading:
4
+
5
+ ```sh
6
+ akm migrate apply
7
+ akm task sync
8
+ ```
9
+
10
+ akm migrate apply reads config.json through the normal load pipeline and
11
+ writes its current shape back under a backup, dropping every retired key it
12
+ finds -- run it with --dry-run first to see the exact list for your config.
13
+ From 0.9.16, that includes index.graph.*, index.metadataEnhance,
14
+ search.minScore, search.graphBoost.*, improve.utilityDecay.*, and a dozen
15
+ smaller ones. None of them do anything any more -- leaving them in
16
+ config.json is harmless until you run migrate apply, which drops them for
17
+ good. It also deletes files nothing reads any more: 0.9.16's transaction
18
+ journals ($DATA/txn/ and $DATA/txn-quarantine/), its maintenance barrier
19
+ lock, and the maintenance-activities/ registry, which leaked an entry per
20
+ process and reached 927 MB on one host. It also drops a leftover
21
+ improve.strategies["graph-refresh"] override block, but not
22
+ defaults.improveStrategy: "graph-refresh" itself -- if you set that, change
23
+ it to a different strategy by hand, since graph-refresh now refuses
24
+ outright, naming the retirement, rather than quietly running as a no-op. The
25
+ shipped akm-graph-refresh-weekly task template is gone; remove or repoint
26
+ any task that used it. Separately, akm search --no-project-context and akm
27
+ proposal drain --policy/--max-diff-lines now fail as unknown flags; drop
28
+ them from any script or task that still passes them.
29
+
30
+ The first akm index after upgrading migrates index.db to layout 26 in
31
+ place -- for a 0.9.16 index this includes a one-time full-text rebuild it
32
+ was already due for, so budget a few seconds for a 24k-entry index rather
33
+ than a fraction of one. This also drops the LLM entity-graph tables -- the
34
+ per-file entity/relation extraction that fed akm show's old related list is
35
+ retired -- and ends with a VACUUM that reclaims the space both steps free;
36
+ akm index has never VACUUMed index.db before, so this is the first time it
37
+ happens at all, not a repeat of something 0.9.16 already did. Nothing is
38
+ re-embedded by this migration.
39
+
40
+ Embeddings are a separate matter if you use a nomic-embed or E5 model:
41
+ akm index now sends the model's own document template (nomic:
42
+ "search_document: ", E5: "passage: ") instead of the raw text, which changes
43
+ what gets embedded, so every entry re-embeds once on the next akm index.
44
+ Qwen3, the BGE/mxbai/arctic family, and the default local model have no
45
+ document template and keep their stored vectors untouched. Set
46
+ embedding.documentTemplate: "" if you'd rather keep the old vectors than pay
47
+ for a full re-embed. If you had index.metadataEnhance on before upgrading
48
+ (it defaulted off), run akm index --full once afterward to replace the
49
+ LLM-written descriptions it left behind with the plain deterministic ones --
50
+ an ordinary incremental akm index does not touch them.
51
+
52
+ The first akm task sync after upgrading rewrites every akm-managed
53
+ scheduler row once, in place. Each row now sets its own AKM_BUNDLE_DIR
54
+ (plus any AKM_CONFIG_DIR/AKM_DATA_DIR/AKM_CACHE_DIR/AKM_STATE_DIR the
55
+ syncing shell had set explicitly) inline, instead of pointing at a
56
+ --scheduler-context <file> descriptor, and a crontab's PATH moves the same
57
+ way, into a # akm:env block next to the task rows. The rewrite keeps each
58
+ row's existing launcher and schedule -- it is reported as an update, never
59
+ an add or a remove -- and rows written by 0.9.0 through 0.9.16 keep firing
60
+ normally until that sync runs. Once akm task doctor lists no binding still
61
+ pointing at a descriptor file, the old $DATA/tasks/context/ files can be
62
+ deleted.
63
+
64
+ akm improve now reworks only what retrieval actually returned -- an asset a
65
+ real search, curate, show, or feedback touched in the last 90 days -- plus
66
+ material no improve stage has processed yet. The proactive-maintenance and
67
+ high-salience lanes, and the memory consolidation judge, no longer reach
68
+ into the unread tail of a stash; an asset left out this way shows up under a
69
+ new retrieval gate in akm improve --dry-run and under akm health's
70
+ not_retrieved skip reason, not silently.
71
+
72
+ Consolidation gets a second pass. It compares each new-or-changed memory,
73
+ flat knowledge/ file, or lesson against its nearest neighbours, and where an
74
+ LLM judge calls a pair duplicate, subsumed, or superseding, it mints a
75
+ reviewed retire proposal for the losing side -- nothing is retired
76
+ automatically. Review these like any other proposal: akm proposal list
77
+ --generator consolidate-pair (or just akm proposal list, which flags one
78
+ carrying continuity risk inline), show, diff, then accept or reject.
79
+ Accepting one archives the losing file instead of deleting it, and akm
80
+ proposal revert restores it exactly; archived files are only deleted 30 days
81
+ after retirement, and only once git holds them clean and unmodified.
82
+ Accepting a promotion (a consolidate promote proposal, by a person or by
83
+ triage auto-promotion) now also archives the source memory it was promoted
84
+ from, so a promotion no longer leaves a duplicate sitting next to the
85
+ knowledge doc it produced.
86
+
87
+ akm proposal drain, and improve's own triage pre-pass, no longer take a
88
+ policy. Both now accept only a proposal the quality judge passed on its
89
+ exact content and reject an empty diff; everything else goes to
90
+ processes.triage.judgment or waits for review -- including extract and
91
+ consolidate proposals, which used to be auto-accepted on size alone under
92
+ the personal-stash default.
93
+
94
+ akm show no longer has a related field. It lists an asset's links instead:
95
+ the relations a bundle already declares -- cross-references, supersededBy,
96
+ a .derived memory's parent, a wiki's citations, resolved page links, a
97
+ workflow's or task's target -- grouped outgoing/incoming/unresolved,
98
+ computed with no LLM call. Curate's support refs are drawn from the same
99
+ links now, not from the retired entity graph.
100
+
101
+ akm info always exits 0 and always prints a report, even when something is
102
+ wrong: an invalid config.json, a missing or unreadable bundle directory, or
103
+ a locked/newer/corrupt index.db is named in the report (configError,
104
+ bundleDirError, indexStats.unavailable) instead of failing the command or
105
+ showing unexplained zeros. It also reports link counts per kind, including
106
+ how many are unresolved.
107
+
108
+ Decide every pending retire proposal -- akm proposal accept or reject --
109
+ before downgrading to 0.9.16. Older releases predate the retire proposal
110
+ shape entirely: akm proposal show/diff/drain exits 70 on one it doesn't
111
+ recognize, and drain's own nightly pre-pass failing on the first pending
112
+ retire proposal it meets stops that run's auto-promotion for the whole
113
+ stash, not just for the one proposal.
114
+
115
+ 0.9.16 already refuses to open a newer-layout index.db outright
116
+ (INDEX_SCHEMA_INCOMPATIBLE, naming the upgrade) and leaves the file
117
+ untouched, so downgrading needs a fresh index: move index.db aside and run
118
+ akm index --full under 0.9.16, which re-embeds everything from scratch.
119
+
120
+ A workflow run started under 0.9.17 freezes its plan as irVersion 6,
121
+ which 0.9.16 does not understand and refuses to resume
122
+ (WORKFLOW_IR_VERSION_UNSUPPORTED) -- let any in-flight workflow runs finish
123
+ before downgrading, or plan to restart them fresh under 0.9.16.
124
+
125
+ The state.db migration that adds the improve ledger
126
+ (028-improve-ledger) drops six tables 0.9.16 and earlier used
127
+ (proposal_fingerprints and friends). Because it drops schema, the akm that
128
+ runs it first copies the database to state.db.pre-028-improve-ledger.bak
129
+ before making the change. That backup is a snapshot from the moment of
130
+ upgrade -- restoring it to downgrade discards every proposal, event, and run
131
+ state.db has recorded since, not just the ledger's own six tables, so treat
132
+ it as a last resort rather than routine.
@@ -7,6 +7,13 @@ live one level up in `docs/migration/`.
7
7
 
8
8
  ## Available notes
9
9
 
10
+ - [0.9.17](0.9.17.md) — consolidate's retire proposals and continuity check,
11
+ promotions archiving their source memory, declared links replacing the LLM
12
+ entity graph, index layout 26, scheduler rows carrying their own context,
13
+ and `akm improve` scoped to what retrieval actually returns
14
+ - [0.9.16](0.9.16.md) — source-bound scheduler grants, local execution
15
+ authority, config inheritance's portable-data boundary, and split unsafe
16
+ overrides
10
17
  - [0.9.15](0.9.15.md) — exit-code 75 for lease/state.db contention,
11
18
  `--require-engines` scheduled task templates, `--no-probe` cli-version
12
19
  skip, thinking-control wire forms, embedding re-embed safety and
@@ -104,7 +104,7 @@ with an `INVALID_SHAPE_VALUE` usage error (exit 2) — an honest rejection rathe
104
104
  than a silent fallback. It returns a compact view suitable for capability
105
105
  discovery:
106
106
 
107
- - **show**: `type`, `name`, canonical `ref`, `description`, `tags`, `parameters`, `workflowTitle`, `action`, `run`, `origin`, `keys`, `related`, `links`
107
+ - **show**: `type`, `name`, canonical `ref`, `description`, `tags`, `parameters`, `workflowTitle`, `action`, `run`, `origin`, `keys`, `links`
108
108
 
109
109
  ## Exit Codes and Error Envelope
110
110
 
@@ -195,8 +195,8 @@ akm setup
195
195
  The setup wizard configures AKM in two steps:
196
196
 
197
197
  **Step 1 — Small model connection** (for background processing)
198
- Configures the OpenAI-compatible endpoint and model used for `akm index`
199
- metadata enhancement and `akm remember --enrich`. Supports Ollama,
198
+ Configures the OpenAI-compatible endpoint and model used for `akm improve`
199
+ and `akm remember --enrich`. Supports Ollama,
200
200
  OpenAI, LM Studio, or any custom endpoint. Skipping disables enrichment features.
201
201
 
202
202
  **Step 2 — Agent connection** (for agentic commands)
@@ -300,10 +300,8 @@ invokes `akm index` directly should pass `--skip-if-locked` so it steps
300
300
  aside instead of piling up behind a longer rebuild (the shipped
301
301
  `index-refresh` task does this).
302
302
 
303
- `akm index` always rebuilds the search index and keeps metadata in the index.
304
- When a selected named LLM engine (`defaults.llmEngine` or an indexing-pass
305
- override) is configured and the per-pass gate allows it, metadata
306
- enhancement runs during indexing. In text mode, the default CLI UI shows a
303
+ `akm index` always rebuilds the search index and keeps metadata in the
304
+ index, generated deterministically. In text mode, the default CLI UI shows a
307
305
  spinner with processed-versus-total source counts; structured output modes
308
306
  (`json`, `yaml`, `jsonl`) stay clean and machine-readable.
309
307
 
@@ -320,8 +318,10 @@ Returns a JSON object with:
320
318
  | Field | Description |
321
319
  | --- | --- |
322
320
  | `version` | Current akm version |
323
- | `bundleDir` | Primary bundle directory — same resolution `akm bundle list` uses |
321
+ | `bundleDir` | Primary bundle directory — same resolution `akm bundle list` uses. Falls back to the platform-default location when no bundle resolves. |
324
322
  | `defaultBundle` | Name of the primary bundle from config, or `null` when none is configured |
323
+ | `configError` | Present only when `config.json` exists but could not be loaded (parse or schema failure); every config-derived field falls back to the same defaults a fresh install reports |
324
+ | `bundleDirError` | Present when a bundle IS configured (an env override or `bundles.*` in config) but its path doesn't resolve, OR when the platform-default fallback itself can't resolve (e.g. `HOME` unset) — absent for the ordinary "no bundle created yet" state, where `bundleDir` needs no explanation |
325
325
  | `dataDir` | Resolved data directory (`getDataDir()`) |
326
326
  | `configDir` | Resolved config directory (`getConfigDir()`) |
327
327
  | `cacheDir` | Resolved cache directory (`getCacheDir()`) |
@@ -331,7 +331,14 @@ Returns a JSON object with:
331
331
  | `semanticSearch` | Semantic search status: `mode`, `status`, and optional `reason`/`message` |
332
332
  | `registries` | Configured registries |
333
333
  | `sourceProviders` | Configured sources (filesystem, git, website, npm) |
334
- | `indexStats` | Index stats: `entryCount`, `byType` (per-asset-type breakdown), `links` (declared links per kind with `total` and `unresolved`; absent when the index holds none), `lastBuiltAt`, `hasEmbeddings` |
334
+ | `indexStats` | Index stats: `entryCount`, `byType` (per-asset-type breakdown), `links` (declared links per kind with `total` and `unresolved`; absent when the index holds none), `lastBuiltAt`, `hasEmbeddings`, `unreadable` (index.db exists but the filesystem refuses to read it — a permissions problem), `unavailable` (index.db is readable but the SQLite-level read didn't complete: locked by another akm process, a newer layout this akm can't understand, or a corrupt file) |
335
+
336
+ `akm info` never refuses: an unreadable config, an unresolvable bundle
337
+ directory, or an index.db that's locked, newer, older, corrupt, missing, or
338
+ empty each degrade the relevant field(s) above instead of failing the
339
+ command. It also never waits more than about 1.5s on a locked index.db,
340
+ regardless of the shared 30s lock-wait every write command otherwise uses,
341
+ and warns rather than refusing on an unrecognized flag.
335
342
 
336
343
  `semanticSearch.status` values:
337
344
  - `"ready-js"` — every entry has a vector; semantic search is active (the name is historical: `"ready-vec"`, the sqlite-vec variant, is gone)
@@ -389,7 +396,7 @@ Primary result fields:
389
396
  | `improve` | Recent improve-loop counts derived from `improve_invoked`, `improve_skipped`, and `improve_completed` events |
390
397
 
391
398
  The `improve` section includes counts for planned refs, reflect/distill actions,
392
- memory-prune actions, memory-inference writes, graph-extraction refreshes,
399
+ memory-prune actions, memory-inference writes,
393
400
  session-extraction outcomes (`sessionsScanned`, `sessionsExtracted`, `proposalsCreated`),
394
401
  dead-URL detections, and skip reasons observed in the selected time window.
395
402
 
@@ -432,13 +439,6 @@ for an infrastructure reason (`llm_unavailable`, `read_failed`, `exception`,
432
439
  `locked_concurrent`) — naming the reason and, when recorded, the engine — and
433
440
  `pass` otherwise, with per-outcome counts.
434
441
 
435
- The indexed entity graph (entities/relations extracted from bundle assets) has
436
- no dedicated inspection command; its summary counts surface as an info-level
437
- metric in `akm health`. Graph data is automatically re-extracted on the first
438
- `akm improve` cycle after a `DB_VERSION` upgrade. The graph backs `akm show`'s
439
- `related` list; it does not affect search ranking. Curate's support refs come
440
- from declared links (`akm show`'s `links`), not from this graph.
441
-
442
442
  ### search
443
443
 
444
444
  Search bundle assets, registries, or both.
@@ -703,8 +703,7 @@ workflow and task targets name), `incoming` (the assets that name it), and
703
703
  kind is `{ "total": n, "refs": [...] }` with at most 10 refs; `total` counts
704
704
  them all. The field is omitted when nothing links either way. Links are read
705
705
  from frontmatter and parsed structure at index time, with no model; they do
706
- not affect search ranking. `related` is separate: the LLM entity graph's
707
- shared-entity neighbours.
706
+ not affect search ranking.
708
707
 
709
708
  Opaque fragment shows and `--context lead` keep `ref` as the canonical parent
710
709
  identity and add
@@ -2528,7 +2527,7 @@ clock, or session-log changes.
2528
2527
 
2529
2528
  `plan.processes` (#947) is the resolved process -> engine -> model routing
2530
2529
  table: one row per improve process (`reflect`, `distill`, `consolidate`,
2531
- `memoryInference`, `graphExtraction`, `extract`, `validation`, `triage`,
2530
+ `memoryInference`, `extract`, `validation`, `triage`,
2532
2531
  `proactiveMaintenance`), plus a `triage.judgment` row when the strategy
2533
2532
  configures a judgment engine. Each row carries `enabled`, the resolved
2534
2533
  `engine`/`model` (llm-backed processes only) and `engineKind`, this process's
@@ -2583,7 +2582,7 @@ field on the result (`result_json` in `improve_runs`, and in the
2583
2582
  `calls`, `failures`, `promptTokens`, `completionTokens`, `totalTokens`,
2584
2583
  `reasoningTokens`, and `totalDurationMs`. `noCalls` lists every LLM-backed
2585
2584
  process (`reflect`, `distill`, `consolidate`, `memoryInference`,
2586
- `graphExtraction`, `extract`, `validation` — not `triage`/`proactiveMaintenance`,
2585
+ `extract`, `validation` — not `triage`/`proactiveMaintenance`,
2587
2586
  which never make an attributable LLM call themselves) the active strategy
2588
2587
  enabled but that ended the run with zero calls, each with a `reason` drawn
2589
2588
  from the existing skip-reason vocabulary: `"engine_unavailable"` (also in
@@ -2721,6 +2720,7 @@ akm proposal list
2721
2720
  akm proposal list --queue team-bundle
2722
2721
  akm proposal list --status pending|accepted|rejected|reverted
2723
2722
  akm proposal list --ref skills/deploy
2723
+ akm proposal list --generator consolidate-pair
2724
2724
  ```
2725
2725
 
2726
2726
  | Flag | Description |
@@ -2729,6 +2729,12 @@ akm proposal list --ref skills/deploy
2729
2729
  | `--status` | Filter by `pending`, `accepted`, `rejected`, or `reverted` |
2730
2730
  | `--ref` | Filter by asset ref. A qualified ref preserves bundle identity; a short ref matches that concept in the selected queue |
2731
2731
  | `--type` | Reserved type filter |
2732
+ | `--generator <name>` | Filter by generator/source (e.g. `reflect`, `distill`, `consolidate-pair`) — the same value `accept`/`reject --generator` take |
2733
+
2734
+ Each retire proposal's `retirement.continuityRisk`, when present, also shows
2735
+ in the default listing (`⚠ continuity-risk` inline) and in `proposal show`'s
2736
+ text output (the specific failing/unverified queries) — see
2737
+ [Retirement continuity](https://github.com/itlackey/akm/blob/main/docs/architecture/improvement.md#retirement-continuity).
2732
2738
 
2733
2739
  Each proposal record carries an optional `confidence` field (0..1) emitted by
2734
2740
  reflect/propose runs. It is recorded for triage and ranking only — there is no
@@ -314,7 +314,7 @@ can select `engine`, `model`, `timeoutMs`, and LLM request overrides:
314
314
  "engine": "fast",
315
315
  "processes": {
316
316
  "reflect": { "llm": { "temperature": 0.2 } },
317
- "graphExtraction": { "model": "qwen3-small" }
317
+ "memoryInference": { "model": "qwen3-small" }
318
318
  }
319
319
  }
320
320
  }
@@ -327,13 +327,6 @@ incompatible engine never falls back to another engine. Built-in strategies
327
327
  are complete presets. User-defined strategies inherit omitted fields from the
328
328
  built-in `default` strategy before applying their own overrides.
329
329
 
330
- Graph extraction also reads `index.graph`. Its `engine`, `model`, `timeoutMs`
331
- and `llm` apply over the strategy-wide values, and `processes.graphExtraction`
332
- overrides them. `processes.graphExtraction.batchSize` and `includeTypes` win
333
- over `index.graph.graphExtractionBatchSize` and `graphExtractionIncludeTypes`;
334
- with neither set, the batch size is 4 and the types are `memory` and
335
- `knowledge`.
336
-
337
330
  `processes.triage.judgment` explicitly controls the optional judgment tier.
338
331
  Use `true` to enable it, `false` to disable it, or an object with `enabled`,
339
332
  `engine`, `model`, `timeoutMs`, and/or `llm` overrides. Existing object values
@@ -848,7 +841,23 @@ profile identities.
848
841
  config that still sets it is simply ignored — it still loads, unvalidated
849
842
  and without warning.
850
843
 
851
- `index.graph.lazyGraphExtraction` is retired in 0.9.17: `akm show` and
852
- `akm curate` no longer extract or queue graph work, and graph extraction runs
853
- only in `akm improve`. A config that still sets it loads; the key is named
854
- once as unknown, and `akm migrate apply` removes it.
844
+ `index.graph.*` and every strategy's `processes.graphExtraction.*` are retired
845
+ in 0.9.17-alpha.9: the LLM entity graph they configured is gone —
846
+ `akm show`'s links come from declared links instead (see `## Strategies`
847
+ above). A config that still sets them loads; each key is named once as
848
+ unknown, and `akm migrate apply` removes it. The built-in `graph-refresh`
849
+ strategy is retired too, but not the same way as an ordinary unknown name:
850
+ naming it via `--strategy` or a task always fails with a message pointing at
851
+ the retirement, even when `improve.strategies["graph-refresh"]` still has a
852
+ leftover override from customizing the built-in (the message names it;
853
+ `akm migrate apply` drops it — a leftover override is never resolved as a new
854
+ custom strategy, which would silently run a full, unplanned improve pass).
855
+ `defaults.improveStrategy: "graph-refresh"` still loads config successfully;
856
+ the refusal happens lazily, when the strategy is actually resolved.
857
+
858
+ `improve.strategies.<name>.processes.consolidate.incrementalSince` and
859
+ `.neighborsPerChanged` are retired in 0.9.17-alpha.9: the consolidate pair
860
+ pass is now the candidate generator, narrowing per initiator through the
861
+ improve ledger rather than a global time window. A config that still sets
862
+ either key loads; each is named once as unknown, and `akm migrate apply`
863
+ removes it.
@@ -199,7 +199,6 @@ the set of types the code actually emits at HEAD (verified against every
199
199
  | `extract_triaged` | The pre-LLM extract triage gate evaluated at least one session | `evaluated`, `passed`, `triagedOut`, `sourceRun` (aggregated) |
200
200
  | `schema_repair_invoked` | The schema-repair pass inside `akm improve` (`runSchemaRepairPass`) attempts to patch missing frontmatter on an asset that failed schema validation. **There is no `akm lint --repair` flag** — `lint` has `--fix`/`--auto-fix`, unrelated to this event | `ref`, outcome |
201
201
  | `proactive_selected` | The proactive-maintenance selector runs (once per `akm improve` run) | `count`, `dueTotal`, `neverReflected` (aggregated) |
202
- | `improve_salience_rank_change` | Bundle-wide rank-change report, from the second improve run onward | `stashSize`, `totalChanged`, `forgettingCandidates`, `topDrops` |
203
202
  | `events_purged` | Old events deleted by improve maintenance (90-day default retention) | `purgedCount`, `retentionDays` |
204
203
  | `improve_runs_purged` | Old `improve_runs` rows deleted by improve maintenance (same retention window as events) | `purgedCount`, `retentionDays` |
205
204
  | `state_db_vacuumed` | state.db was VACUUMed after the retention purge because more than half its pages were free | `pagesBefore`, `pagesAfter`, `freelistRatioBefore` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akm-cli",
3
- "version": "0.9.17-alpha.8",
3
+ "version": "0.9.17",
4
4
  "type": "module",
5
5
  "description": "akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cursor.",
6
6
  "keywords": [