Migration notes for akm v0.9.19
Nothing has to run after upgrading from 0.9.18: 0.9.19 adds no config keys, changes no index layout, adds no state.db migration and leaves scheduler rows alone. (0.9.18 asked nothing either. From 0.9.16 or earlier, also read the 0.9.17 note: akm help migrate 0.9.17.) What changes is which bundle akm improve works on, how a rejected proposal is undone, what consolidation and distill queue for review, and what akm proposal diff shows for a retirement.
akm improve now improves one bundle: the one it writes to, which is --bundle when you pass it, else defaultWriteTarget, else your working bundle (AKM_BUNDLE_DIR when set, otherwise defaultBundle). Through 0.9.18 a run also picked assets out of your other writable bundles, read each one from the bundle that owned it, and filed the proposal in its own write target, so an asset that lived elsewhere came back either as a new copy in the write target or as an edit of the write target's own copy made from the other bundle's text. That no longer happens, and a rewrite filed outside the bundle that owns its asset is now refused. If everything you improve lives in one bundle, nothing changes for you. If you improve more than one, this is the one action in the release: a scheduled akm improve, the shipped akm-improve tasks included, now covers only its write target, so add one akm improve --bundle run for each other bundle you want improved:
akm task add improve-team --schedule "15 3 * * *"
--command "akm improve --bundle team --skip-if-locked --require-engines"
(The --bundle inside the command string is akm improve's own; the --bundle of akm task add itself picks which bundle stores the task.) akm improve skills/x for an asset that lives only in another bundle now fails with a not-found error whose hint names that bundle, and akm improve team//skills/x works. Proposals an older run already queued stay in the queue, so review them with akm proposal list: one that creates an asset you already have in another bundle, or overwrites this bundle's copy with the other's text, is one of those. akm improve --dry-run and --plan now preview the bundle a live run improves; with no --bundle or defaultWriteTarget, a dry run used to read defaultBundle while a live run started from AKM_BUNDLE_DIR.
A rejection is no longer final. akm proposal reopen
The case that matters most is consolidate's retire proposals. Through 0.9.18, akm proposal diff drew a retirement as the file replaced by one blank line, so a correct retirement could pass for data loss. One reviewer rejected all 65 of a bundle's retire proposals that way, and each rejection also kept the pair pass from proposing that retirement again while both documents stayed unchanged. To take them back, check each rejection's reason (akm proposal list --status rejected --generator consolidate-pair --detail normal --format json shows it as review.reason) and reopen only those rejected over the diff, one id at a time so a refusal skips only that one (the pattern matches the reason given here; change it to yours):
akm proposal list --status rejected --generator consolidate-pair
--detail normal --format json
| jq -r '.proposals[] | select(.review.reason // "" | test("blank line"))
| .id'
| xargs -r -n 1 akm proposal reopen --reason "diff was misrendered"
A proposal refused because another pending retire proposal involves the same document can be reopened once that one is decided. A pair whose documents changed after the rejection needs no recovery, because the old rejection only held while both were unchanged and the pair pass may judge it afresh. Reopened retire proposals wait for you like any other. drain never accepts a retire proposal, so read each with akm proposal diff and accept or reject it.
akm proposal diff now draws a retirement as one. The text output has a retire header, the pair's verdict (label, reason, and continuity risk when the pair was flagged), a note that accept archives the file and revert restores it byte for byte, and then only the removed lines under a +++ /dev/null (retired: archived; successor ) line. In JSON, a retire proposal's diff result gains op ("delete"), retirement (retiredRef, successorRef, judgeLabel, judgeReason, cosine, and continuityRisk when flagged) and note; the diff result of every other proposal is unchanged. A script that matched the old "(update: ...)" header on a retire proposal should expect "(retire: ...)". akm proposal show --detail full no longer ends a retire proposal with an empty payload heading, and a reopened proposal's JSON gains reviewHistory.
Consolidation queues fewer repeat promotions. Before it queues a memory as a knowledge proposal, it compares the memory with the 20 knowledge docs in its bundle nearest to it by stored vector, and skips it, with skip reason dedup_covered_by_knowledge, when one of them already holds at least half of the memory's distinct 5-word runs. That needs stored vectors: with semantic search off the check does nothing and only the exact slug and whole-body checks apply. A memory whose promotion was accepted or rejected is now offered again only when its body changes (frontmatter edits do not count); it used to come back at once after an accept and after 7 days after a rejection. A decision an older release recorded has no body hash to compare and keeps those old windows. Expect a shorter promotion queue, with the skipped memories showing up as skip reasons and warnings in the run's result; to have a memory considered again, edit its text.
One consequence of those old windows: if you deleted the knowledge copies an
older release promoted, to keep only the memories, those memories have
neither a hold nor a copy for the coverage check to find, so the first 0.9.19
run proposes each of them once more, word for word. Reject them (akm proposal
reject
Three changes keep a tool failure recorded as feedback from becoming a lesson about the error or a TODO placeholder in a memory. Reflect's feedback caveat no longer offers a TODO: verify placeholder: when feedback asks for something the asset lacks, reflect is told to leave the section unchanged. TODO lines earlier runs already put in your assets stay until you remove them; grep -rniE "TODO:? *verify" over the bundle finds them. Distill's quality judge now also scores whether a lesson is about what its source is about. A lesson scored 1 (off-subject) is dropped as quality_rejected (a ledger row and a distill_invoked event, no proposal) instead of passing or waiting in the queue as review_needed. A lesson scored 2 is borderline: it is queued as review_needed for you to decide, even when its novelty and non-redundancy alone would pass it, unless those alone would reject it, which stays quality_rejected. The judge also reads the same first 3000 characters of the source body, without frontmatter, that the generator saw. The shipped agent guidance changed with it: akm help agents now says to record feedback about an asset's content, that it helped or turned out wrong, stale or unhelpful, and not a failed akm command such as akm show erroring, and akm feedback --help says the same of --reason. If you pasted that guide into an AGENTS.md or a system prompt, regenerate the block: the old text told agents to record --negative "when it fails".
Downgrading to 0.9.18 needs no data change, since no schema moved, but it brings some of this back. 0.9.18 does not know reviewHistory: a proposal reopened under 0.9.19 reads as an ordinary pending proposal, and 0.9.18 drops its history if it rewrites the row (accepting or rejecting it, say). It also counts a pending proposal's age from its creation, so a reopened proposal can be expired by the next akm improve run (retire proposals never expire) or swept by a scheduled accept, reject or drain with --older-than. It does not hold a promoted memory either: 0.9.19 records a decided promotion with the memory's body hash and no retry time, which 0.9.18 reads as eligible now, so those memories go back to consolidation on the next run, a rejected one sooner than the 7 days 0.9.18 would have waited. Its improve plans assets from every writable bundle again, and its akm proposal diff draws a retirement as a blank replacement again.