akm-cli 0.9.17-alpha.8 → 0.9.17-alpha.9

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 (104) hide show
  1. package/CHANGELOG.md +334 -0
  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/commands/health/archive-usage.js +98 -0
  17. package/dist/commands/health/data-dir-usage.js +25 -13
  18. package/dist/commands/health/html-report.js +1 -4
  19. package/dist/commands/health/improve-metrics.js +0 -25
  20. package/dist/commands/health/md-report.js +1 -6
  21. package/dist/commands/health/report-view-model.js +4 -14
  22. package/dist/commands/health/windows.js +0 -1
  23. package/dist/commands/health.js +13 -0
  24. package/dist/commands/improve/consolidate/continuity-check.js +137 -0
  25. package/dist/commands/improve/consolidate/pair-pass.js +791 -0
  26. package/dist/commands/improve/consolidate.js +38 -63
  27. package/dist/commands/improve/extract-prompt.js +1 -2
  28. package/dist/commands/improve/improve-cli.js +1 -1
  29. package/dist/commands/improve/improve-strategies.js +23 -5
  30. package/dist/commands/improve/improve.js +19 -30
  31. package/dist/commands/improve/ledger.js +3 -2
  32. package/dist/commands/improve/loop-stages.js +5 -85
  33. package/dist/commands/improve/memory/memory-belief.js +3 -1
  34. package/dist/commands/improve/memory/memory-improve.js +269 -11
  35. package/dist/commands/improve/planner.js +0 -5
  36. package/dist/commands/improve/preparation.js +20 -135
  37. package/dist/commands/improve/retrieval-scope.js +19 -4
  38. package/dist/commands/improve/salience.js +1 -14
  39. package/dist/commands/improve/stage.js +0 -1
  40. package/dist/commands/lint/base-linter.js +19 -11
  41. package/dist/commands/proposal/drain.js +8 -1
  42. package/dist/commands/proposal/proposal-cli.js +16 -2
  43. package/dist/commands/proposal/proposal-types.js +7 -0
  44. package/dist/commands/proposal/proposal.js +37 -6
  45. package/dist/commands/proposal/repository.js +613 -4
  46. package/dist/commands/proposal/validators/proposals.js +9 -0
  47. package/dist/commands/read/knowledge.js +3 -2
  48. package/dist/commands/read/show.js +0 -14
  49. package/dist/commands/sources/stash-cli.js +2 -2
  50. package/dist/core/bundle-rename.js +1 -7
  51. package/dist/core/config/config-schema.js +8 -1
  52. package/dist/core/config/config.js +23 -48
  53. package/dist/core/config/engine-semantics.js +0 -2
  54. package/dist/core/config/schema/improve-processes.js +17 -42
  55. package/dist/core/config/schema/index-config.js +5 -25
  56. package/dist/core/file-change.js +13 -5
  57. package/dist/core/improve-result.js +16 -5
  58. package/dist/core/improve-types.js +0 -1
  59. package/dist/core/loopback.js +7 -12
  60. package/dist/core/parse.js +13 -16
  61. package/dist/core/state/migrations.js +15 -0
  62. package/dist/core/time.js +0 -20
  63. package/dist/indexer/db/llm-cache.js +2 -2
  64. package/dist/indexer/ensure-index.js +2 -2
  65. package/dist/indexer/index-written-assets.js +2 -3
  66. package/dist/indexer/indexer.js +18 -418
  67. package/dist/indexer/passes/metadata.js +0 -19
  68. package/dist/indexer/walk/walker.js +3 -4
  69. package/dist/llm/client.js +8 -10
  70. package/dist/llm/embedders/remote.js +1 -2
  71. package/dist/llm/feature-gate.js +0 -5
  72. package/dist/output/shapes/helpers.js +20 -4
  73. package/dist/output/text/command-format.js +0 -8
  74. package/dist/output/text/proposal-format.js +47 -1
  75. package/dist/output/text/show-format.js +0 -20
  76. package/dist/scripts/akm-migrate-node.js +917 -950
  77. package/dist/scripts/akm-migrate.js +917 -950
  78. package/dist/setup/steps/connection.js +5 -6
  79. package/dist/setup/steps/platforms.js +2 -2
  80. package/dist/sources/providers/git-stash.js +55 -4
  81. package/dist/storage/repositories/improve-ledger-repository.js +48 -7
  82. package/dist/storage/repositories/index-entries-repository.js +4 -7
  83. package/dist/storage/repositories/index-entry-schema.js +4 -2
  84. package/dist/storage/repositories/index-llm-cache-repository.js +7 -26
  85. package/dist/storage/repositories/index-schema.js +55 -104
  86. package/dist/storage/repositories/proposals-repository.js +61 -0
  87. package/dist/storage/repositories/salience-repository.js +1 -19
  88. package/docs/reference/cli.md +16 -19
  89. package/docs/reference/configuration.md +21 -12
  90. package/docs/reference/data-and-telemetry.md +0 -1
  91. package/package.json +1 -1
  92. package/schemas/akm-config.json +0 -342
  93. package/dist/assets/improve-strategies/graph-refresh.json +0 -15
  94. package/dist/assets/prompts/contradiction-judge.md +0 -33
  95. package/dist/assets/prompts/graph-extract-system.md +0 -1
  96. package/dist/assets/prompts/graph-extract-user-prompt.md +0 -35
  97. package/dist/assets/prompts/metadata-enhance-system.md +0 -1
  98. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +0 -4
  99. package/dist/indexer/db/graph-db.js +0 -399
  100. package/dist/indexer/graph/graph-extraction.js +0 -809
  101. package/dist/indexer/graph/graph-related.js +0 -131
  102. package/dist/indexer/graph/graph-types.js +0 -4
  103. package/dist/llm/graph-extract.js +0 -892
  104. package/dist/llm/metadata-enhance.js +0 -95
