akm-cli 0.9.0-rc.2 → 0.9.0-rc.3
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 +34 -8
- package/dist/assets/improve-strategies/catchup.json +3 -1
- package/dist/assets/improve-strategies/consolidate.json +3 -1
- package/dist/assets/improve-strategies/frequent.json +3 -1
- package/dist/assets/improve-strategies/graph-refresh.json +3 -1
- package/dist/assets/improve-strategies/memory-focus.json +4 -1
- package/dist/assets/improve-strategies/proactive-maintenance.json +1 -0
- package/dist/assets/improve-strategies/quick.json +3 -1
- package/dist/assets/improve-strategies/recombine-only.json +2 -0
- package/dist/assets/improve-strategies/reflect-distill.json +1 -0
- package/dist/assets/improve-strategies/synthesize.json +2 -0
- package/dist/assets/tasks/core/backup.yml +2 -2
- package/dist/cli/config-migrate.js +554 -26
- package/dist/cli.js +3 -1
- package/dist/commands/backup-cli.js +6 -4
- package/dist/commands/config-cli.js +10 -3
- package/dist/commands/feedback-cli.js +24 -7
- package/dist/commands/health/checks.js +94 -15
- package/dist/commands/health/surfaces.js +2 -1
- package/dist/commands/improve/anti-collapse.js +3 -3
- package/dist/commands/improve/collapse-detector.js +5 -5
- package/dist/commands/improve/consolidate.js +123 -66
- package/dist/commands/improve/distill/promote-memory.js +8 -6
- package/dist/commands/improve/distill/quality-gate.js +1 -1
- package/dist/commands/improve/distill-guards.js +1 -1
- package/dist/commands/improve/distill-promotion-policy.js +32 -26
- package/dist/commands/improve/distill.js +52 -36
- package/dist/commands/improve/eligibility.js +9 -8
- package/dist/commands/improve/extract-prompt.js +2 -2
- package/dist/commands/improve/extract.js +43 -11
- package/dist/commands/improve/improve-auto-accept.js +14 -28
- package/dist/commands/improve/improve-cli.js +4 -2
- package/dist/commands/improve/improve-strategies.js +2 -1
- package/dist/commands/improve/improve.js +49 -8
- package/dist/commands/improve/loop-stages.js +11 -6
- package/dist/commands/improve/preparation.js +51 -17
- package/dist/commands/improve/procedural.js +10 -6
- package/dist/commands/improve/recombine.js +31 -7
- package/dist/commands/improve/reflect.js +24 -14
- package/dist/commands/improve/salience.js +5 -1
- package/dist/commands/improve/source-identity.js +43 -0
- package/dist/commands/migrate-cli.js +36 -0
- package/dist/commands/mv-cli.js +647 -258
- package/dist/commands/proposal/drain.js +16 -4
- package/dist/commands/proposal/proposal-cli.js +3 -2
- package/dist/commands/proposal/proposal.js +16 -55
- package/dist/commands/proposal/repository.js +664 -58
- package/dist/commands/read/curate.js +4 -2
- package/dist/commands/read/knowledge.js +4 -1
- package/dist/commands/read/search.js +6 -2
- package/dist/commands/read/show.js +8 -7
- package/dist/commands/sources/self-update.js +156 -112
- package/dist/commands/sources/sources-cli.js +7 -2
- package/dist/core/common.js +36 -3
- package/dist/core/config/config-io.js +2 -2
- package/dist/core/config/config-schema.js +15 -3
- package/dist/core/config/config.js +14 -13
- package/dist/core/file-lock.js +2 -2
- package/dist/core/migration-backup.js +816 -171
- package/dist/core/migration-operation.js +44 -0
- package/dist/core/state/migrations.js +4 -7
- package/dist/core/state-db.js +22 -7
- package/dist/core/write-source.js +86 -18
- package/dist/indexer/db/db.js +109 -53
- package/dist/indexer/index-writer-lock.js +38 -37
- package/dist/indexer/index-written-assets.js +73 -56
- package/dist/indexer/indexer.js +5 -1
- package/dist/indexer/search/search-source.js +2 -2
- package/dist/indexer/usage/usage-events.js +8 -2
- package/dist/integrations/agent/engine-resolution.js +14 -7
- package/dist/scripts/migrate-storage.js +867 -990
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +405 -566
- package/dist/setup/detected-engines.js +7 -13
- package/dist/setup/engine-config.js +14 -4
- package/dist/setup/setup.js +1 -1
- package/dist/setup/steps/connection.js +1 -1
- package/dist/setup/steps/tasks.js +1 -1
- package/dist/sources/providers/git-stash.js +58 -21
- package/dist/sources/providers/git.js +1 -1
- package/dist/storage/engines/sqlite-migrations.js +138 -3
- package/dist/storage/repositories/events-repository.js +24 -0
- package/dist/storage/repositories/proposals-repository.js +14 -0
- package/dist/tasks/embedded.js +2 -0
- package/dist/workflows/db.js +24 -13
- package/dist/workflows/validator.js +24 -2
- package/docs/migration/release-notes/0.9.0.md +24 -7
- package/docs/migration/v0.8-to-v0.9.md +124 -19
- package/package.json +1 -1
|
@@ -242,11 +242,18 @@ export const configCommand = defineGroupCommand({
|
|
|
242
242
|
migrate: defineJsonCommand({
|
|
243
243
|
meta: {
|
|
244
244
|
name: "migrate",
|
|
245
|
-
description: "
|
|
245
|
+
description: "Compatibility alias for the canonical top-level migrate status/apply coordinator.",
|
|
246
246
|
},
|
|
247
|
-
|
|
247
|
+
args: {
|
|
248
|
+
config: {
|
|
249
|
+
type: "string",
|
|
250
|
+
description: "Path to an operator-prepared current-version config to validate and install",
|
|
251
|
+
},
|
|
252
|
+
dryRun: { type: "boolean", default: false, description: "Validate and report without changing files" },
|
|
253
|
+
},
|
|
254
|
+
async run({ args }) {
|
|
248
255
|
const { runConfigMigrate } = await import("../cli/config-migrate.js");
|
|
249
|
-
await runConfigMigrate();
|
|
256
|
+
await runConfigMigrate({ preparedConfigPath: args.config, dryRun: args.dryRun });
|
|
250
257
|
},
|
|
251
258
|
}),
|
|
252
259
|
enable: defineJsonCommand({
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import { defineJsonCommand, output, parseAllFlagValues } from "../cli/shared.js";
|
|
6
|
-
import { parseAssetRef } from "../core/asset/asset-ref.js";
|
|
6
|
+
import { parseAssetRef, refToString } from "../core/asset/asset-ref.js";
|
|
7
7
|
import { assembleAsset } from "../core/asset/asset-serialize.js";
|
|
8
8
|
import { parseFrontmatter, parseFrontmatterBlock } from "../core/asset/frontmatter.js";
|
|
9
9
|
import { writeFileAtomic } from "../core/common.js";
|
|
@@ -12,7 +12,8 @@ import { UsageError } from "../core/errors.js";
|
|
|
12
12
|
import { appendEvent } from "../core/events.js";
|
|
13
13
|
import { getDbPath } from "../core/paths.js";
|
|
14
14
|
import { warn } from "../core/warn.js";
|
|
15
|
-
import { applyFeedbackToUtilityScore, closeDatabase, findEntryIdByRef, getEntryFilePathById, openExistingDatabase, } from "../indexer/db/db.js";
|
|
15
|
+
import { applyFeedbackToUtilityScore, closeDatabase, findEntryIdByRef, getEntryById, getEntryFilePathById, openExistingDatabase, } from "../indexer/db/db.js";
|
|
16
|
+
import { resolveSourceEntries } from "../indexer/search/search-source.js";
|
|
16
17
|
import { countFeedbackSignals, insertUsageEvent } from "../indexer/usage/usage-events.js";
|
|
17
18
|
// ── Tag validation ────────────────────────────────────────────────────────────
|
|
18
19
|
const TAG_KEY_RE = /^[a-z_][a-z0-9_]*$/;
|
|
@@ -156,7 +157,7 @@ export const feedbackCommand = defineJsonCommand({
|
|
|
156
157
|
if (!ref) {
|
|
157
158
|
throw new UsageError("Asset ref is required. Usage: akm feedback <ref> --positive|--negative", "MISSING_REQUIRED_ARGUMENT", "Pass a ref like `skill:deploy` and either --positive or --negative.");
|
|
158
159
|
}
|
|
159
|
-
parseAssetRef(ref);
|
|
160
|
+
const parsedRef = parseAssetRef(ref);
|
|
160
161
|
if (args.positive && args.negative) {
|
|
161
162
|
throw new UsageError("Specify either --positive or --negative, not both.");
|
|
162
163
|
}
|
|
@@ -216,9 +217,19 @@ export const feedbackCommand = defineJsonCommand({
|
|
|
216
217
|
// background reindex it spawned — now that ensureIndex is removed, holding
|
|
217
218
|
// the lock only causes feedback to block for the full improve run duration.
|
|
218
219
|
let utilityResult;
|
|
220
|
+
let durableRef = ref;
|
|
219
221
|
const db = openExistingDatabase();
|
|
220
222
|
try {
|
|
221
|
-
const
|
|
223
|
+
const config = loadConfig();
|
|
224
|
+
const sources = resolveSourceEntries(undefined, config);
|
|
225
|
+
const requestedSource = parsedRef.origin
|
|
226
|
+
? sources.find((source) => source.registryId === parsedRef.origin ||
|
|
227
|
+
((parsedRef.origin === "local" || parsedRef.origin === "stash") && source === sources[0]))
|
|
228
|
+
: undefined;
|
|
229
|
+
if (parsedRef.origin && !requestedSource) {
|
|
230
|
+
throw new UsageError(`Source "${parsedRef.origin}" is not configured.`, "INVALID_FLAG_VALUE");
|
|
231
|
+
}
|
|
232
|
+
const entryId = findEntryIdByRef(db, ref, requestedSource?.path);
|
|
222
233
|
if (entryId === undefined) {
|
|
223
234
|
throw new UsageError(`Ref "${ref}" is not in the index. ` +
|
|
224
235
|
"Run 'akm search' to verify the asset exists, then 'akm index' if it was recently added.");
|
|
@@ -228,9 +239,15 @@ export const feedbackCommand = defineJsonCommand({
|
|
|
228
239
|
// For negative signals, the score is adjusted the next time `akm index`
|
|
229
240
|
// runs — the signal is durable in the DB but does NOT suppress ranking
|
|
230
241
|
// in search results until after reindexing.
|
|
242
|
+
const indexedEntry = getEntryById(db, entryId);
|
|
243
|
+
const source = sources.find((candidate) => candidate.path === indexedEntry?.stashDir);
|
|
244
|
+
durableRef = refToString({
|
|
245
|
+
...parsedRef,
|
|
246
|
+
origin: parsedRef.origin ?? source?.registryId ?? "stash",
|
|
247
|
+
});
|
|
231
248
|
insertUsageEvent(db, {
|
|
232
249
|
event_type: "feedback",
|
|
233
|
-
entry_ref:
|
|
250
|
+
entry_ref: durableRef,
|
|
234
251
|
entry_id: entryId,
|
|
235
252
|
signal,
|
|
236
253
|
metadata: metadataStr,
|
|
@@ -253,7 +270,7 @@ export const feedbackCommand = defineJsonCommand({
|
|
|
253
270
|
}
|
|
254
271
|
appendEvent({
|
|
255
272
|
eventType: "feedback",
|
|
256
|
-
ref,
|
|
273
|
+
ref: durableRef,
|
|
257
274
|
metadata: metadataObj,
|
|
258
275
|
});
|
|
259
276
|
// F-5 / #386: When a high-utility asset crosses below the review threshold,
|
|
@@ -268,7 +285,7 @@ export const feedbackCommand = defineJsonCommand({
|
|
|
268
285
|
try {
|
|
269
286
|
appendEvent({
|
|
270
287
|
eventType: "improve_review_needed",
|
|
271
|
-
ref,
|
|
288
|
+
ref: durableRef,
|
|
272
289
|
metadata: {
|
|
273
290
|
previousUtility: utilityResult.previousUtility,
|
|
274
291
|
nextUtility: utilityResult.nextUtility,
|
|
@@ -6,20 +6,25 @@ import { createRequire } from "node:module";
|
|
|
6
6
|
import { loadConfig } from "../../core/config/config.js";
|
|
7
7
|
import { resolveEngine } from "../../integrations/agent/engine-resolution.js";
|
|
8
8
|
import { resolveModel } from "../../integrations/agent/model-aliases.js";
|
|
9
|
+
import { resolveImprovePlan } from "../improve/improve-strategies.js";
|
|
9
10
|
import { TASK_FAIL_RATE_WARN, } from "./types.js";
|
|
10
11
|
const ACTIVE_RUN_WARN_MS = 15 * 60 * 1000;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
function credentialAvailable(credential, env) {
|
|
13
|
+
return !credential?.required || credential.names.some((name) => Boolean(env[name]?.trim()));
|
|
14
|
+
}
|
|
15
|
+
function runConfiguredEngineProbe(checkName, engineName, config, deps) {
|
|
14
16
|
if (!engineName) {
|
|
15
17
|
return {
|
|
16
|
-
name:
|
|
18
|
+
name: checkName,
|
|
17
19
|
kind: "deterministic",
|
|
18
20
|
status: "unknown",
|
|
19
21
|
confidence: "high",
|
|
20
|
-
message: "
|
|
22
|
+
message: checkName === "default-llm-engine"
|
|
23
|
+
? "No default LLM engine is configured."
|
|
24
|
+
: "No default engine is configured.",
|
|
21
25
|
};
|
|
22
26
|
}
|
|
27
|
+
const env = deps.env ?? process.env;
|
|
23
28
|
const configuredEngine = config.engines?.[engineName];
|
|
24
29
|
if (configuredEngine?.kind === "agent" && configuredEngine.platform === "opencode-sdk") {
|
|
25
30
|
let packageAvailable = false;
|
|
@@ -36,6 +41,7 @@ export function runDefaultEngineProbe(deps = {}) {
|
|
|
36
41
|
const binaryAvailable = (version.status ?? 1) === 0;
|
|
37
42
|
const fallbackEngine = configuredEngine.llmEngine ?? config.defaults?.llmEngine;
|
|
38
43
|
let fallback;
|
|
44
|
+
let fallbackCredential;
|
|
39
45
|
let sdkRunner;
|
|
40
46
|
const resolve = deps.resolveEngine ?? resolveEngine;
|
|
41
47
|
try {
|
|
@@ -48,12 +54,15 @@ export function runDefaultEngineProbe(deps = {}) {
|
|
|
48
54
|
}
|
|
49
55
|
if (sdkRunner?.fallbackConnection) {
|
|
50
56
|
fallback = { kind: "llm", connection: sdkRunner.fallbackConnection };
|
|
57
|
+
fallbackCredential = sdkRunner.fallbackCredential;
|
|
51
58
|
}
|
|
52
59
|
else if (fallbackEngine) {
|
|
53
60
|
try {
|
|
54
61
|
const resolved = resolve(fallbackEngine, config);
|
|
55
|
-
if (resolved.kind === "llm")
|
|
62
|
+
if (resolved.kind === "llm") {
|
|
56
63
|
fallback = resolved;
|
|
64
|
+
fallbackCredential = resolved.credential;
|
|
65
|
+
}
|
|
57
66
|
}
|
|
58
67
|
catch {
|
|
59
68
|
fallback = undefined;
|
|
@@ -63,14 +72,15 @@ export function runDefaultEngineProbe(deps = {}) {
|
|
|
63
72
|
? resolveModel(configuredEngine.model, "opencode-sdk", configuredEngine.modelAliases, config.modelAliases)
|
|
64
73
|
: undefined;
|
|
65
74
|
const effectiveModel = sdkRunner?.profile.model ?? configuredModel ?? fallback?.connection.model;
|
|
75
|
+
const fallbackCredentialAvailable = credentialAvailable(fallbackCredential, env);
|
|
66
76
|
const missing = [
|
|
67
77
|
!packageAvailable ? "@opencode-ai/sdk package" : undefined,
|
|
68
78
|
!binaryAvailable ? `${binary} binary` : undefined,
|
|
69
|
-
!fallback ? "fallback LLM connection" : undefined,
|
|
70
|
-
!
|
|
79
|
+
fallbackEngine && !fallback ? "configured fallback LLM connection" : undefined,
|
|
80
|
+
!fallbackCredentialAvailable ? "required fallback credential" : undefined,
|
|
71
81
|
].filter((value) => value !== undefined);
|
|
72
82
|
return {
|
|
73
|
-
name:
|
|
83
|
+
name: checkName,
|
|
74
84
|
kind: "deterministic",
|
|
75
85
|
status: missing.length === 0 ? "pass" : "warn",
|
|
76
86
|
confidence: "high",
|
|
@@ -91,24 +101,29 @@ export function runDefaultEngineProbe(deps = {}) {
|
|
|
91
101
|
fallbackEngine: fallbackEngine ?? null,
|
|
92
102
|
fallbackEndpoint: fallback?.connection.endpoint ?? null,
|
|
93
103
|
fallbackModel: fallback?.connection.model ?? null,
|
|
104
|
+
requiredCredentialAvailable: fallbackCredentialAvailable,
|
|
94
105
|
},
|
|
95
106
|
};
|
|
96
107
|
}
|
|
97
108
|
try {
|
|
98
109
|
const runner = (deps.resolveEngine ?? resolveEngine)(engineName, config);
|
|
99
110
|
if (runner.kind === "llm") {
|
|
111
|
+
const requiredCredentialAvailable = credentialAvailable(runner.credential, env);
|
|
100
112
|
return {
|
|
101
|
-
name:
|
|
113
|
+
name: checkName,
|
|
102
114
|
kind: "deterministic",
|
|
103
|
-
status: "pass",
|
|
115
|
+
status: requiredCredentialAvailable ? "pass" : "warn",
|
|
104
116
|
confidence: "high",
|
|
105
|
-
message:
|
|
117
|
+
message: requiredCredentialAvailable
|
|
118
|
+
? `LLM engine "${engineName}" is configured.`
|
|
119
|
+
: `LLM engine "${engineName}" is configured, but its required credential is unavailable.`,
|
|
106
120
|
evidence: {
|
|
107
121
|
engine: engineName,
|
|
108
122
|
platform: null,
|
|
109
123
|
runtimeKind: "llm",
|
|
110
124
|
model: runner.connection.model,
|
|
111
125
|
endpoint: runner.connection.endpoint,
|
|
126
|
+
requiredCredentialAvailable,
|
|
112
127
|
},
|
|
113
128
|
};
|
|
114
129
|
}
|
|
@@ -118,7 +133,7 @@ export function runDefaultEngineProbe(deps = {}) {
|
|
|
118
133
|
const version = (deps.spawnSync ?? spawnSync)(profile.bin, ["--version"], { encoding: "utf8", timeout: 5_000 });
|
|
119
134
|
if ((version.status ?? 1) !== 0) {
|
|
120
135
|
return {
|
|
121
|
-
name:
|
|
136
|
+
name: checkName,
|
|
122
137
|
kind: "deterministic",
|
|
123
138
|
status: "warn",
|
|
124
139
|
confidence: "medium",
|
|
@@ -132,7 +147,7 @@ export function runDefaultEngineProbe(deps = {}) {
|
|
|
132
147
|
};
|
|
133
148
|
}
|
|
134
149
|
return {
|
|
135
|
-
name:
|
|
150
|
+
name: checkName,
|
|
136
151
|
kind: "deterministic",
|
|
137
152
|
status: "pass",
|
|
138
153
|
confidence: "high",
|
|
@@ -147,7 +162,7 @@ export function runDefaultEngineProbe(deps = {}) {
|
|
|
147
162
|
}
|
|
148
163
|
catch (error) {
|
|
149
164
|
return {
|
|
150
|
-
name:
|
|
165
|
+
name: checkName,
|
|
151
166
|
kind: "deterministic",
|
|
152
167
|
status: "warn",
|
|
153
168
|
confidence: "high",
|
|
@@ -155,6 +170,60 @@ export function runDefaultEngineProbe(deps = {}) {
|
|
|
155
170
|
};
|
|
156
171
|
}
|
|
157
172
|
}
|
|
173
|
+
export function runDefaultEngineProbe(deps = {}) {
|
|
174
|
+
const config = deps.loadConfig?.() ?? loadConfig();
|
|
175
|
+
return runConfiguredEngineProbe("default-engine", config.defaults?.engine, config, deps);
|
|
176
|
+
}
|
|
177
|
+
export function runDefaultLlmEngineProbe(deps = {}) {
|
|
178
|
+
const config = deps.loadConfig?.() ?? loadConfig();
|
|
179
|
+
return runConfiguredEngineProbe("default-llm-engine", config.defaults?.llmEngine, config, deps);
|
|
180
|
+
}
|
|
181
|
+
export function runActiveImproveStrategyProbe(deps = {}) {
|
|
182
|
+
const config = deps.loadConfig?.() ?? loadConfig();
|
|
183
|
+
const strategyName = config.defaults?.improveStrategy ?? "default";
|
|
184
|
+
try {
|
|
185
|
+
const plan = resolveImprovePlan(strategyName, config);
|
|
186
|
+
const env = deps.env ?? process.env;
|
|
187
|
+
const unavailableProcesses = Object.entries(plan.processes).flatMap(([name, process]) => {
|
|
188
|
+
if (!process.enabled || !process.runner)
|
|
189
|
+
return [];
|
|
190
|
+
return credentialAvailable(process.runner.credential, env) ? [] : [name];
|
|
191
|
+
});
|
|
192
|
+
if (plan.triageJudgment) {
|
|
193
|
+
const judgmentCredential = plan.triageJudgment.kind === "llm"
|
|
194
|
+
? plan.triageJudgment.credential
|
|
195
|
+
: plan.triageJudgment.kind === "sdk"
|
|
196
|
+
? plan.triageJudgment.fallbackCredential
|
|
197
|
+
: undefined;
|
|
198
|
+
if (!credentialAvailable(judgmentCredential, env))
|
|
199
|
+
unavailableProcesses.push("triage.judgment");
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
name: "active-improve-strategy",
|
|
203
|
+
kind: "deterministic",
|
|
204
|
+
status: unavailableProcesses.length === 0 ? "pass" : "warn",
|
|
205
|
+
confidence: "high",
|
|
206
|
+
message: unavailableProcesses.length === 0
|
|
207
|
+
? `Active improve strategy "${plan.strategy.name}" has available process engines.`
|
|
208
|
+
: `Active improve strategy "${plan.strategy.name}" has unavailable required credentials for: ${unavailableProcesses.join(", ")}.`,
|
|
209
|
+
evidence: {
|
|
210
|
+
strategy: plan.strategy.name,
|
|
211
|
+
unavailableProcesses,
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
const explicitlyConfigured = config.defaults?.improveStrategy !== undefined || Object.keys(config.improve?.strategies ?? {}).length > 0;
|
|
217
|
+
return {
|
|
218
|
+
name: "active-improve-strategy",
|
|
219
|
+
kind: "deterministic",
|
|
220
|
+
status: explicitlyConfigured ? "warn" : "unknown",
|
|
221
|
+
confidence: "high",
|
|
222
|
+
message: `Active improve strategy "${strategyName}" is unavailable: ${error instanceof Error ? error.message : String(error)}`,
|
|
223
|
+
evidence: { strategy: strategyName, unavailableProcesses: [] },
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
158
227
|
/**
|
|
159
228
|
* The ordered health-check registry. ORDER IS LOAD-BEARING: `akmHealth`
|
|
160
229
|
* iterates this array and appends to hardChecks/advisories in sequence, so the
|
|
@@ -236,6 +305,16 @@ export const HEALTH_CHECKS = [
|
|
|
236
305
|
channel: "hard",
|
|
237
306
|
run: () => runDefaultEngineProbe(),
|
|
238
307
|
},
|
|
308
|
+
{
|
|
309
|
+
name: "default-llm-engine",
|
|
310
|
+
channel: "hard",
|
|
311
|
+
run: () => runDefaultLlmEngineProbe(),
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
name: "active-improve-strategy",
|
|
315
|
+
channel: "hard",
|
|
316
|
+
run: () => runActiveImproveStrategyProbe(),
|
|
317
|
+
},
|
|
239
318
|
{
|
|
240
319
|
// C2 (13-bus-factor): the cron task-failure rate was computed and rendered
|
|
241
320
|
// in the HTML report but never surfaced as an advisory, so a sustained
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import fs from "node:fs";
|
|
20
20
|
import path from "node:path";
|
|
21
|
+
import { MAX_CONFIG_FILE_BYTES, readTextFileWithLimit } from "../../core/common.js";
|
|
21
22
|
import { CURRENT_CONFIG_VERSION } from "../../core/config/config-schema.js";
|
|
22
23
|
import { compareConfigVersion } from "../../core/config/config-version.js";
|
|
23
24
|
const GROUP_OTHER_BITS = 0o077;
|
|
@@ -98,7 +99,7 @@ export function collectSecretPermsAdvisory(input, platform = process.platform) {
|
|
|
98
99
|
export function collectConfigSkewAdvisory(configPath) {
|
|
99
100
|
let raw;
|
|
100
101
|
try {
|
|
101
|
-
raw = JSON.parse(
|
|
102
|
+
raw = JSON.parse(readTextFileWithLimit(configPath, MAX_CONFIG_FILE_BYTES, "Config file"));
|
|
102
103
|
}
|
|
103
104
|
catch {
|
|
104
105
|
return undefined;
|
|
@@ -72,8 +72,8 @@ function distinctTokens(text) {
|
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
74
|
* A merge must strictly increase information (R5 §4.2):
|
|
75
|
-
* 1. Provenance: the merged asset's `
|
|
76
|
-
*
|
|
75
|
+
* 1. Provenance: the merged asset's `xrefs` must be a superset of the union of
|
|
76
|
+
* all participants' `xrefs` plus the participant refs
|
|
77
77
|
* themselves — provenance never shrinks through a merge.
|
|
78
78
|
* 2. Specificity: distinctTokens(mergedBody) ≥ minSpecificityRetention ×
|
|
79
79
|
* |union(distinctTokens(participant bodies))| — a merge that only
|
|
@@ -111,7 +111,7 @@ export function checkMergeInformationFloor(mergedBody, mergedSourceRefs, partici
|
|
|
111
111
|
const specificityOk = specificityRetention >= minRetention;
|
|
112
112
|
const reasons = [];
|
|
113
113
|
if (!provenanceOk) {
|
|
114
|
-
reasons.push(`provenance shrank: merged
|
|
114
|
+
reasons.push(`provenance shrank: merged xrefs missing ${missing.length} ref(s) (e.g. ${missing[0]})`);
|
|
115
115
|
}
|
|
116
116
|
if (!specificityOk) {
|
|
117
117
|
reasons.push(`specificity retention ${specificityRetention.toFixed(2)} < ${minRetention} (merge genericized/shortened)`);
|
|
@@ -175,10 +175,10 @@ export function normHash(text) {
|
|
|
175
175
|
return hash.toString(16);
|
|
176
176
|
}
|
|
177
177
|
/**
|
|
178
|
-
* Score one canary against the live index, merge-following via
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
178
|
+
* Score one canary against the live index, merge-following via canonical
|
|
179
|
+
* `xrefs`. Legacy `source_refs` remains readable for assets written before the
|
|
180
|
+
* xref cutover. A hit is the anchor ref itself OR any returned entry whose
|
|
181
|
+
* provenance contains the anchor.
|
|
182
182
|
* Returns the 0-based rank of the first hit, or -1.
|
|
183
183
|
*/
|
|
184
184
|
function scoreCanary(indexDb, canary, k) {
|
|
@@ -188,7 +188,7 @@ function scoreCanary(indexDb, canary, k) {
|
|
|
188
188
|
const ref = makeAssetRef(r.entry.type, r.entry.name);
|
|
189
189
|
if (ref === canary.anchor_ref)
|
|
190
190
|
return i;
|
|
191
|
-
if (r.entry.sourceRefs?.includes(canary.anchor_ref))
|
|
191
|
+
if (r.entry.xrefs?.includes(canary.anchor_ref) || r.entry.sourceRefs?.includes(canary.anchor_ref))
|
|
192
192
|
return i;
|
|
193
193
|
}
|
|
194
194
|
return -1;
|