akm-cli 0.9.17 → 0.9.19-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +157 -0
- package/STABILITY.md +2 -1
- package/dist/assets/hints/cli-hints-full.md +4 -2
- package/dist/assets/hints/cli-hints-short.md +5 -3
- package/dist/assets/prompts/reflect-feedback-framing.md +1 -1
- package/dist/commands/feedback-cli.js +1 -1
- package/dist/commands/health/checks.js +6 -6
- package/dist/commands/health.js +3 -3
- package/dist/commands/improve/consolidate/coverage.js +132 -0
- package/dist/commands/improve/consolidate/pair-pass.js +31 -21
- package/dist/commands/improve/consolidate.js +46 -13
- package/dist/commands/improve/distill.js +8 -4
- package/dist/commands/improve/eligibility.js +39 -9
- package/dist/commands/improve/improve-cli.js +9 -6
- package/dist/commands/improve/improve.js +13 -4
- package/dist/commands/improve/ledger.js +2 -2
- package/dist/commands/improve/loop-stages.js +2 -0
- package/dist/commands/improve/preparation.js +3 -1
- package/dist/commands/improve/reflect.js +2 -2
- package/dist/commands/improve/stage.js +43 -12
- package/dist/commands/proposal/diff-format.js +21 -0
- package/dist/commands/proposal/proposal-cli.js +48 -10
- package/dist/commands/proposal/proposal-types.js +11 -0
- package/dist/commands/proposal/proposal.js +60 -5
- package/dist/commands/proposal/repository.js +250 -18
- package/dist/commands/read/knowledge.js +13 -11
- package/dist/commands/read/remember-cli.js +7 -3
- package/dist/commands/sources/source-clone.js +1 -1
- package/dist/commands/tasks/tasks-cli.js +1 -1
- package/dist/commands/tasks/tasks.js +10 -3
- package/dist/core/mutation-target.js +8 -3
- package/dist/core/write-source.js +3 -2
- package/dist/indexer/usage/usage-events.js +2 -1
- package/dist/output/shapes/helpers.js +7 -0
- package/dist/output/shapes/passthrough.js +1 -0
- package/dist/output/shapes/proposal/reopen.js +14 -0
- package/dist/output/shapes.js +2 -0
- package/dist/output/text/helpers.js +1 -1
- package/dist/output/text/proposal/proposal.js +3 -1
- package/dist/output/text/proposal-format.js +87 -32
- package/dist/scripts/akm-migrate-node.js +102 -25
- package/dist/scripts/akm-migrate.js +102 -25
- package/dist/storage/repositories/improve-ledger-repository.js +65 -6
- package/dist/storage/repositories/index-vec-repository.js +13 -8
- package/dist/storage/repositories/proposals-repository.js +23 -0
- package/dist/storage/sqlite-read-snapshot.js +46 -2
- package/dist/storage/state-db-integrity.js +12 -9
- package/dist/tasks/run/load-task.js +5 -1
- package/docs/migration/README.md +1 -0
- package/docs/migration/release-notes/0.9.19.md +134 -0
- package/docs/migration/release-notes/README.md +5 -0
- package/docs/migration/v0.7-to-v0.8.md +2 -2
- package/docs/migration/v0.8-to-v0.9.md +5 -1
- package/docs/reference/cli.md +189 -28
- package/docs/reference/configuration.md +9 -8
- package/docs/reference/data-and-telemetry.md +24 -16
- package/package.json +1 -1
|
@@ -106,6 +106,20 @@ const GATE_OUTCOMES = {
|
|
|
106
106
|
staged: true,
|
|
107
107
|
"auto-rejected": true,
|
|
108
108
|
};
|
|
109
|
+
function isRecord(value) {
|
|
110
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
111
|
+
}
|
|
112
|
+
/** One undone rejection in `metadata_json.reviewHistory` (see `ProposalReviewHistoryEntry`): only the fields readers rely on are checked. */
|
|
113
|
+
function isReviewHistoryEntry(value) {
|
|
114
|
+
if (!isRecord(value))
|
|
115
|
+
return false;
|
|
116
|
+
const { review, gateDecision, reopenedAt, reopenReason } = value;
|
|
117
|
+
return (typeof reopenedAt === "string" &&
|
|
118
|
+
(reopenReason === undefined || typeof reopenReason === "string") &&
|
|
119
|
+
(review === undefined ||
|
|
120
|
+
(isRecord(review) && typeof review.outcome === "string" && typeof review.decidedAt === "string")) &&
|
|
121
|
+
(gateDecision === undefined || isRecord(gateDecision)));
|
|
122
|
+
}
|
|
109
123
|
function validatePresentMetadata(meta) {
|
|
110
124
|
const stringFields = ["sourceRun", "beforeHash", "beforeHashNormalized", "backupContent"];
|
|
111
125
|
for (const field of stringFields) {
|
|
@@ -129,6 +143,12 @@ function validatePresentMetadata(meta) {
|
|
|
129
143
|
invalidPresentField("review");
|
|
130
144
|
}
|
|
131
145
|
}
|
|
146
|
+
if (Object.hasOwn(meta, "reviewHistory")) {
|
|
147
|
+
const history = meta.reviewHistory;
|
|
148
|
+
if (!Array.isArray(history) || history.some((entry) => !isReviewHistoryEntry(entry))) {
|
|
149
|
+
invalidPresentField("reviewHistory");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
132
152
|
if (Object.hasOwn(meta, "gateDecision")) {
|
|
133
153
|
const gate = meta.gateDecision;
|
|
134
154
|
if (typeof gate !== "object" ||
|
|
@@ -270,6 +290,7 @@ export function proposalRowToProposal(row) {
|
|
|
270
290
|
...(typeof meta.beforeHash === "string" ? { beforeHash: meta.beforeHash } : {}),
|
|
271
291
|
...(typeof meta.beforeHashNormalized === "string" ? { beforeHashNormalized: meta.beforeHashNormalized } : {}),
|
|
272
292
|
...(meta.review !== undefined ? { review: meta.review } : {}),
|
|
293
|
+
...(meta.reviewHistory !== undefined ? { reviewHistory: meta.reviewHistory } : {}),
|
|
273
294
|
...(typeof meta.confidence === "number" ? { confidence: meta.confidence } : {}),
|
|
274
295
|
...(meta.gateDecision !== undefined ? { gateDecision: meta.gateDecision } : {}),
|
|
275
296
|
...(typeof meta.backupContent === "string" ? { backupContent: meta.backupContent } : {}),
|
|
@@ -332,6 +353,8 @@ export function proposalToRowValues(proposal, stashDir) {
|
|
|
332
353
|
metaObj.sourceRun = proposal.sourceRun;
|
|
333
354
|
if (proposal.review !== undefined)
|
|
334
355
|
metaObj.review = proposal.review;
|
|
356
|
+
if (proposal.reviewHistory !== undefined)
|
|
357
|
+
metaObj.reviewHistory = proposal.reviewHistory;
|
|
335
358
|
if (proposal.confidence !== undefined)
|
|
336
359
|
metaObj.confidence = proposal.confidence;
|
|
337
360
|
if (proposal.gateDecision !== undefined)
|
|
@@ -9,7 +9,12 @@
|
|
|
9
9
|
* must not do that. This helper copies a stable main/WAL pair and opens that
|
|
10
10
|
* private copy, so SQLite never attaches to the operator's original
|
|
11
11
|
* main/WAL/SHM files.
|
|
12
|
+
*
|
|
13
|
+
* The copy itself is made by a child process, never by this one: see
|
|
14
|
+
* {@link copyFileOutsideThisProcess} for why an in-process read of a live
|
|
15
|
+
* database is unsafe.
|
|
12
16
|
*/
|
|
17
|
+
import { spawnSync } from "node:child_process";
|
|
13
18
|
import fs from "node:fs";
|
|
14
19
|
import os from "node:os";
|
|
15
20
|
import path from "node:path";
|
|
@@ -57,6 +62,45 @@ function fingerprintsEqual(left, right) {
|
|
|
57
62
|
const sameFile = (a, b) => a?.size === b?.size && a?.mtimeNs === b?.mtimeNs && a?.ctimeNs === b?.ctimeNs;
|
|
58
63
|
return sameFile(left.main, right.main) && sameFile(left.wal, right.wal);
|
|
59
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Copy `source` to `destination` without opening `source` in THIS process.
|
|
67
|
+
*
|
|
68
|
+
* POSIX advisory locks belong to the process, not to the descriptor: when a
|
|
69
|
+
* process closes ANY descriptor for a file, the kernel drops every lock that
|
|
70
|
+
* process holds on it, including the SHARED lock a live SQLite connection in
|
|
71
|
+
* this same process (`akm improve` keeps one on state.db for the whole run)
|
|
72
|
+
* holds. A peer using an older SQLite (< 3.51) read-write would then see no
|
|
73
|
+
* reader, take EXCLUSIVE when it closes, and delete the `-wal`/`-shm` this
|
|
74
|
+
* process is still using. The read therefore happens in a child `cp`, whose
|
|
75
|
+
* descriptors and locks are its own. Windows locks belong to the handle, so
|
|
76
|
+
* an in-process copy cannot release another handle's lock there (and there is
|
|
77
|
+
* no `cp`).
|
|
78
|
+
*
|
|
79
|
+
* A source that vanished (a WAL checkpointed away mid-copy) throws an
|
|
80
|
+
* `ENOENT` error, which {@link openSqliteReadSnapshot} retries.
|
|
81
|
+
*/
|
|
82
|
+
function copyFileOutsideThisProcess(source, destination) {
|
|
83
|
+
if (process.platform === "win32") {
|
|
84
|
+
fs.copyFileSync(source, destination);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const result = spawnSync("cp", ["--", source, destination], {
|
|
88
|
+
encoding: "utf8",
|
|
89
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
90
|
+
});
|
|
91
|
+
if (result.error) {
|
|
92
|
+
throw new SqliteReadSnapshotUnavailableError(`cannot run cp to copy ${source}: ${result.error.message}`);
|
|
93
|
+
}
|
|
94
|
+
if (result.status === 0)
|
|
95
|
+
return;
|
|
96
|
+
// `cp` reports its errors as text, so the vanished-source case is recognised
|
|
97
|
+
// by looking at the source itself.
|
|
98
|
+
if (fileFingerprint(source) === undefined) {
|
|
99
|
+
throw Object.assign(new Error(`${source} disappeared while it was being copied`), { code: "ENOENT" });
|
|
100
|
+
}
|
|
101
|
+
const reason = result.stderr.trim() || (result.signal ? `killed by ${result.signal}` : `exit status ${result.status}`);
|
|
102
|
+
throw new Error(`cp could not copy ${source}: ${reason}`);
|
|
103
|
+
}
|
|
60
104
|
/**
|
|
61
105
|
* Open an isolated copy of an existing SQLite database.
|
|
62
106
|
*
|
|
@@ -82,9 +126,9 @@ export function openSqliteReadSnapshot(dbPath) {
|
|
|
82
126
|
if (pathExists(`${dbPath}-journal`))
|
|
83
127
|
continue;
|
|
84
128
|
const before = databaseFingerprint(dbPath);
|
|
85
|
-
|
|
129
|
+
copyFileOutsideThisProcess(dbPath, snapshotPath);
|
|
86
130
|
if (before.wal)
|
|
87
|
-
|
|
131
|
+
copyFileOutsideThisProcess(`${dbPath}-wal`, `${snapshotPath}-wal`);
|
|
88
132
|
else
|
|
89
133
|
fs.rmSync(`${snapshotPath}-wal`, { force: true });
|
|
90
134
|
const after = databaseFingerprint(dbPath);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* `akm health`'s `state-db-integrity` check (src/commands/health/checks.ts)
|
|
9
9
|
* is a pure projection like every other check, so the actual IO lives here:
|
|
10
|
-
* a read-only `PRAGMA
|
|
10
|
+
* a read-only `PRAGMA integrity_check` and a read-only freelist/page-count read.
|
|
11
11
|
* Both open their own short-lived read-only connection via the plain
|
|
12
12
|
* {@link openDatabase} opener rather than `openStateDatabase`
|
|
13
13
|
* (src/core/state-db.ts), since a corrupt database must not need a clean
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
import { appendEvent } from "../core/events.js";
|
|
28
28
|
import { openDatabase } from "./database.js";
|
|
29
29
|
import { applyReadonlyPragmas } from "./sqlite-pragmas.js";
|
|
30
|
-
/** How many corruption errors `PRAGMA
|
|
31
|
-
const
|
|
30
|
+
/** How many corruption errors `PRAGMA integrity_check` collects before it stops scanning and returns. */
|
|
31
|
+
const INTEGRITY_CHECK_ERROR_LIMIT = 10;
|
|
32
32
|
/**
|
|
33
33
|
* Above this fraction of free pages, `state-db-integrity` warns, and
|
|
34
34
|
* {@link vacuumIfReclaimable} compacts state.db (after improve's retention
|
|
@@ -48,16 +48,19 @@ function openReadonlyStateDb(dbPath) {
|
|
|
48
48
|
return db;
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
|
-
* Run `PRAGMA
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
51
|
+
* Run `PRAGMA integrity_check(N)` against `dbPath` read-only. Unlike
|
|
52
|
+
* `quick_check`, it also verifies every index against its table ("row N
|
|
53
|
+
* missing from index", "wrong # of entries in index"): the damage a WAL
|
|
54
|
+
* deleted under a live connection leaves behind, which `quick_check` reports
|
|
55
|
+
* as `ok`. Sub-second on a healthy state.db of a few hundred MB; on a corrupt
|
|
56
|
+
* one, `N` bounds how many errors SQLite collects before it stops scanning,
|
|
57
|
+
* which keeps the check's runtime bounded even against a badly corrupt file.
|
|
55
58
|
*/
|
|
56
|
-
export function
|
|
59
|
+
export function runStateDbIntegrityCheck(dbPath) {
|
|
57
60
|
let db;
|
|
58
61
|
try {
|
|
59
62
|
db = openReadonlyStateDb(dbPath);
|
|
60
|
-
const rows = db.prepare(`PRAGMA
|
|
63
|
+
const rows = db.prepare(`PRAGMA integrity_check(${INTEGRITY_CHECK_ERROR_LIMIT})`).all();
|
|
61
64
|
const lines = rows.map((row) => String(firstColumn(row)));
|
|
62
65
|
const ok = lines.length === 1 && lines[0] === "ok";
|
|
63
66
|
return { ok, lines };
|
|
@@ -107,7 +107,11 @@ export async function loadPreparedTask(id, options) {
|
|
|
107
107
|
return { file: await resolveAssetPath(bundleDir, type, name), bundleRoot: bundleDir };
|
|
108
108
|
}
|
|
109
109
|
const resolutionConfig = requiresCommandConfig ? config : loadConfig();
|
|
110
|
-
|
|
110
|
+
// `bundle` is the qualifier of an asset ref in the task, not a flag.
|
|
111
|
+
const resolvedBundle = resolveWriteTarget(resolutionConfig, bundle, {
|
|
112
|
+
requireWritable: false,
|
|
113
|
+
flag: "The asset ref's bundle",
|
|
114
|
+
});
|
|
111
115
|
return {
|
|
112
116
|
file: await resolveAssetPath(resolvedBundle.source.path, type, name),
|
|
113
117
|
bundleRoot: resolvedBundle.source.path,
|
package/docs/migration/README.md
CHANGED
|
@@ -6,6 +6,7 @@ Upgrade guides and per-release migration notes.
|
|
|
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
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
|
+
- [v0.9.19 release note](release-notes/0.9.19.md) -- `akm improve` scoped to the bundle it writes to (scheduled runs need one `--bundle` run per other bundle), `akm proposal reopen` and the retire-proposal diff, and fewer repeat consolidate promotions
|
|
9
10
|
- [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
11
|
- [v0.7 -> v0.8 migration guide](v0.7-to-v0.8.md) -- Task schema and 0.8-era changes
|
|
11
12
|
- [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
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Migration notes for akm v0.9.19
|
|
2
|
+
|
|
3
|
+
Nothing has to run after upgrading from 0.9.18: 0.9.19 adds no config keys,
|
|
4
|
+
changes no index layout, adds no state.db migration and leaves scheduler rows
|
|
5
|
+
alone. (0.9.18 asked nothing either. From 0.9.16 or earlier, also read the
|
|
6
|
+
0.9.17 note: akm help migrate 0.9.17.) What changes is which bundle akm
|
|
7
|
+
improve works on, how a rejected proposal is undone, what consolidation and
|
|
8
|
+
distill queue for review, and what akm proposal diff shows for a retirement.
|
|
9
|
+
|
|
10
|
+
akm improve now improves one bundle: the one it writes to, which is --bundle
|
|
11
|
+
when you pass it, else defaultWriteTarget, else your working bundle
|
|
12
|
+
(AKM_BUNDLE_DIR when set, otherwise defaultBundle). Through 0.9.18 a run also
|
|
13
|
+
picked assets out of your other writable bundles, read each one from the
|
|
14
|
+
bundle that owned it, and filed the proposal in its own write target, so an
|
|
15
|
+
asset that lived elsewhere came back either as a new copy in the write target
|
|
16
|
+
or as an edit of the write target's own copy made from the other bundle's
|
|
17
|
+
text. That no longer happens, and a rewrite filed outside the bundle that
|
|
18
|
+
owns its asset is now refused. If everything you improve lives in one bundle,
|
|
19
|
+
nothing changes for you. If you improve more than one, this is the one action
|
|
20
|
+
in the release: a scheduled akm improve, the shipped akm-improve tasks
|
|
21
|
+
included, now covers only its write target, so add one akm improve --bundle
|
|
22
|
+
run for each other bundle you want improved:
|
|
23
|
+
|
|
24
|
+
akm task add improve-team --schedule "15 3 * * *" \
|
|
25
|
+
--command "akm improve --bundle team --skip-if-locked --require-engines"
|
|
26
|
+
|
|
27
|
+
(The --bundle inside the command string is akm improve's own; the --bundle of
|
|
28
|
+
akm task add itself picks which bundle stores the task.) akm improve skills/x
|
|
29
|
+
for an asset that lives only in another bundle now fails with a not-found
|
|
30
|
+
error whose hint names that bundle, and akm improve team//skills/x works.
|
|
31
|
+
Proposals an older run already queued stay in the queue, so review them with
|
|
32
|
+
akm proposal list: one that creates an asset you already have in another
|
|
33
|
+
bundle, or overwrites this bundle's copy with the other's text, is one of
|
|
34
|
+
those. akm improve --dry-run and --plan now preview the bundle a live run
|
|
35
|
+
improves; with no --bundle or defaultWriteTarget, a dry run used to read
|
|
36
|
+
defaultBundle while a live run started from AKM_BUNDLE_DIR.
|
|
37
|
+
|
|
38
|
+
A rejection is no longer final. akm proposal reopen <id> [<id> ...] puts
|
|
39
|
+
rejected proposals back to pending and keeps each rejection in the proposal's
|
|
40
|
+
reviewHistory, which akm proposal show prints; a proposal_reopened event goes
|
|
41
|
+
into akm log. Reopen refuses a proposal that is not rejected or whose target
|
|
42
|
+
changed since it was created, by the same rule accept applies: an update needs
|
|
43
|
+
its target unchanged, a create needs the target still absent, and a retire
|
|
44
|
+
proposal needs its successor present and both documents unchanged. It also
|
|
45
|
+
refuses a retire proposal while another pending retire proposal involves
|
|
46
|
+
either of its documents. Given several ids it reopens all of them or none and
|
|
47
|
+
lists every refusal. It takes full ids, since a prefix only matches pending
|
|
48
|
+
proposals (an asset ref works too, for the newest proposal on it while none
|
|
49
|
+
is pending, but never for a retire proposal). The age that retention expiry
|
|
50
|
+
and --older-than see starts over at the reopen, so a scheduled sweep does not
|
|
51
|
+
take a proposal you just put back.
|
|
52
|
+
|
|
53
|
+
The case that matters most is consolidate's retire proposals. Through 0.9.18,
|
|
54
|
+
akm proposal diff drew a retirement as the file replaced by one blank line,
|
|
55
|
+
so a correct retirement could pass for data loss. One reviewer rejected all
|
|
56
|
+
65 of a bundle's retire proposals that way, and each rejection also kept the
|
|
57
|
+
pair pass from proposing that retirement again while both documents stayed
|
|
58
|
+
unchanged. To take them back, check each rejection's reason (akm proposal
|
|
59
|
+
list --status rejected --generator consolidate-pair --detail normal --format
|
|
60
|
+
json shows it as review.reason) and reopen only those rejected over the diff,
|
|
61
|
+
one id at a time so a refusal skips only that one (the pattern matches the
|
|
62
|
+
reason given here; change it to yours):
|
|
63
|
+
|
|
64
|
+
akm proposal list --status rejected --generator consolidate-pair \
|
|
65
|
+
--detail normal --format json \
|
|
66
|
+
| jq -r '.proposals[] | select(.review.reason // "" | test("blank line"))
|
|
67
|
+
| .id' \
|
|
68
|
+
| xargs -r -n 1 akm proposal reopen --reason "diff was misrendered"
|
|
69
|
+
|
|
70
|
+
A proposal refused because another pending retire proposal involves the same
|
|
71
|
+
document can be reopened once that one is decided. A pair whose documents
|
|
72
|
+
changed after the rejection needs no recovery, because the old rejection only
|
|
73
|
+
held while both were unchanged and the pair pass may judge it afresh. Reopened
|
|
74
|
+
retire proposals wait for you like any other. drain never accepts a retire
|
|
75
|
+
proposal, so read each with akm proposal diff and accept or reject it.
|
|
76
|
+
|
|
77
|
+
akm proposal diff now draws a retirement as one. The text output has a retire
|
|
78
|
+
header, the pair's verdict (label, reason, and continuity risk when the pair
|
|
79
|
+
was flagged), a note that accept archives the file and revert restores it byte
|
|
80
|
+
for byte, and then only the removed lines under a
|
|
81
|
+
+++ /dev/null (retired: archived; successor <ref>) line. In JSON, a retire
|
|
82
|
+
proposal's diff result gains op ("delete"), retirement (retiredRef,
|
|
83
|
+
successorRef, judgeLabel, judgeReason, cosine, and continuityRisk when flagged)
|
|
84
|
+
and note; the diff result of every other proposal is unchanged. A script that
|
|
85
|
+
matched the old "(update: ...)" header on a retire proposal should expect
|
|
86
|
+
"(retire: ...)". akm proposal show --detail full no longer ends a retire
|
|
87
|
+
proposal with an empty payload heading, and a reopened proposal's JSON gains
|
|
88
|
+
reviewHistory.
|
|
89
|
+
|
|
90
|
+
Consolidation queues fewer repeat promotions. Before it queues a memory as a
|
|
91
|
+
knowledge proposal, it compares the memory with the 20 knowledge docs in its
|
|
92
|
+
bundle nearest to it by stored vector, and skips it, with skip reason
|
|
93
|
+
dedup_covered_by_knowledge, when one of them already holds at least half of
|
|
94
|
+
the memory's distinct 5-word runs. That needs stored vectors: with semantic
|
|
95
|
+
search off the check does nothing and only the exact slug and whole-body
|
|
96
|
+
checks apply. A memory whose promotion was accepted or rejected is now offered
|
|
97
|
+
again only when its body changes (frontmatter edits do not count); it used to
|
|
98
|
+
come back at once after an accept and after 7 days after a rejection. A
|
|
99
|
+
decision an older release recorded has no body hash to compare and keeps
|
|
100
|
+
those old windows. Expect a shorter promotion queue, with the skipped
|
|
101
|
+
memories showing up as skip reasons and warnings in the run's result; to have
|
|
102
|
+
a memory considered again, edit its text.
|
|
103
|
+
|
|
104
|
+
Three changes keep a tool failure recorded as feedback from becoming a lesson
|
|
105
|
+
about the error or a TODO placeholder in a memory. Reflect's feedback caveat no
|
|
106
|
+
longer offers a TODO: verify placeholder: when feedback asks for something the
|
|
107
|
+
asset lacks, reflect is told to leave the section unchanged. TODO lines earlier
|
|
108
|
+
runs already put in your assets stay until you remove them; grep -rniE
|
|
109
|
+
"TODO:? *verify" over the bundle finds them. Distill's quality judge now also
|
|
110
|
+
scores whether a lesson is about what its source is about, and a lesson judged
|
|
111
|
+
off-subject is dropped as quality_rejected (a ledger row and a distill_invoked
|
|
112
|
+
event, no proposal) instead of passing or waiting in the queue as
|
|
113
|
+
review_needed; the judge also reads the same first 3000 characters of the
|
|
114
|
+
source body, without frontmatter, that the generator saw. The shipped agent
|
|
115
|
+
guidance changed with it: akm help agents now says to record feedback about an
|
|
116
|
+
asset's content, that it helped or turned out wrong, stale or unhelpful, and
|
|
117
|
+
not a failed akm command such as akm show erroring, and akm feedback --help
|
|
118
|
+
says the same of --reason. If you pasted that guide into an AGENTS.md or a
|
|
119
|
+
system prompt, regenerate the block: the old text told agents to record
|
|
120
|
+
--negative "when it fails".
|
|
121
|
+
|
|
122
|
+
Downgrading to 0.9.18 needs no data change, since no schema moved, but it
|
|
123
|
+
brings some of this back. 0.9.18 does not know reviewHistory: a proposal
|
|
124
|
+
reopened under 0.9.19 reads as an ordinary pending proposal, and 0.9.18 drops
|
|
125
|
+
its history if it rewrites the row (accepting or rejecting it, say). It also
|
|
126
|
+
counts a pending proposal's age from its creation, so a reopened proposal can
|
|
127
|
+
be expired by the next akm improve run (retire proposals never expire) or
|
|
128
|
+
swept by a scheduled accept, reject or drain with --older-than. It does not
|
|
129
|
+
hold a promoted memory either: 0.9.19 records a decided promotion with the
|
|
130
|
+
memory's body hash and no retry time, which 0.9.18 reads as eligible now, so
|
|
131
|
+
those memories go back to consolidation on the next run, a rejected one
|
|
132
|
+
sooner than the 7 days 0.9.18 would have waited. Its improve plans assets
|
|
133
|
+
from every writable bundle again, and its akm proposal diff draws a
|
|
134
|
+
retirement as a blank replacement again.
|
|
@@ -7,6 +7,11 @@ live one level up in `docs/migration/`.
|
|
|
7
7
|
|
|
8
8
|
## Available notes
|
|
9
9
|
|
|
10
|
+
- [0.9.19](0.9.19.md) — `akm improve` planning only the bundle it writes to
|
|
11
|
+
(a scheduled run needs one `--bundle` run per other bundle),
|
|
12
|
+
`akm proposal reopen` and the retire-proposal diff, consolidate's coverage
|
|
13
|
+
check and content-driven hold on decided promotions, distill's grounding
|
|
14
|
+
check, and what a downgrade to 0.9.18 undoes
|
|
10
15
|
- [0.9.17](0.9.17.md) — consolidate's retire proposals and continuity check,
|
|
11
16
|
promotions archiving their source memory, declared links replacing the LLM
|
|
12
17
|
entity graph, index layout 26, scheduler rows carrying their own context,
|
|
@@ -988,7 +988,7 @@ akm-migrate storage --dry-run
|
|
|
988
988
|
and look for import warnings. You can also inspect `state.db` directly:
|
|
989
989
|
|
|
990
990
|
```sh
|
|
991
|
-
sqlite3 ~/.local/share/akm/state.db 'SELECT COUNT(*) FROM events;'
|
|
991
|
+
sqlite3 -readonly ~/.local/share/akm/state.db 'SELECT COUNT(*) FROM events;'
|
|
992
992
|
```
|
|
993
993
|
|
|
994
994
|
**`akm list` shows empty stashes.**
|
|
@@ -1039,7 +1039,7 @@ If you need to roll back to 0.7.x:
|
|
|
1039
1039
|
wrote new events on 0.8.0 and need them in 0.7.x, export them:
|
|
1040
1040
|
|
|
1041
1041
|
```sh
|
|
1042
|
-
sqlite3 ~/.local/share/akm/state.db \
|
|
1042
|
+
sqlite3 -readonly ~/.local/share/akm/state.db \
|
|
1043
1043
|
"SELECT json_object('schemaVersion', schema_version, 'ts', ts, 'eventType', event_type, 'ref', ref, 'metadata', json(metadata)) FROM events ORDER BY id;" \
|
|
1044
1044
|
>> ~/.cache/akm/events.jsonl
|
|
1045
1045
|
```
|
|
@@ -140,7 +140,11 @@ paths (same-filesystem rename, or copy-then-delete across filesystems). A
|
|
|
140
140
|
lock file is only ever deleted once the same staleness check `akm improve`
|
|
141
141
|
itself uses says its holder is dead; a lock a live run still holds (or one
|
|
142
142
|
this process cannot read) is left in place and reported instead. The whole
|
|
143
|
-
step is idempotent — a second run reports nothing pending.
|
|
143
|
+
step is idempotent — a second run reports nothing pending. 0.9.17-alpha.4
|
|
144
|
+
removed that step: `akm migrate` no longer relocates these files, and one left
|
|
145
|
+
at an old path is inert (nothing reads it). Two of the five writers no longer
|
|
146
|
+
exist either — the improve ledger replaced `distill-rejected/`, and the
|
|
147
|
+
write-only `eval-cases/` path was removed.
|
|
144
148
|
`$STASH/.akm/memory-cleanup/` did not move; it is the one confirmed exception
|
|
145
149
|
to the rule (see Storage locations, above).
|
|
146
150
|
|