knodin 0.13.0 → 0.14.0
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/dist/bin/cli.js +120 -9
- package/dist/src/backup-retention.js +345 -12
- package/dist/src/class-consumer-cursor.js +2 -0
- package/dist/src/cli-model.js +12 -0
- package/dist/src/compact-structural.js +7 -2
- package/dist/src/engine/apex-class-uses.js +54 -8
- package/dist/src/engine/apex-dead-chains.js +368 -0
- package/dist/src/engine/apex-entry-points.js +44 -8
- package/dist/src/engine/candidate-database.js +624 -66
- package/dist/src/engine/index.js +1768 -258
- package/dist/src/engine/seal.js +28 -4
- package/dist/src/engine/state-paths.js +192 -31
- package/dist/src/init.js +14 -2
- package/dist/src/manager-update.js +9 -10
- package/dist/src/mirror.js +2 -0
- package/dist/src/response-budget.js +14 -2
- package/dist/src/shared-index/restore.js +16 -6
- package/dist/src/storage-budget.js +146 -0
- package/dist/src/storage-inventory.js +263 -0
- package/dist/src/storage-management.js +204 -0
- package/dist/src/storage-policy-contract.js +121 -0
- package/dist/src/tools/knodin-tools.js +1 -0
- package/dist/src/worktree-seed.js +35 -2
- package/docs/BACKUP-RETENTION.md +46 -2
- package/docs/CLI.md +12 -5
- package/docs/DEAD-CODE-AND-IMPACT.md +41 -0
- package/docs/MCP.md +15 -3
- package/docs/SALESFORCE-BINDINGS.md +26 -8
- package/docs/releases/0.13.1.md +216 -0
- package/docs/releases/0.14.0.md +78 -0
- package/package.json +4 -1
package/dist/bin/cli.js
CHANGED
|
@@ -31,6 +31,7 @@ import { exportContext, grepPackedArtifact, readPackedArtifact } from "../src/co
|
|
|
31
31
|
import { clearDiagnostics, collectDiagnostics, diagnosticsStatus, disableDiagnostics, enableDiagnostics, inspectDiagnosticsBundle, persistDiagnosticsPreview, recordDiagnosticFailure, } from "../src/diagnostics.js";
|
|
32
32
|
import { getDocSection, listDocTopics } from "../src/docs-sections.js";
|
|
33
33
|
import { diagnoseInstallation } from "../src/doctor.js";
|
|
34
|
+
import { sweepAbandonedCandidates } from "../src/engine/candidate-database.js";
|
|
34
35
|
import { createEngine, describeThrown, KNODIN_SCHEMA_VERSION, REPO_WIDE_QUERY_PATTERNS, } from "../src/engine/index.js";
|
|
35
36
|
import { runSeal } from "../src/engine/seal-command.js";
|
|
36
37
|
import { runSealedQuery } from "../src/engine/sealed-query.js";
|
|
@@ -60,6 +61,8 @@ import { detectRepositorySignals, discoverRepositories, formatRepositoryHuman, i
|
|
|
60
61
|
import { applyResponseBudget } from "../src/response-budget.js";
|
|
61
62
|
import { appendSessionEvent, clearSessionTelemetry, disableSessionTelemetry, enableSessionTelemetry, readSessionEvents, sessionTelemetryStatus, } from "../src/session-telemetry.js";
|
|
62
63
|
import { inspectKnodinSkills, installKnodinSkills, KNODIN_SKILLS, removeKnodinSkills, } from "../src/skill-management.js";
|
|
64
|
+
import { inventoryStorage } from "../src/storage-inventory.js";
|
|
65
|
+
import { configureStoragePolicy, postOperationStorageMaintenance, resolveStoragePolicy, } from "../src/storage-management.js";
|
|
63
66
|
import { configuredRepositoryInitMemoryLimitBytes, enrichSystemRelationships, incorporateSystemQueryEvidence, indexModeForPath, loadSystemConfiguration, queryConfiguredSystem, systemMembershipsForPath, validateSystemHealth, } from "../src/system-config.js";
|
|
64
67
|
import { applySystemPlan, planSystemImport, planSystemLink, planSystemRelate, planSystemUnlink, planSystemUnrelate, } from "../src/system-management.js";
|
|
65
68
|
import { coordinationStatus } from "../src/update-coordination.js";
|
|
@@ -154,7 +157,17 @@ function formatRepairHuman(result) {
|
|
|
154
157
|
result.after.missing.files.length + result.after.missing.records.length;
|
|
155
158
|
const firstIssue = result.after.missing.files[0] ?? result.after.missing.records[0];
|
|
156
159
|
const detail = firstIssue ? ` First issue: ${firstIssue}.` : "";
|
|
157
|
-
|
|
160
|
+
// Say when nothing moved: on nova the same 111-issue graph went through
|
|
161
|
+
// status -> repair -> status unchanged, and "Repair finished with 141
|
|
162
|
+
// remaining issue(s)" read as progress. Then print the audit's own remedy
|
|
163
|
+
// rather than a generic pointer; for a legacy epoch-less graph the audit
|
|
164
|
+
// already knows repair cannot fix it and index --clean can (KNODIN-50).
|
|
165
|
+
const unchanged = result.repaired.length === 0 &&
|
|
166
|
+
result.before.missing.files.length === result.after.missing.files.length &&
|
|
167
|
+
result.before.missing.records.length === result.after.missing.records.length;
|
|
168
|
+
const progress = unchanged ? "Repair made no changes; " : "Repair finished with ";
|
|
169
|
+
const remedy = result.after.repairSteps[0] ?? "Run `knodin status --deep` for details.";
|
|
170
|
+
return `${progress}${outstanding.toLocaleString()} remaining issue(s).${detail} ${remedy}\n`;
|
|
158
171
|
}
|
|
159
172
|
/**
|
|
160
173
|
* The semantic gap, stated rather than left to be discovered.
|
|
@@ -195,7 +208,8 @@ function formatIndexVerificationError(result) {
|
|
|
195
208
|
const condition = result.verification.issueCount > 0
|
|
196
209
|
? `${result.verification.issueCount.toLocaleString()} graph issue(s) remain`
|
|
197
210
|
: `graph verification reported status "${result.verification.status}"`;
|
|
198
|
-
|
|
211
|
+
const remedy = result.verification.repairSteps[0] ?? "Run `knodin repair`.";
|
|
212
|
+
return `knodin index: requested work completed, but ${condition}.${detail} ${remedy}\n`;
|
|
199
213
|
}
|
|
200
214
|
/**
|
|
201
215
|
* The hooks/lifecycle line of `status`.
|
|
@@ -498,7 +512,14 @@ function formatStatusHuman(result) {
|
|
|
498
512
|
const lifecycleOnly = result.lifecycle?.status === "degraded" &&
|
|
499
513
|
(result.coverage.countsUnknown || result.missing.files.length === 0) &&
|
|
500
514
|
result.missing.records.every((record) => result.lifecycle?.issues.includes(record));
|
|
501
|
-
|
|
515
|
+
// Otherwise the engine's first step is authoritative too: it distinguishes
|
|
516
|
+
// "rebuild damaged rows" from "this graph predates the content-proof epoch
|
|
517
|
+
// and only index --clean can certify it" — a case where sending the user to
|
|
518
|
+
// repair costs two failing round trips (KNODIN-50).
|
|
519
|
+
const repairCommand = worktreeStep ??
|
|
520
|
+
(lifecycleOnly
|
|
521
|
+
? "Run `knodin repair --lifecycle`."
|
|
522
|
+
: (result.repairSteps?.[0] ?? "Run `knodin repair`."));
|
|
502
523
|
return `Graph or lifecycle needs repair: ${outstanding} issue(s) found (${coverage}).${detail} ${repairCommand}\n${lifecycleLine}${integrationLine}`;
|
|
503
524
|
}
|
|
504
525
|
function humanLabel(key) {
|
|
@@ -1289,6 +1310,8 @@ async function main() {
|
|
|
1289
1310
|
const depth = invocation.options.depth;
|
|
1290
1311
|
const retentionDays = invocation.options.retentionDays;
|
|
1291
1312
|
const keepNewest = invocation.options.keepNewest;
|
|
1313
|
+
const maxCount = invocation.options.maxCount;
|
|
1314
|
+
const maxAllocatedBytes = invocation.options.maxBytes;
|
|
1292
1315
|
let output;
|
|
1293
1316
|
if (action === "list")
|
|
1294
1317
|
output = listBackups(defaultRoots(), { depth });
|
|
@@ -1297,16 +1320,70 @@ async function main() {
|
|
|
1297
1320
|
depth,
|
|
1298
1321
|
retentionDays,
|
|
1299
1322
|
keepNewest,
|
|
1323
|
+
maxCount,
|
|
1324
|
+
maxAllocatedBytes,
|
|
1300
1325
|
apply: invocation.options.apply === true,
|
|
1301
1326
|
});
|
|
1302
1327
|
}
|
|
1328
|
+
else if (action === "policy") {
|
|
1329
|
+
output = defaultRoots().map((repository) => {
|
|
1330
|
+
if (retentionAction === "status")
|
|
1331
|
+
return {
|
|
1332
|
+
repository,
|
|
1333
|
+
policy: resolveStoragePolicy(repository),
|
|
1334
|
+
inventory: inventoryStorage(repository),
|
|
1335
|
+
};
|
|
1336
|
+
if (retentionAction !== "preview" && retentionAction !== "apply")
|
|
1337
|
+
throw new Error("knodin backups policy requires preview, apply, or status");
|
|
1338
|
+
const changes = {
|
|
1339
|
+
apply: retentionAction === "apply",
|
|
1340
|
+
};
|
|
1341
|
+
if (maxCount !== undefined)
|
|
1342
|
+
changes.maxCount = maxCount;
|
|
1343
|
+
if (maxAllocatedBytes !== undefined)
|
|
1344
|
+
changes.maxAllocatedBytes = maxAllocatedBytes;
|
|
1345
|
+
if (invocation.options.reserveBytes !== undefined)
|
|
1346
|
+
changes.minFreeBytes = invocation.options.reserveBytes;
|
|
1347
|
+
const mode = invocation.options.mode;
|
|
1348
|
+
if (mode !== undefined) {
|
|
1349
|
+
changes.enabled = mode !== "disabled";
|
|
1350
|
+
changes.dryRun = mode === "preview";
|
|
1351
|
+
}
|
|
1352
|
+
const configured = configureStoragePolicy(repository, changes);
|
|
1353
|
+
const maintenance = retentionAction === "apply" ? postOperationStorageMaintenance(repository) : null;
|
|
1354
|
+
const candidateWarnings = [];
|
|
1355
|
+
const candidatesRemoved = maintenance?.policy?.enabled && !maintenance.policy.dryRun
|
|
1356
|
+
? sweepAbandonedCandidates(repository, {
|
|
1357
|
+
onDiagnostic: (warning) => {
|
|
1358
|
+
if (candidateWarnings.length < 8)
|
|
1359
|
+
candidateWarnings.push(warning.reason.slice(0, 512));
|
|
1360
|
+
},
|
|
1361
|
+
})
|
|
1362
|
+
: 0;
|
|
1363
|
+
return {
|
|
1364
|
+
repository,
|
|
1365
|
+
...configured,
|
|
1366
|
+
inventory: inventoryStorage(repository),
|
|
1367
|
+
cleanup: retentionAction === "apply"
|
|
1368
|
+
? { ...maintenance, candidatesRemoved, candidateWarnings }
|
|
1369
|
+
: pruneBackups([repository], {
|
|
1370
|
+
...configured.policy,
|
|
1371
|
+
depth: 0,
|
|
1372
|
+
includeRegistry: false,
|
|
1373
|
+
apply: false,
|
|
1374
|
+
}),
|
|
1375
|
+
};
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1303
1378
|
else if (action === "retention" && retentionAction === "install") {
|
|
1304
1379
|
output = installBackupRetention(roots, {
|
|
1305
1380
|
depth,
|
|
1306
1381
|
retentionDays,
|
|
1307
1382
|
keepNewest,
|
|
1383
|
+
maxCount,
|
|
1384
|
+
maxAllocatedBytes,
|
|
1308
1385
|
schedule: invocation.options.schedule,
|
|
1309
|
-
dryRun: invocation.options.dryRun
|
|
1386
|
+
dryRun: invocation.options.dryRun,
|
|
1310
1387
|
launcher: runtimeCommand,
|
|
1311
1388
|
});
|
|
1312
1389
|
}
|
|
@@ -1389,10 +1466,19 @@ async function main() {
|
|
|
1389
1466
|
const repo = resolved.repo;
|
|
1390
1467
|
if (cmd !== "doctor") {
|
|
1391
1468
|
try {
|
|
1392
|
-
maybeRunOpportunisticRetention(repo);
|
|
1469
|
+
const maintenance = maybeRunOpportunisticRetention(repo);
|
|
1470
|
+
const issues = "issues" in maintenance
|
|
1471
|
+
? maintenance.issues
|
|
1472
|
+
: "result" in maintenance && maintenance.result
|
|
1473
|
+
? maintenance.result.skipped.map((entry) => entry.reason)
|
|
1474
|
+
: [];
|
|
1475
|
+
if (issues?.length)
|
|
1476
|
+
process.stderr.write(`knodin storage: ${issues.slice(0, 8).join("; ").slice(0, 4096)}\n`);
|
|
1393
1477
|
}
|
|
1394
|
-
catch {
|
|
1395
|
-
//
|
|
1478
|
+
catch (error) {
|
|
1479
|
+
// Cleanup remains nonfatal, but failures are not hidden or reported as
|
|
1480
|
+
// reclaimed storage. Keep stdout/JSON framing untouched.
|
|
1481
|
+
process.stderr.write(`knodin storage: ${String(error).slice(0, 512)}\n`);
|
|
1396
1482
|
}
|
|
1397
1483
|
}
|
|
1398
1484
|
if (cmd === "agent-event" && !fs.existsSync(resolveDbPath(repo)))
|
|
@@ -2168,10 +2254,34 @@ async function main() {
|
|
|
2168
2254
|
throw new Error("knodin hook-refresh: invalid lifecycle event");
|
|
2169
2255
|
}
|
|
2170
2256
|
const shared = await opportunisticSharedRestore();
|
|
2257
|
+
// Keep the audit that the scoped index already ran. Before this the
|
|
2258
|
+
// hook reported only the paths it wrote and exited 0, so a graph the
|
|
2259
|
+
// audit refused to certify — nova's 0.13.0 graph after the 0.13.1
|
|
2260
|
+
// upgrade, 33 commits behind with lifecycle "healthy" — looked like a
|
|
2261
|
+
// success on every commit (KNODIN-49).
|
|
2262
|
+
let verification;
|
|
2171
2263
|
const indexed = shared?.restored
|
|
2172
2264
|
? []
|
|
2173
|
-
: await refreshFromGitEvent(repo, event, (target, files) =>
|
|
2174
|
-
|
|
2265
|
+
: await refreshFromGitEvent(repo, event, async (target, files) => {
|
|
2266
|
+
const indexResult = await engine.index(target, files);
|
|
2267
|
+
verification = indexResult.verification;
|
|
2268
|
+
return indexResult;
|
|
2269
|
+
});
|
|
2270
|
+
if (verification?.status === "repair-needed") {
|
|
2271
|
+
const firstIssue = verification.missing.records[0] ?? verification.missing.files[0] ?? "unknown issue";
|
|
2272
|
+
const remedy = verification.repairSteps[0] ?? "Run `knodin repair`.";
|
|
2273
|
+
throw new Error(`knodin hook-refresh: indexed ${indexed.length} path(s) but the graph audit refused to certify the result (${verification.issueCount.toLocaleString()} issue(s); first: ${firstIssue}). ${remedy}`);
|
|
2274
|
+
}
|
|
2275
|
+
// `at` and `event` make each indexer.log record self-describing; the
|
|
2276
|
+
// background script supplies the queued event's sequence through
|
|
2277
|
+
// KNODIN_HOOK_EVENT_SEQUENCE so the line can be tied to its Git event.
|
|
2278
|
+
result = {
|
|
2279
|
+
at: new Date().toISOString(),
|
|
2280
|
+
event: { ...event, sequence: process.env.KNODIN_HOOK_EVENT_SEQUENCE ?? null },
|
|
2281
|
+
indexed,
|
|
2282
|
+
...(verification ? { verification } : {}),
|
|
2283
|
+
...(shared ? { sharedRestore: shared } : {}),
|
|
2284
|
+
};
|
|
2175
2285
|
}
|
|
2176
2286
|
finally {
|
|
2177
2287
|
lifecycleLease.release();
|
|
@@ -2525,6 +2635,7 @@ async function main() {
|
|
|
2525
2635
|
? {
|
|
2526
2636
|
mode: "source",
|
|
2527
2637
|
identity: explained.identity,
|
|
2638
|
+
platformEntry: explained.platformEntry,
|
|
2528
2639
|
symbol: explained.symbol,
|
|
2529
2640
|
source: explained.source,
|
|
2530
2641
|
staleness: explained.staleness,
|