akm-cli 0.9.0-beta.36 → 0.9.0-beta.37
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.
|
@@ -54,7 +54,7 @@ import { DEFAULT_DUE_DAYS, DEFAULT_MAX_PER_RUN, filterProactiveDue, selectProact
|
|
|
54
54
|
import { akmProcedural } from "./procedural.js";
|
|
55
55
|
import { akmRecombine } from "./recombine.js";
|
|
56
56
|
import { akmReflect } from "./reflect.js";
|
|
57
|
-
import { buildRankChangeReport, computeSalience, getAllRankScores, getAssetSalience, getConsecutiveNoOps, getLastUseMsByRef, recordNoOp, resetConsecutiveNoOps, SALIENCE_NO_OP_DAMPEN_FACTOR, SALIENCE_NO_OP_DAMPEN_THRESHOLD, upsertAssetSalience, } from "./salience.js";
|
|
57
|
+
import { buildRankChangeReport, computeSalience, getAllRankScores, getAssetSalience, getConsecutiveNoOps, getLastUseMsByRef, isContentEncodingRow, recordNoOp, resetConsecutiveNoOps, SALIENCE_NO_OP_DAMPEN_FACTOR, SALIENCE_NO_OP_DAMPEN_THRESHOLD, upsertAssetSalience, } from "./salience.js";
|
|
58
58
|
// #607 Lock Decomposition: fine-grained per-process locks replace the single
|
|
59
59
|
// `improve.lock`. Three independent locks allow concurrent improve runs when
|
|
60
60
|
// they touch different subsystems (e.g. quick-shredder consolidate can run
|
|
@@ -2733,6 +2733,37 @@ async function runImprovePreparationStage(args) {
|
|
|
2733
2733
|
const outcomeWeightEnabled = salienceConfig?.outcomeWeightEnabled === true;
|
|
2734
2734
|
const salienceMap = new Map();
|
|
2735
2735
|
const nowForSalience = Date.now();
|
|
2736
|
+
// #644 — preserve content-derived encoding scores across runs.
|
|
2737
|
+
//
|
|
2738
|
+
// Before computing the salience vector, load each ref's stored encoding score
|
|
2739
|
+
// and its provenance. When the stored row carries a genuine content-derived
|
|
2740
|
+
// score (written by the distill path via `scoreEncodingSalience`), pass that
|
|
2741
|
+
// value back in as `inputs.encodingSalience` so `computeSalience` does NOT fall
|
|
2742
|
+
// back to the type-weight stub — keeping both the persisted `encoding_salience`
|
|
2743
|
+
// AND the derived `rank_score` keyed on real novelty/magnitude/prediction-error.
|
|
2744
|
+
// Refs that have never been content-scored keep the type-weight stub fallback.
|
|
2745
|
+
const storedEncodingByRef = new Map();
|
|
2746
|
+
{
|
|
2747
|
+
let dbForStoredEncoding;
|
|
2748
|
+
try {
|
|
2749
|
+
dbForStoredEncoding = openStateDatabase(eventsCtx?.dbPath);
|
|
2750
|
+
for (const r of mergedRefs) {
|
|
2751
|
+
const type = r.ref.includes(":") ? r.ref.slice(0, r.ref.indexOf(":")) : "";
|
|
2752
|
+
const row = getAssetSalience(dbForStoredEncoding, r.ref);
|
|
2753
|
+
if (row && isContentEncodingRow(row, type)) {
|
|
2754
|
+
storedEncodingByRef.set(r.ref, row.encoding_salience);
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
catch (err) {
|
|
2759
|
+
rethrowIfTestIsolationError(err);
|
|
2760
|
+
// best-effort: if DB unavailable, fall back to type-weight stub (prior behaviour)
|
|
2761
|
+
}
|
|
2762
|
+
finally {
|
|
2763
|
+
if (dbForStoredEncoding)
|
|
2764
|
+
dbForStoredEncoding.close();
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2736
2767
|
for (const r of mergedRefs) {
|
|
2737
2768
|
const type = r.ref.includes(":") ? r.ref.slice(0, r.ref.indexOf(":")) : "";
|
|
2738
2769
|
const sizeBytes = (() => {
|
|
@@ -2746,9 +2777,13 @@ async function runImprovePreparationStage(args) {
|
|
|
2746
2777
|
return undefined;
|
|
2747
2778
|
}
|
|
2748
2779
|
})();
|
|
2780
|
+
const storedEncoding = storedEncodingByRef.get(r.ref);
|
|
2749
2781
|
const vector = computeSalience({
|
|
2750
2782
|
ref: r.ref,
|
|
2751
2783
|
type,
|
|
2784
|
+
// #644: pass the stored content-derived score (if any) so the type-weight
|
|
2785
|
+
// stub is NOT re-asserted over a real distill-written encoding score.
|
|
2786
|
+
...(storedEncoding !== undefined ? { encodingSalience: storedEncoding } : {}),
|
|
2752
2787
|
retrievalFreq: retrievalCounts.get(r.ref) ?? 0,
|
|
2753
2788
|
lastUseMs: lastUseMsByRef.get(r.ref),
|
|
2754
2789
|
utilityScore: utilityMap.get(r.ref),
|
|
@@ -126,6 +126,7 @@ export function computeSalience(inputs) {
|
|
|
126
126
|
// Fall back to the type-importance stub only when the caller has not yet
|
|
127
127
|
// computed a content-based score (e.g. on older assets before the first
|
|
128
128
|
// staleness refresh runs).
|
|
129
|
+
const encodingSource = inputs.encodingSalience !== undefined ? "content" : "type-stub";
|
|
129
130
|
const encoding = inputs.encodingSalience !== undefined
|
|
130
131
|
? Math.min(1, Math.max(0, inputs.encodingSalience))
|
|
131
132
|
: (DEFAULT_TYPE_ENCODING_WEIGHTS[inputs.type] ?? DEFAULT_ENCODING_SALIENCE);
|
|
@@ -213,24 +214,75 @@ export function computeSalience(inputs) {
|
|
|
213
214
|
}
|
|
214
215
|
const rawRankScore = (we * encoding + wo * outcome + wr * retrieval) * sizePenalty;
|
|
215
216
|
const rankScore = Math.min(1, Math.max(0, rawRankScore));
|
|
216
|
-
return { encoding, outcome, retrieval, rankScore };
|
|
217
|
+
return { encoding, outcome, retrieval, rankScore, encodingSource };
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Does this row carry a genuine content-derived `encoding_salience` (#644)?
|
|
221
|
+
*
|
|
222
|
+
* Returns true when the provenance flag is `"content"`. For legacy rows
|
|
223
|
+
* (`encoding_source === null`, written before migration 015) we apply a
|
|
224
|
+
* conservative heuristic: treat the stored value as content-derived only when it
|
|
225
|
+
* does NOT equal the pure type-weight stub for the asset's type — because before
|
|
226
|
+
* the #644 fix every run overwrote real scores with the stub, a value that still
|
|
227
|
+
* differs from the stub must have been content-written and never re-clobbered.
|
|
228
|
+
* When the type cannot be determined (no `type` given) a null-provenance row is
|
|
229
|
+
* treated as a stub (the safe default).
|
|
230
|
+
*/
|
|
231
|
+
export function isContentEncodingRow(row, type) {
|
|
232
|
+
if (row.encoding_source === "content")
|
|
233
|
+
return true;
|
|
234
|
+
if (row.encoding_source === "type-stub")
|
|
235
|
+
return false;
|
|
236
|
+
// Legacy NULL provenance: differ-from-stub heuristic.
|
|
237
|
+
if (!type)
|
|
238
|
+
return false;
|
|
239
|
+
const stub = DEFAULT_TYPE_ENCODING_WEIGHTS[type] ?? DEFAULT_ENCODING_SALIENCE;
|
|
240
|
+
return Math.abs(row.encoding_salience - stub) > 1e-9;
|
|
217
241
|
}
|
|
218
242
|
/**
|
|
219
243
|
* Upsert salience scores for one asset into state.db.
|
|
220
244
|
*
|
|
221
|
-
* Idempotent: safe to call every run; updates
|
|
245
|
+
* Idempotent: safe to call every run; updates the outcome / retrieval / rank
|
|
246
|
+
* columns on conflict.
|
|
247
|
+
*
|
|
248
|
+
* #644 — encoding provenance guard: the `encoding_salience` + `encoding_source`
|
|
249
|
+
* columns are NOT lowered from a real content-derived score to a type-weight
|
|
250
|
+
* stub. When the stored row is `encoding_source = 'content'` and the incoming
|
|
251
|
+
* vector is a `type-stub` fallback, the stored encoding score and its provenance
|
|
252
|
+
* are preserved (only the other sub-scores and `rank_score` advance). A `content`
|
|
253
|
+
* write always wins; a `type-stub` write only seeds a row that has no content
|
|
254
|
+
* score yet. This stops the improve loop's type-weight fallback re-asserting the
|
|
255
|
+
* stub over a distill-written score on every run.
|
|
256
|
+
*
|
|
257
|
+
* NOTE: when the guard preserves the stored encoding score, the incoming
|
|
258
|
+
* `vector.rankScore` (computed from the stub encoding) is still written. Callers
|
|
259
|
+
* that want the rank_score to reflect the preserved content score should pass the
|
|
260
|
+
* stored content score back in as `inputs.encodingSalience` to `computeSalience`
|
|
261
|
+
* — which the improve loop does. The guard here is the defensive backstop.
|
|
222
262
|
*/
|
|
223
263
|
export function upsertAssetSalience(db, ref, vector, now) {
|
|
224
264
|
const ts = now ?? Date.now();
|
|
225
265
|
db.prepare(`INSERT INTO asset_salience
|
|
226
|
-
(asset_ref, encoding_salience, outcome_salience, retrieval_salience, rank_score, consecutive_no_ops, updated_at)
|
|
227
|
-
VALUES (?, ?, ?, ?, ?, 0, ?)
|
|
266
|
+
(asset_ref, encoding_salience, outcome_salience, retrieval_salience, rank_score, consecutive_no_ops, updated_at, encoding_source)
|
|
267
|
+
VALUES (?, ?, ?, ?, ?, 0, ?, ?)
|
|
228
268
|
ON CONFLICT(asset_ref) DO UPDATE SET
|
|
229
|
-
|
|
269
|
+
-- #644: never lower a real content-derived score to a type-weight stub.
|
|
270
|
+
-- Keep the stored encoding score + provenance when the stored row is
|
|
271
|
+
-- 'content' and the incoming write is a 'type-stub' fallback.
|
|
272
|
+
encoding_salience = CASE
|
|
273
|
+
WHEN asset_salience.encoding_source = 'content' AND excluded.encoding_source = 'type-stub'
|
|
274
|
+
THEN asset_salience.encoding_salience
|
|
275
|
+
ELSE excluded.encoding_salience
|
|
276
|
+
END,
|
|
277
|
+
encoding_source = CASE
|
|
278
|
+
WHEN asset_salience.encoding_source = 'content' AND excluded.encoding_source = 'type-stub'
|
|
279
|
+
THEN asset_salience.encoding_source
|
|
280
|
+
ELSE excluded.encoding_source
|
|
281
|
+
END,
|
|
230
282
|
outcome_salience = excluded.outcome_salience,
|
|
231
283
|
retrieval_salience = excluded.retrieval_salience,
|
|
232
284
|
rank_score = excluded.rank_score,
|
|
233
|
-
updated_at = excluded.updated_at`).run(ref, vector.encoding, vector.outcome, vector.retrieval, vector.rankScore, ts);
|
|
285
|
+
updated_at = excluded.updated_at`).run(ref, vector.encoding, vector.outcome, vector.retrieval, vector.rankScore, ts, vector.encodingSource ?? "type-stub");
|
|
234
286
|
}
|
|
235
287
|
/**
|
|
236
288
|
* Load the salience row for one asset, or undefined if not yet computed.
|
|
@@ -238,7 +290,7 @@ export function upsertAssetSalience(db, ref, vector, now) {
|
|
|
238
290
|
export function getAssetSalience(db, ref) {
|
|
239
291
|
const row = db
|
|
240
292
|
.prepare(`SELECT asset_ref, encoding_salience, outcome_salience, retrieval_salience,
|
|
241
|
-
rank_score, consecutive_no_ops, updated_at
|
|
293
|
+
rank_score, consecutive_no_ops, updated_at, encoding_source
|
|
242
294
|
FROM asset_salience WHERE asset_ref = ?`)
|
|
243
295
|
.get(ref);
|
|
244
296
|
// Bun SQLite returns null (not undefined) when no row found.
|
package/dist/core/state-db.js
CHANGED
|
@@ -791,6 +791,31 @@ const MIGRATIONS = [
|
|
|
791
791
|
ON recombine_hypotheses(last_seen_at);
|
|
792
792
|
`,
|
|
793
793
|
},
|
|
794
|
+
// ── Migration 015 — asset_salience: encoding_source provenance column (#644) ──
|
|
795
|
+
//
|
|
796
|
+
// Records HOW the stored `encoding_salience` was derived so an improve run's
|
|
797
|
+
// type-weight fallback can no longer clobber a real content-derived score:
|
|
798
|
+
//
|
|
799
|
+
// "content" — written by the distill path from `scoreEncodingSalience`
|
|
800
|
+
// (novelty·0.40 + magnitude·0.35 + predictionError·0.25).
|
|
801
|
+
// "type-stub" — written by `computeSalience`'s `DEFAULT_TYPE_ENCODING_WEIGHTS`
|
|
802
|
+
// fallback (no content-based score available for this ref yet).
|
|
803
|
+
// NULL — legacy row written before this migration; provenance unknown.
|
|
804
|
+
// Treated as a stub by readers UNLESS its stored value differs
|
|
805
|
+
// from the pure type-weight (best-effort heuristic for old data).
|
|
806
|
+
//
|
|
807
|
+
// Why a column rather than inference: before #644, every improve run overwrote
|
|
808
|
+
// the distill-written content score with the type-weight stub, so the stored
|
|
809
|
+
// value alone cannot distinguish a real score from a stub. The provenance flag
|
|
810
|
+
// is the single source of truth going forward; `upsertAssetSalience` refuses to
|
|
811
|
+
// lower a "content" row to a "type-stub" so the high-salience gate (#608) keys
|
|
812
|
+
// on genuine novelty/magnitude/prediction-error, not the asset type.
|
|
813
|
+
{
|
|
814
|
+
id: "015-asset-salience-encoding-source",
|
|
815
|
+
up: `
|
|
816
|
+
ALTER TABLE asset_salience ADD COLUMN encoding_source TEXT DEFAULT NULL;
|
|
817
|
+
`,
|
|
818
|
+
},
|
|
794
819
|
];
|
|
795
820
|
/**
|
|
796
821
|
* Apply every pending migration in a single transaction per migration.
|
|
@@ -9978,6 +9978,12 @@ var init_state_db = __esm(() => {
|
|
|
9978
9978
|
CREATE INDEX IF NOT EXISTS idx_recombine_hypotheses_last_seen
|
|
9979
9979
|
ON recombine_hypotheses(last_seen_at);
|
|
9980
9980
|
`
|
|
9981
|
+
},
|
|
9982
|
+
{
|
|
9983
|
+
id: "015-asset-salience-encoding-source",
|
|
9984
|
+
up: `
|
|
9985
|
+
ALTER TABLE asset_salience ADD COLUMN encoding_source TEXT DEFAULT NULL;
|
|
9986
|
+
`
|
|
9981
9987
|
}
|
|
9982
9988
|
];
|
|
9983
9989
|
});
|
|
@@ -9108,6 +9108,12 @@ var MIGRATIONS = [
|
|
|
9108
9108
|
CREATE INDEX IF NOT EXISTS idx_recombine_hypotheses_last_seen
|
|
9109
9109
|
ON recombine_hypotheses(last_seen_at);
|
|
9110
9110
|
`
|
|
9111
|
+
},
|
|
9112
|
+
{
|
|
9113
|
+
id: "015-asset-salience-encoding-source",
|
|
9114
|
+
up: `
|
|
9115
|
+
ALTER TABLE asset_salience ADD COLUMN encoding_source TEXT DEFAULT NULL;
|
|
9116
|
+
`
|
|
9111
9117
|
}
|
|
9112
9118
|
];
|
|
9113
9119
|
function runMigrations2(db) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akm-cli",
|
|
3
|
-
"version": "0.9.0-beta.
|
|
3
|
+
"version": "0.9.0-beta.37",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "akm (Agent Knowledge Management) — A package manager for AI agent skills, commands, tools, and knowledge. Works with Claude Code, OpenCode, Cursor, and any AI coding assistant.",
|
|
6
6
|
"keywords": [
|