package/CHANGELOG.md CHANGED
@@ -6,6 +6,340 @@ 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.9] - 2026-09-29
10
+
11
+ `akm improve` now forgets, reversibly and under review. A consolidation pair
12
+ pass compares each new or changed memory, flat knowledge file or lesson with
13
+ its nearest neighbours; where an LLM judge calls a pair duplicate, subsumed or
14
+ superseding, it mints a retire proposal that a person reviews (`akm proposal
15
+ list --generator consolidate-pair`). Accepting one archives the older or
16
+ contained copy, and `akm proposal revert` restores it exactly; an accepted
17
+ promotion now retires its source memory, so promotion no longer leaves a
18
+ duplicate. A continuity check flags a retirement whose survivor does not rank
19
+ where the retired asset did for its own past searches, and a flagged proposal
20
+ is never bulk-accepted. Archived files are purged 30 days after retirement,
21
+ only when git holds them unmodified. The per-run forgetting-safety lane, LLM
22
+ metadata enrichment and LLM entity-graph extraction are removed: each measured
23
+ no benefit. Decide pending retire proposals before downgrading to
24
+ 0.9.17-alpha.8.
25
+
26
+ ### Added
27
+
28
+ - **Consolidate pair pass: duplicate, subsumed and superseding memories are
29
+ now retired, review-gated.** A second pass inside `akmConsolidate`,
30
+ alongside the existing promote pass. It walks memory-tier assets (a
31
+ memory, base or `.derived`; a flat `knowledge/` asset; or a lesson) in the
32
+ retrieval scope that are new to the pass or whose body has changed since
33
+ their last full attempt — tracked by content hash, not a time window, so
34
+ an initiator the nightly cap or a pending-proposal collision leaves out
35
+ stays eligible rather than being marked settled, and the pass's own
36
+ ledger rows are never retrieval-scope evidence for the other improve
37
+ lanes — takes each one's nearest neighbours by stored vector (fetching 20,
38
+ keeping the first 5 that clear every filter; same bundle, memory tier
39
+ only — structured knowledge in subfolders is excluded), and judges every
40
+ pair at cosine >= `T_pair` (0.93) with one LLM call using the calibrated
41
+ relation prompt (`src/assets/prompts/consolidate-pair.md`, six labels:
42
+ `duplicate`, `subsumed`, `supersedes`, `contradicts`, `overlap`,
43
+ `unrelated`). An initiator with no prior attempt is held to a higher
44
+ `T_pair` >= 0.95, unless it is new material (git first-added within the
45
+ last 7 days), which judges at the ordinary 0.93. "Older"/"newer" for the
46
+ judge's own A/B labelling comes from one `git log` per run over the
47
+ bundle (first-add time, following renames so a moved or renamed file
48
+ keeps its original date), not frontmatter or file mtime — mtime is only
49
+ the fallback for a file git does not know, or a bundle with no `.git` at
50
+ all. At most 300 pairs are judged a night, admitted a whole initiator at
51
+ a time rather than by flat cosine rank: new-or-changed initiators first,
52
+ then the existing backlog by its own best cosine, each admitted only if
53
+ every one of its candidate pairs fits in what remains of the 300 — so an
54
+ initiator blocked on another pending decision never spends a slot doing
55
+ nothing, and a smaller initiator further down still fits when a larger
56
+ one ahead of it does not. `duplicate`, `subsumed` and `supersedes` mint a
57
+ reviewed `retire` proposal for the losing side (owner-calibrated
58
+ precision 20/22 = 0.91 [0.72, 0.97] against a second-rater baseline of
59
+ 0.17 for `supersedes` alone); `contradicts` is counted but stays a human
60
+ decision, and `overlap`/`unrelated` get no proposal. Guards: never a
61
+ `captureMode: hot` memory, never a `.derived` memory whose parent still
62
+ exists, never a pair where either side already has a pending retire
63
+ proposal (as the retired ref or its successor), and never retiring or
64
+ reusing as a successor an asset already spent earlier in the same run.
65
+ (`src/commands/improve/consolidate/pair-pass.ts`,
66
+ `src/commands/improve/retrieval-scope.ts`,
67
+ `src/storage/repositories/improve-ledger-repository.ts`,
68
+ `src/assets/prompts/consolidate-pair.md`)
69
+ - **Retire proposals.** A pair-pass retire proposal mints under its own
70
+ source, `consolidate-pair` — kept apart from the promote pass's
71
+ `consolidate` proposals, so a bulk `accept`/`reject --generator
72
+ consolidate` never sweeps a retirement, and the reverse; a bare `akm
73
+ proposal accept <ref>` never resolves to one either (it matches the
74
+ newest non-retire proposal for the ref, if any — a retire is reached by
75
+ its own proposal id, or the bulk `--generator consolidate-pair` form),
76
+ and retention expiry never drops a pending one for age alone. A pair the owner
77
+ rejected or reverted is not proposed again while both sides are unchanged. Its primary
78
+ change deletes its target instead of writing content. Accepting one first
79
+ confirms the decision is still fresh — the successor still exists, and
80
+ both sides' recorded body hashes still match their current files, not
81
+ just the retired side's, so a decision a later accept elsewhere made
82
+ stale (an A->B/B->C chain, or A->B/B->A both minted) is refused cleanly
83
+ rather than partially applied — then archives the asset (and its
84
+ `.derived` twin, if one exists) through a generalized
85
+ `archiveCleanupCandidate` (now usable on any memory, knowledge or lesson
86
+ file, not only `.derived` memories): the same
87
+ `.akm/memory-cleanup/archive/` encoding memory cleanup already used,
88
+ never the dead `.akm/archive/`. A `supersedes` judgement first writes the
89
+ `supersededBy` edge on the older asset, then archives it. Triage never
90
+ auto-accepts a retire proposal, whatever `applyMode` says — it waits for
91
+ a direct `akm proposal accept`, reviewed the same way as any other
92
+ proposal (`akm proposal list`, `show`, `diff`, bulk `accept --generator
93
+ consolidate-pair`; `--max-diff-lines` counts a retire by its target's own
94
+ line count). `accept` is crash-safe: it records its full intent —
95
+ `backupContent` and which file is about to move — durably before moving
96
+ anything, so a crash partway through, including between a primary and its
97
+ `.derived` twin, resumes and finishes from what was recorded rather than
98
+ leaving an asset stranded or the decision unrecorded. `revert` needs no
99
+ intent of its own — it resumes from what `accept` already recorded, and
100
+ refuses instead of overwriting a path that was reused by an unrelated file
101
+ since (its current content no longer matching what was retired). `revert`
102
+ restores the archived file(s) byte-for-byte from the bytes recorded at
103
+ accept, even a file that had no trailing newline — YAML comments, key
104
+ order and any human-written edge all survive the round trip. A ref to a retired asset keeps resolving to its tombstone
105
+ (`isArchivedRelPath`) — fixed along the way: that resolver assumed only
106
+ memories are ever archived, so an xref to a retired knowledge or lesson
107
+ asset was wrongly reported `missing-ref` by `akm lint` until now. Pending
108
+ retire proposals must be accepted or rejected before downgrading to
109
+ 0.9.17-alpha.8 or earlier — that release predates the retire shape
110
+ entirely and exits 70 on one in `show`/`diff`/`drain`, and drain's own
111
+ nightly pre-pass failing on the first one it meets stops that run's
112
+ auto-promotion for the whole stash. Downgrading also revives the
113
+ new-material starvation this same branch fixed forward-only: 0.9.17-alpha.8
114
+ counts a pair-pass ledger row as retrieval-scope evidence again, so its own
115
+ nightly attempts crowd new material back out of every other improve lane —
116
+ measured, two nights left only 341 of 2,183 new-only assets still in scope
117
+ once read under alpha.8
118
+ (`docs/architecture/persisted-data-compat.md`).
119
+ (`src/commands/proposal/repository.ts`,
120
+ `src/commands/improve/memory/memory-improve.ts`,
121
+ `src/commands/lint/base-linter.ts`)
122
+ - **A promotion retires its source memory (O1).** When `akm proposal accept`
123
+ promotes a consolidate `promote` proposal — by a person or by triage
124
+ auto-promotion — it now archives the source memory (and its `.derived`
125
+ twin) through the same retire-archive path, tombstoned `reason: promoted`,
126
+ provided the source's body still matches the hash recorded when the
127
+ promotion was minted; an edit since then leaves the source alone (a
128
+ proposal minted before this hash existed is never archived, for the same
129
+ reason). A promotion no longer leaves a memory/knowledge duplicate behind.
130
+ Best-effort: a failure to archive the source only warns; the promotion
131
+ itself is not undone. (`src/commands/improve/consolidate.ts`,
132
+ `src/commands/proposal/repository.ts`)
133
+ - **Retirement continuity check (rule R3).** Before the pair pass mints a
134
+ `retire` proposal, it replays up to five of the retired asset's own past
135
+ `search`/`curate` queries through akm's own search, in-process — the
136
+ ranking a user actually gets, no LLM. For every query where the retired
137
+ asset ranked in the top 10, the successor must too; compared directly,
138
+ since search itself returns at most the top 10 hits. A failing
139
+ query never blocks the mint — the
140
+ proposal's `retirement.continuityRisk` records the failing query count
141
+ and, per failing query, the retired asset's rank and the successor's
142
+ (`null` when the successor did not rank in the top 10 at all). A proposal
143
+ carrying `continuityRisk` is excluded from every bulk accept path (`accept
144
+ --generator …`, with or without `--yes`) but not from bulk reject —
145
+ declining a flagged proposal is always the safe direction; a person can
146
+ always accept one by id. An asset with no recorded queries is not checked
147
+ at all. A query that never ran (the search call threw) or that fell back
148
+ to keyword-only ranking (an unreachable embedding endpoint, most often)
149
+ is never silently trusted or silently dropped either: it counts as
150
+ "unverified" and, on its own, is enough to flag `continuityRisk` — an
151
+ endpoint outage reads as "risk unknown," never as "no risk found," for
152
+ every proposal checked while it stays down, not just the first. The
153
+ first fallback in a pair-pass run forces every later query in that same
154
+ run to skip the semantic attempt entirely, so a dead endpoint costs one
155
+ failed attempt total, not one per remaining query. Two fixes against false
156
+ flags, measured on a real night-1 admission (300 pairs, 6 flags, 3
157
+ spurious): replayed queries are the same cleaned set the retrieval
158
+ regression gate uses (`loadRetrievalQueries`) — stash-README boilerplate,
159
+ harness/tool envelopes, pastes over 2,000 characters, and near-duplicate
160
+ queries (equal once whitespace is collapsed) are dropped before replay,
161
+ not just capped at five raw entries; and the check does not run at all
162
+ when the retired and successor bodies are content-identical once
163
+ whitespace is collapsed — search's own content-dedupe already hides the
164
+ successor behind the retired asset for every such query, so a "successor
165
+ missing" finding would not be a real risk.
166
+ (`src/commands/improve/consolidate/continuity-check.ts`,
167
+ `src/commands/proposal/proposal-types.ts`,
168
+ `src/commands/proposal/proposal.ts`)
169
+ - **Continuity-risk visibility, and a `--generator` filter for `proposal
170
+ list`.** A retire proposal carrying `retirement.continuityRisk` now
171
+ shows `⚠ continuity-risk` inline in the default `akm proposal list`
172
+ output (and `--format text`), not just in `proposal show`. `proposal
173
+ show`'s text output now lists the actual failing query text and rank per
174
+ query, not just a count, and separately reports `unverifiedQueries` when
175
+ the risk is (also, or only) an unverified query rather than a rank
176
+ failure. `akm proposal accept --generator … --dry-run` (and a real bulk
177
+ run) now reports `skippedForContinuityRisk`, the count of otherwise
178
+ matching proposals excluded specifically for this reason, apart from an
179
+ ordinary `--max-diff-lines`/`--older-than` miss. `akm proposal list` gains
180
+ a `--generator <name>` filter, the same value `accept`/`reject
181
+ --generator` already take, so the (potentially large) backlog of one
182
+ generator's retire proposals can be reviewed as its own list.
183
+ (`src/commands/proposal/proposal.ts`, `src/commands/proposal/proposal-cli.ts`,
184
+ `src/output/text/proposal-format.ts`, `src/output/shapes/helpers.ts`)
185
+ - **Archive purge sweep.** Deterministic, no LLM, run once at the very start
186
+ of every `akm improve` invocation, ahead of index bootstrap and triage.
187
+ For a git-backed bundle, deletes the archived asset file(s) of a
188
+ retirement — never its `cleanup.md` tombstone — once `retiredAt` is more
189
+ than 30 days old (`RETIRE_GRACE_DAYS`) AND every file under that
190
+ retirement's archive directory is git-tracked, clean (`git ls-files` plus
191
+ `git status --porcelain -uall`), and verifiable (`git ls-files -v`: a
192
+ file marked `assume-unchanged` or `skip-worktree` hides its own edits
193
+ from `git status`, so it is never trusted as clean either) — each checked
194
+ once per sweep; git history keeps the bytes. `.git` presence alone is not
195
+ enough: `proposal accept` only commits for a `kind: "git"` write target,
196
+ and improve's own auto-sync stages only the paths its own run wrote, so a
197
+ filesystem-kind bundle can carry archived retirements that were never
198
+ committed — the tracked/clean/verifiable check is what keeps the sweep
199
+ from deleting the only surviving copy of those. A directory with even one
200
+ untracked, modified, or unverifiable file (tombstone included) is left
201
+ whole for a later sweep — and so is the ENTIRE archive for that sweep if
202
+ the underlying `git status` or `git ls-files` call itself fails (a broken
203
+ submodule, for instance, can fail `git status` while `git ls-files`
204
+ still succeeds): an empty result from a failed check is never treated as
205
+ "nothing to protect", and the sweep warns once rather than silently
206
+ purging nothing. A memory-cleanup family-prune archive carries no
207
+ `retiredAt`, so this sweep never touches that older archive class. Every
208
+ deleted file is journaled individually, so the end-of-run auto-sync
209
+ commits the removal the same way it commits the archive move itself.
210
+ (`src/commands/improve/memory/memory-improve.ts`,
211
+ `src/sources/providers/git-stash.ts`, `src/commands/improve/improve.ts`)
212
+ - **`akm health`'s `memory-cleanup-archive` advisory now covers every
213
+ bundle**, not just one with no `.git` at all. A bundle with no `.git` of
214
+ its own keeps every retirement's archived bytes forever (there is no
215
+ history to fall back on, so the purge sweep never runs there), and its
216
+ size and file count are reported as before. A git-backed bundle can ALSO
217
+ carry archived bytes the purge sweep will never remove — `.git` presence
218
+ alone never proved a retirement was committed — so this now runs the same
219
+ tracked/clean/verifiable check the purge sweep itself uses and reports
220
+ how many files and bytes of the archive cannot currently be purged
221
+ (untracked, modified, or unverifiable), alongside the total. Silent
222
+ whenever there is nothing to say: the archive is empty or absent, or (for
223
+ a git-backed bundle) every byte in it is purgeable once it ages out.
224
+ (`src/commands/health/archive-usage.ts`, `src/commands/health/data-dir-usage.ts`)
225
+
226
+ ### Removed
227
+
228
+ - **LLM metadata enrichment (`index.metadataEnhance`) is retired.** On 49
229
+ stratified queries, with every eligible candidate enriched (1,968
230
+ entries): search nDCG@10 moved −0.0092 [−0.0324, +0.0165], curate P@5
231
+ +0.000 [−0.037, +0.045], and long prompts lost −0.054 [−0.093, −0.012]. A
232
+ Doc2Query-- filter made it worse (P@5 −0.020 [−0.045, −0.004]). The pass
233
+ replaced authored descriptions on 89% of the entries it rewrote, and a
234
+ full pass costs about 27 B70-hours (RS-D, owner ruling 2026-09-28). It was
235
+ already off by default and off in the maintainer's config. The LLM call
236
+ (`src/llm/metadata-enhance.ts`), its `akm index` dispatch, and the
237
+ `metadata_enhance` feature-gate key are gone; the deterministic metadata
238
+ pass, `quality: "generated"`, and memory inference are unaffected. A
239
+ config that still sets `index.metadataEnhance` loads, named once by the
240
+ same unknown-config-key path every other retired key uses: kept in
241
+ memory, round-trips through ordinary writes, and is dropped only by
242
+ `akm migrate apply`. The pass's `llm_enrichment_cache` rows (the default
243
+ `cache_variant`; graph and memory inference use their own named variants)
244
+ are deleted on the next writable open of `index.db`. An index built while
245
+ enrichment was on keeps its entries' LLM-written descriptions on
246
+ incremental runs — nothing rewrites an unchanged row; run
247
+ `akm index --full` once to replace them with the deterministic ones.
248
+ (`src/indexer/indexer.ts`, `src/llm/feature-gate.ts`,
249
+ `src/core/config/config.ts`, `src/core/config/schema/index-config.ts`,
250
+ `src/storage/repositories/index-schema.ts`)
251
+ - **The LLM entity-graph extraction pass.** `akm improve`'s per-file
252
+ entity/relation extraction, its persisted tables (`graph_meta`,
253
+ `graph_files`, `graph_file_entities`, `graph_file_relations`), and `akm
254
+ show`'s `related` list are gone. On the navigation eval, vector kNN beat
255
+ the LLM `related` list by 0.157 P@5 [0.051, 0.260]; the ranking boost it
256
+ once fed was already removed in 0.9.17-alpha.4. Declared links (#935,
257
+ alpha.8) are the only navigation surface `akm show` has now, and curate's
258
+ support refs already came from them, not the graph. index.db is a
259
+ regenerable cache, so the graph tables are dropped unconditionally on the
260
+ next writable open — nothing migrates or backs them up.
261
+ - **The `graph-refresh` improve strategy** and the `akm-graph-refresh-weekly`
262
+ task template are deleted. Naming `graph-refresh` via `--strategy` or a task
263
+ now fails with a message naming the retirement, unconditionally — even when
264
+ `improve.strategies["graph-refresh"]` still has a leftover override block
265
+ from customizing the built-in (the message names it; `akm migrate apply`
266
+ drops it). `defaults.improveStrategy: "graph-refresh"` still loads config
267
+ successfully — the refusal happens lazily, when the strategy is actually
268
+ resolved, not at every command's config load.
269
+ - **Retired config keys:** `index.graph.*` and every strategy's
270
+ `processes.graphExtraction.*`. An old config that still sets them keeps
271
+ loading and the keys are unread, but `index.graph` is now also named once
272
+ by the unknown-config-key warning (it previously validated silently
273
+ against the generic per-pass catchall) and, like any other retired key,
274
+ is dropped only by `akm migrate apply` — not by an ordinary config write.
275
+ - **`akm health` drops every graph metric** — the KPI card, summary-table
276
+ rows, per-run duration/entity/relation columns, and the
277
+ `improve.graphExtraction.failures` window-compare delta. `--window-compare`
278
+ and `--group-by run` still count every run, including a pre-alpha.9 one:
279
+ its stored `graphExtraction`/`graphExtractionDurationMs` result fields and
280
+ its `graph-extraction` `plan.stages` / `graphExtraction` `plan.processes`
281
+ entries still decode, read-only, same as any other retired field (AGENTS.md
282
+ "Reading persisted data") — they are just no longer rendered. The
283
+ `improve_completed` event's `graphExtractionExtractedFiles`,
284
+ `graphExtractionDurationMs`, `graphCoverage`, `graphDensity`, and
285
+ `graphEntities` metadata fields are no longer emitted.
286
+ - **The per-run forgetting-safety lane.** `scoreSalience`'s stash-wide
287
+ salience-rank comparison, `applyForgettingSafety`, and the
288
+ `improve_salience_rank_change` event are gone. It was a one-time WS-1
289
+ cutover guard from the June 2026 ranking-formula change that had kept
290
+ running on every improve run since; the last 30 days of events
291
+ (2026-08-30 to 2026-09-29: 47 `improve_salience_rank_change` events, 5
292
+ refs flagged across 4 runs — 09-05, 09-08 x2, 09-19, 09-28) showed no
293
+ marginal pick over the signal-delta lane and the retrieval scope: 4 of
294
+ the 5 flagged refs were also picked that same run by signal-delta
295
+ (adjacent event ids/timestamps, 2–26 minutes after the rank-change
296
+ event), and the 5th (`workflows/create-github-issues-from-spec`, flagged
297
+ 09-05) has no `reflect_invoked` or `distill_invoked` event in the
298
+ retained history, but that run's `improve_runs.plannedRefs` shows it,
299
+ too, was planned under `signal-delta` — just not reflected (a
300
+ dispatch/budget limit that run, not a lane-exclusive pick). All 5
301
+ flagged refs were signal-delta picks; zero were forgetting-safety-only.
302
+ It also protected
303
+ `asset_salience.rank_score`, which only improve itself ever read — a rank
304
+ drop could not hide anything from search. `buildRankChangeReport` (its
305
+ comparator) is also gone: the new retirement continuity check (see
306
+ Added) compares ranks directly instead, and nothing else called it.
307
+ `forgetting-safety` stays a valid `eligibilitySource`/event-type
308
+ value so old proposals and events still decode, but nothing assigns or
309
+ emits it any more. (`src/commands/improve/preparation.ts`,
310
+ `src/commands/improve/salience.ts`, `src/core/events.ts`,
311
+ `src/storage/repositories/salience-repository.ts`)
312
+ - **`improve.strategies.<name>.processes.consolidate.incrementalSince` and
313
+ `.neighborsPerChanged`.** The consolidate pair pass is now the candidate
314
+ generator, narrowing per initiator through the improve ledger rather than
315
+ a global time window; neither key was set anywhere in the owner's config.
316
+ `narrowToIncrementalCandidates` goes with them, along with its
317
+ now-orphaned `parseSinceToIsoLenient` helper. A config that still sets
318
+ either key keeps loading under the retired-key contract: named once as
319
+ unknown, it survives an ordinary config write, and only `akm migrate
320
+ apply` drops it. (`src/core/config/schema/improve-processes.ts`,
321
+ `src/commands/improve/consolidate.ts`, `src/core/time.ts`,
322
+ `docs/reference/configuration.md`)
323
+
324
+ ### Fixed
325
+
326
+ - **Stale "advisory merge/delete/contradict" documentation.** Consolidation
327
+ stopped executing its merge/delete/contradict operations at `e82eec811`
328
+ (2026-07, #732; they had run in production until then, not "never
329
+ executed" as a couple of doc comments and `improve-workflow.md` claimed),
330
+ and 0.9.17-alpha.1 (`f4ebd763a`) dropped them from the prompt and schema,
331
+ which have offered `promote` only since. But `docs/architecture/
332
+ improvement.md`, `STABILITY.md` and `docs/architecture/internals/
333
+ improve-workflow.md` still described them as advisory planned output.
334
+ Corrected, and `improve-workflow.md` gains a section documenting the pair
335
+ pass, retire proposals and O1. Also corrected: the `default` strategy's
336
+ "advisory consolidation" description, two comments that still credited a
337
+ `beliefState` ranking boost alpha.4 removed (`memory-belief.ts`,
338
+ `knowledge.ts`), and D27's stale `archiveMemory` naming in the
339
+ architecture decision history. Deleted the unused
340
+ `src/assets/prompts/contradiction-judge.md` (no reader since
341
+ `e82eec811`).
342
+
9
343
  ## [0.9.17-alpha.8] - 2026-09-28
10
344
 
11
345
  `akm index` now records the links a bundle already declares (`xrefs`,
package/STABILITY.md CHANGED
@@ -366,8 +366,8 @@ for scripted use.
366
366
  ### `akm improve` autonomy — opt-in in 0.9.0
367
367
 
368
368
  **`akm improve` is review-first by default in 0.9.0.** The command itself is ON
369
- — its schedules, reflect/distill proposals, and graph extraction all run — but
370
- the lanes that mutate assets *without* review require an explicit opt-in:
369
+ — its schedules and reflect/distill proposals all run — but the lanes that
370
+ mutate assets *without* review require an explicit opt-in:
371
371
 
372
372
  ```sh
373
373
  akm config set experimental.improveAutonomy true
@@ -388,9 +388,10 @@ review-first config correctly shows `queue`.
388
388
  | memory cleanup | Belief-state frontmatter rewrites, archive moves | analyzed but not applied |
389
389
  | `triage` `applyMode: "promote"` | Auto-accepts queued proposals into the bundle | downgraded to `queue` — triage still runs, it just does not auto-accept |
390
390
 
391
- Consolidation remains enabled with autonomy off because merge, delete, and
392
- contradiction operations are advisory; promotion only emits a reviewable
393
- proposal.
391
+ Consolidation remains enabled with autonomy off: both its passes (promotion,
392
+ and the pair pass's duplicate/subsumed/supersedes judging) only ever emit a
393
+ reviewable proposal, and a pair-pass `retire` proposal is never auto-accepted
394
+ by `triage` `applyMode: "promote"` regardless of this gate.
394
395
 
395
396
  Because the gate is applied before the LLM preflight, a review-first workspace
396
397
  also needs fewer engines configured: a strategy whose only model-backed process
@@ -409,9 +410,9 @@ Autonomy is never inferred: an absent `experimental` section, an absent key, and
409
410
  an explicit `false` all read as off, so a partially-written or older config is
410
411
  review-first rather than accidentally permissive.
411
412
 
412
- Reflect, distill, extract candidates, validation, proactive-maintenance
413
- selection, and graph extraction are proposal-only and never write assets
414
- directly. Two further direct writes are ungated by design: `extract`'s session
413
+ Reflect, distill, extract candidates, validation, and proactive-maintenance
414
+ selection are proposal-only and never write assets directly. Two further
415
+ direct writes are ungated by design: `extract`'s session
415
416
  indexing (additive `sessions/**` writes,
416
417
  `processes.extract.indexSessions`, default on) and distill's
417
418
  encoding-salience frontmatter stamp (metadata only).
@@ -245,10 +245,9 @@ akm improve --no-push # commit but skip push for this ru
245
245
  akm improve --sync # force sync even on strategies that disable it
246
246
  ```
247
247
 
248
- Strategy sync defaults: `catchup`, `consolidate`, `default`,
249
- `graph-refresh`, `quick`, and `thorough` auto-commit + push;
250
- `proactive-maintenance` and `reflect-distill` skip sync entirely. Override
251
- with `--sync` / `--no-sync` flags.
248
+ Strategy sync defaults: `catchup`, `consolidate`, `default`, `quick`, and
249
+ `thorough` auto-commit + push; `proactive-maintenance` and `reflect-distill`
250
+ skip sync entirely. Override with `--sync` / `--no-sync` flags.
252
251
 
253
252
  The `--writable` flag on `akm bundle add` opts a remote git bundle into push-on-sync:
254
253
 
@@ -308,8 +307,8 @@ akm bundle create # Initialize working bund
308
307
  akm setup # Interactive wizard: bundle + LLM/embedding + agent + registry config
309
308
  akm setup --dir ~/custom-bundle # Run the wizard against a custom bundle path
310
309
  akm setup --yes # Non-interactive, accepts all defaults
311
- akm index # Rebuild search index (metadata enrichment when configured)
312
- akm index --full # Full reindex (metadata enrichment when configured)
310
+ akm index # Rebuild search index
311
+ akm index --full # Full reindex
313
312
  akm bundle list # List all sources
314
313
  akm lint # Structural lint over the bundle; exits 0 regardless of findings
315
314
  akm lint --fix # Auto-fix Tier 1 issues
@@ -399,7 +398,7 @@ akm agent --model sonnet --prompt "..." # Model override (aliases or exa
399
398
  ```sh
400
399
  akm info # Capabilities, bundle dir, index stats, semantic-search status
401
400
  akm health # Runtime diagnostics; exit 0 ok / 4 warn / 1 fail
402
- akm health --report # Adds accept-rate and graph-coverage metrics
401
+ akm health --report # Adds accept-rate metrics
403
402
  akm log # Append-only event stream (mutations, feedback, indexing)
404
403
  akm log --ref <ref> # One asset's event trail
405
404
  akm log --since @offset:<id> # Durable row-id cursor — poll this to follow the stream
@@ -16,9 +16,6 @@
16
16
  "memoryInference": {
17
17
  "enabled": false
18
18
  },
19
- "graphExtraction": {
20
- "enabled": false
21
- },
22
19
  "extract": {
23
20
  "enabled": false
24
21
  },
@@ -5,7 +5,6 @@
5
5
  "distill": { "enabled": false },
6
6
  "consolidate": { "enabled": true, "allowedTypes": ["memory"], "maxChunkSize": 25 },
7
7
  "memoryInference": { "enabled": false },
8
- "graphExtraction": { "enabled": false },
9
8
  "extract": { "enabled": false },
10
9
  "triage": { "enabled": false },
11
10
  "validation": { "enabled": false },
@@ -1,5 +1,5 @@
1
1
  {
2
- "description": "Standard improve pass — reflect, distill, advisory consolidation, graph extraction, and validation. Memory inference is listed below but only runs when experimental.improveAutonomy is set; improve-stage extract and proactive maintenance off.",
2
+ "description": "Standard improve pass — reflect, distill, consolidation (promotion plus the reviewed pair-pass retire/supersede proposals), and validation. Memory inference is listed below but only runs when experimental.improveAutonomy is set; improve-stage extract and proactive maintenance off.",
3
3
  "processes": {
4
4
  "reflect": {
5
5
  "enabled": true,
@@ -9,7 +9,6 @@
9
9
  "distill": { "enabled": true, "allowedTypes": ["memory"], "requirePlannedRefs": true },
10
10
  "consolidate": { "enabled": true, "allowedTypes": ["memory"] },
11
11
  "memoryInference": { "enabled": true },
12
- "graphExtraction": { "enabled": true },
13
12
  "extract": { "enabled": false, "triage": { "enabled": true, "minScore": 2 } },
14
13
  "validation": { "enabled": true },
15
14
  "proactiveMaintenance": { "enabled": false, "dueDays": 30, "maxPerRun": 15 },
@@ -1,5 +1,5 @@
1
1
  {
2
- "description": "Opt-in proactive-maintenance pass — reflect, distill, proposal triage (promote, high budget), and the proactive-maintenance lane (maxPerRun 100); consolidate/memoryInference/graphExtraction/extract off. Sync disabled: an interrupted run would otherwise leave an uncommitted backlog.",
2
+ "description": "Opt-in proactive-maintenance pass — reflect, distill, proposal triage (promote, high budget), and the proactive-maintenance lane (maxPerRun 100); consolidate/memoryInference/extract off. Sync disabled: an interrupted run would otherwise leave an uncommitted backlog.",
3
3
  "processes": {
4
4
  "reflect": {
5
5
  "enabled": true,
@@ -8,7 +8,6 @@
8
8
  "distill": { "enabled": true, "allowedTypes": ["memory"] },
9
9
  "consolidate": { "enabled": false },
10
10
  "memoryInference": { "enabled": false },
11
- "graphExtraction": { "enabled": false },
12
11
  "extract": { "enabled": false },
13
12
  "validation": { "enabled": false },
14
13
  "triage": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "description": "Reflect-only pass — no extract, distill, consolidate, memoryInference, or graphExtraction.",
2
+ "description": "Reflect-only pass — no extract, distill, consolidate, or memoryInference.",
3
3
  "processes": {
4
4
  "reflect": {
5
5
  "enabled": true,
@@ -9,7 +9,6 @@
9
9
  "distill": { "enabled": false },
10
10
  "consolidate": { "enabled": false },
11
11
  "memoryInference": { "enabled": false },
12
- "graphExtraction": { "enabled": false },
13
12
  "triage": { "enabled": false },
14
13
  "validation": { "enabled": false },
15
14
  "proactiveMaintenance": { "enabled": false }
@@ -1,5 +1,5 @@
1
1
  {
2
- "description": "Reflect + distill pass — reflect, distill, memoryInference, and proposal triage (promote); proactiveMaintenance/consolidate/graphExtraction/extract off. Sync disabled: an interrupted run would otherwise leave an uncommitted backlog.",
2
+ "description": "Reflect + distill pass — reflect, distill, memoryInference, and proposal triage (promote); proactiveMaintenance/consolidate/extract off. Sync disabled: an interrupted run would otherwise leave an uncommitted backlog.",
3
3
  "processes": {
4
4
  "reflect": {
5
5
  "enabled": true,
@@ -8,7 +8,6 @@
8
8
  "distill": { "enabled": true, "allowedTypes": ["memory"], "requirePlannedRefs": false },
9
9
  "consolidate": { "enabled": false },
10
10
  "memoryInference": { "enabled": true },
11
- "graphExtraction": { "enabled": false },
12
11
  "extract": {
13
12
  "enabled": false,
14
13
  "timeoutMs": 300000,
@@ -18,9 +18,6 @@
18
18
  "memoryInference": {
19
19
  "enabled": true
20
20
  },
21
- "graphExtraction": {
22
- "enabled": true
23
- },
24
21
  "extract": {
25
22
  "enabled": false,
26
23
  "triage": {
@@ -0,0 +1,20 @@
1
+ You compare two assets from one person's agent-memory library (memories and knowledge notes an AI coding agent reads). Asset A is the OLDER one and asset B the NEWER one, by the dates shown.
2
+
3
+ Classify the relation between them as exactly one of:
4
+
5
+ - "duplicate": they state the same durable facts. Wording, title or formatting may differ, but neither adds a claim a reader would need that the other lacks.
6
+ - "subsumed": one of them contains every durable claim of the other, plus more. The smaller one is redundant.
7
+ - "supersedes": B updates, corrects, reverses or replaces a claim in A (a newer version, a changed decision, a fixed bug, a new value). A is now stale or wrong on that point.
8
+ - "contradicts": they make logically exclusive claims about the same thing and nothing shows which one is current.
9
+ - "overlap": same subject, but each has durable claims the other lacks. Keeping both loses nothing; merging them would keep both sets of claims.
10
+ - "unrelated": different subjects or different facts that happen to share words.
11
+
12
+ Rules:
13
+ - A durable claim is a fact, decision, value, command, path, number or rule someone would act on. Ignore dates, headings, tags and phrasing.
14
+ - Prefer "overlap" over "duplicate" when either asset has a specific detail (a number, command, file, version or condition) that the other lacks.
15
+ - "supersedes" needs a specific claim in A that B changes. Being newer or longer is not enough.
16
+ - "contradicts" needs two claims that cannot both be true. Different scope, project or time is not a contradiction.
17
+
18
+ Set "redundant" to "A" or "B" when that asset could be removed with no loss (only for "duplicate" or "subsumed"; for "duplicate" name the less complete or older one), else null. Set "stale" to "A" when the relation is "supersedes", else null.
19
+
20
+ Answer ONLY with JSON: {"relation": "...", "redundant": "A"|"B"|null, "stale": "A"|null, "confidence": 0.0-1.0, "reason": "<at most 25 words>"}
@@ -8,11 +8,11 @@ updated: 2026-09-28
8
8
 
9
9
  <!--
10
10
  SOFT guidance only — advice, not a contract. Back-linking here is a RETRIEVAL
11
- mechanism, not decoration: `xrefs:` frontmatter folds into the search index;
12
- the entity/relation graph is extracted from BODY prose (memory + knowledge),
13
- never from frontmatter. Over-linking degrades ranking, so these rules are
14
- deliberately conservative. (LLM-wiki bundles carry their own xref system in
15
- their pages' frontmatter — this convention is for in-stash assets.)
11
+ mechanism, not decoration: `xrefs:` frontmatter folds into the search index
12
+ and is stored as a declared link `akm show` lists on both assets.
13
+ Over-linking degrades ranking, so these rules are deliberately conservative.
14
+ (LLM-wiki bundles carry their own xref system in their pages' frontmatter —
15
+ this convention is for in-stash assets.)
16
16
  -->
17
17
 
18
18
  # Back-linking conventions
@@ -21,9 +21,8 @@ Cross-references are how knowledge compounds instead of being re-derived every
21
21
  session. In AKM they are also **indexed**: the strings in an asset's `xrefs:`
22
22
  frontmatter fold into its search-hint text and are stored as links that
23
23
  `akm show` lists on both assets (with `supersededBy:`, `contradictedBy:` and a
24
- `.derived` memory's parent), and knowledge/memory bodies feed an LLM-extracted
25
- entity/relation graph that boosts ranking. So links are a retrieval
26
- lever — which means both too few and too many hurt.
24
+ `.derived` memory's parent). So links are a retrieval lever — which means both
25
+ too few and too many hurt.
27
26
 
28
27
  ```yaml
29
28
  ---
@@ -48,10 +47,8 @@ xrefs:
48
47
  - **Associative xrefs are discretionary — real relationships only.** Add one when
49
48
  you already know a genuine load-bearing connection. Do **not** hit a link
50
49
  quota by pointing at the topically-nearest sibling — a plausible-but-wrong
51
- xref makes this asset a false search match for the other topic, and a wrong
52
- relationship asserted in prose poisons the entity graph. A relationship you
53
- want the graph to learn must be named in the body (e.g. open with "Corrects
54
- knowledge/auth/oauth-refresh-races").
50
+ xref makes this asset a false search match for the other topic and a false
51
+ declared link on both assets' `akm show` output.
55
52
  - **Cap total xrefs at ~5 (a heuristic, not a measured threshold).** Each xref
56
53
  folds its ref tokens into THIS asset's search hints — past a handful, the
57
54
  asset matches queries about several other topics and its own ranking signal
@@ -80,19 +77,20 @@ prose is indexed in the lowest-weight `content` field, so
80
77
  `description:`/`when_to_use:` remain the primary orientation channel. Then
81
78
  open the body with a plain title plus a one-line
82
79
  orientation naming what it is, its scope/domain, and its key entities in
83
- canonical spelling (`Postgres`, `OAuth`, `TLS`, `Acme`) — the entity/relation
84
- graph is extracted from body prose, and readers land here from `akm show`.
85
- Keep the canonical-spelling list in `facts/conventions/domains` so agents don't
86
- fragment `postgres` / `postgresql` / `pg`.
80
+ canonical spelling (`Postgres`, `OAuth`, `TLS`, `Acme`) — consistent spelling
81
+ keeps search matches from fragmenting across variants, and readers land here
82
+ from `akm show`. Keep the canonical-spelling list in `facts/conventions/domains`
83
+ so agents don't fragment `postgres` / `postgresql` / `pg`.
87
84
 
88
85
  ## Hubs are optional, not per-namespace obligations
89
86
 
90
87
  A hub (a `knowledge/` overview page that xrefs the key assets in a domain) is
91
88
  worth authoring for a **few genuinely high-traffic domains**. Do **not**
92
89
  mandate a hub per namespace and do not edit a hub on every write: that is O(n)
93
- maintenance, a concurrent-write contention point, and it flattens the multi-hop
94
- graph into a namespace-wide star. Let the FTS index be the catalog; spend the
95
- effort on per-asset self-situating headers instead.
90
+ maintenance, a concurrent-write contention point, and it flattens every linked
91
+ asset's declared links into a namespace-wide star centered on the hub. Let the
92
+ FTS index be the catalog; spend the effort on per-asset self-situating headers
93
+ instead.
96
94
 
97
95
  ## Keep assets atomic
98
96
 
@@ -48,8 +48,8 @@ volume justifies it.
48
48
  ## Canonical entity spellings
49
49
 
50
50
  Pick ONE name per entity and use it everywhere in asset **bodies** — retrieval
51
- is case-insensitive but treats aliases as different entities, so alias variants
52
- fragment the entity graph. Extend as your stash grows.
51
+ is case-insensitive but treats aliases as different terms, so alias variants
52
+ fragment search matches. Extend as your stash grows.
53
53
 
54
54
  - Postgres (not postgresql / pg)
55
55
  - Kubernetes (not k8s)