@inerrata-corporation/errata 2.0.22 → 2.0.23-dev.4689
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/THIRD-PARTY-NOTICES.md +1080 -0
- package/consolidate-worker.mjs +1868 -159
- package/errata.mjs +6966 -2415
- package/generalize-worker.mjs +1698 -97
- package/package.json +3 -2
- package/pass-worker.mjs +2307 -361
- package/resources/context-plugin/.codex-plugin/plugin.json +1 -1
- package/resources/context-plugin/SHA256SUMS +3 -3
- package/resources/context-plugin/THIRD-PARTY-NOTICES.md +5 -7
- package/resources/context-plugin/dist/mcp-server.mjs +545 -148
package/pass-worker.mjs
CHANGED
|
@@ -959,10 +959,10 @@ function mergeDefs(...defs) {
|
|
|
959
959
|
function cloneDef(schema) {
|
|
960
960
|
return mergeDefs(schema._zod.def);
|
|
961
961
|
}
|
|
962
|
-
function getElementAtPath(obj,
|
|
963
|
-
if (!
|
|
962
|
+
function getElementAtPath(obj, path2) {
|
|
963
|
+
if (!path2)
|
|
964
964
|
return obj;
|
|
965
|
-
return
|
|
965
|
+
return path2.reduce((acc, key) => acc?.[key], obj);
|
|
966
966
|
}
|
|
967
967
|
function promiseAllObject(promisesObj) {
|
|
968
968
|
const keys = Object.keys(promisesObj);
|
|
@@ -1274,11 +1274,11 @@ function aborted(x, startIndex = 0) {
|
|
|
1274
1274
|
}
|
|
1275
1275
|
return false;
|
|
1276
1276
|
}
|
|
1277
|
-
function prefixIssues(
|
|
1277
|
+
function prefixIssues(path2, issues) {
|
|
1278
1278
|
return issues.map((iss) => {
|
|
1279
1279
|
var _a2;
|
|
1280
1280
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
1281
|
-
iss.path.unshift(
|
|
1281
|
+
iss.path.unshift(path2);
|
|
1282
1282
|
return iss;
|
|
1283
1283
|
});
|
|
1284
1284
|
}
|
|
@@ -1520,7 +1520,7 @@ function formatError(error48, mapper = (issue2) => issue2.message) {
|
|
|
1520
1520
|
}
|
|
1521
1521
|
function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
1522
1522
|
const result = { errors: [] };
|
|
1523
|
-
const processError = (error49,
|
|
1523
|
+
const processError = (error49, path2 = []) => {
|
|
1524
1524
|
var _a2, _b;
|
|
1525
1525
|
for (const issue2 of error49.issues) {
|
|
1526
1526
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -1530,7 +1530,7 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
|
1530
1530
|
} else if (issue2.code === "invalid_element") {
|
|
1531
1531
|
processError({ issues: issue2.issues }, issue2.path);
|
|
1532
1532
|
} else {
|
|
1533
|
-
const fullpath = [...
|
|
1533
|
+
const fullpath = [...path2, ...issue2.path];
|
|
1534
1534
|
if (fullpath.length === 0) {
|
|
1535
1535
|
result.errors.push(mapper(issue2));
|
|
1536
1536
|
continue;
|
|
@@ -1562,8 +1562,8 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
|
|
|
1562
1562
|
}
|
|
1563
1563
|
function toDotPath(_path) {
|
|
1564
1564
|
const segs = [];
|
|
1565
|
-
const
|
|
1566
|
-
for (const seg of
|
|
1565
|
+
const path2 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
1566
|
+
for (const seg of path2) {
|
|
1567
1567
|
if (typeof seg === "number")
|
|
1568
1568
|
segs.push(`[${seg}]`);
|
|
1569
1569
|
else if (typeof seg === "symbol")
|
|
@@ -14257,13 +14257,13 @@ function resolveRef(ref, ctx) {
|
|
|
14257
14257
|
if (!ref.startsWith("#")) {
|
|
14258
14258
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
14259
14259
|
}
|
|
14260
|
-
const
|
|
14261
|
-
if (
|
|
14260
|
+
const path2 = ref.slice(1).split("/").filter(Boolean);
|
|
14261
|
+
if (path2.length === 0) {
|
|
14262
14262
|
return ctx.rootSchema;
|
|
14263
14263
|
}
|
|
14264
14264
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
14265
|
-
if (
|
|
14266
|
-
const key =
|
|
14265
|
+
if (path2[0] === defsKey) {
|
|
14266
|
+
const key = path2[1];
|
|
14267
14267
|
if (!key || !ctx.defs[key]) {
|
|
14268
14268
|
throw new Error(`Reference not found: ${ref}`);
|
|
14269
14269
|
}
|
|
@@ -15535,8 +15535,8 @@ function npmPurl(name2, version3) {
|
|
|
15535
15535
|
function component(name2, version3) {
|
|
15536
15536
|
return { purl: npmPurl(name2, version3), name: name2, version: version3, ecosystem: "npm" };
|
|
15537
15537
|
}
|
|
15538
|
-
function isNpmImporterPath(
|
|
15539
|
-
return
|
|
15538
|
+
function isNpmImporterPath(path2) {
|
|
15539
|
+
return path2 === "" || !path2.includes("node_modules/");
|
|
15540
15540
|
}
|
|
15541
15541
|
function parsePackageLockJson(jsonText) {
|
|
15542
15542
|
let doc;
|
|
@@ -15549,22 +15549,22 @@ function parsePackageLockJson(jsonText) {
|
|
|
15549
15549
|
const components = [];
|
|
15550
15550
|
const hoisted = /* @__PURE__ */ new Map();
|
|
15551
15551
|
const byPath = /* @__PURE__ */ new Map();
|
|
15552
|
-
for (const [
|
|
15553
|
-
if (
|
|
15554
|
-
const ix =
|
|
15552
|
+
for (const [path2, meta3] of Object.entries(packages)) {
|
|
15553
|
+
if (path2 === "" || meta3.link === true) continue;
|
|
15554
|
+
const ix = path2.lastIndexOf("node_modules/");
|
|
15555
15555
|
if (ix === -1) continue;
|
|
15556
|
-
const name2 =
|
|
15556
|
+
const name2 = path2.slice(ix + "node_modules/".length);
|
|
15557
15557
|
const version3 = typeof meta3.version === "string" ? meta3.version : "";
|
|
15558
15558
|
if (!name2 || !version3) continue;
|
|
15559
15559
|
components.push(component(name2, version3));
|
|
15560
|
-
byPath.set(
|
|
15561
|
-
const depth =
|
|
15560
|
+
byPath.set(path2, { name: name2, version: version3 });
|
|
15561
|
+
const depth = path2.split("node_modules/").length;
|
|
15562
15562
|
const prev = hoisted.get(name2);
|
|
15563
15563
|
if (!prev || depth < prev.depth) hoisted.set(name2, { version: version3, depth });
|
|
15564
15564
|
}
|
|
15565
15565
|
const dependencies = [];
|
|
15566
|
-
for (const [
|
|
15567
|
-
const self = byPath.get(
|
|
15566
|
+
for (const [path2, meta3] of Object.entries(packages)) {
|
|
15567
|
+
const self = byPath.get(path2);
|
|
15568
15568
|
if (!self) continue;
|
|
15569
15569
|
const deps = meta3.dependencies;
|
|
15570
15570
|
if (!deps || typeof deps !== "object") continue;
|
|
@@ -15577,8 +15577,8 @@ function parsePackageLockJson(jsonText) {
|
|
|
15577
15577
|
dependencies.push({ ref: npmPurl(self.name, self.version), dependsOn });
|
|
15578
15578
|
}
|
|
15579
15579
|
const directRaw = [];
|
|
15580
|
-
for (const [
|
|
15581
|
-
if (!isNpmImporterPath(
|
|
15580
|
+
for (const [path2, meta3] of Object.entries(packages)) {
|
|
15581
|
+
if (!isNpmImporterPath(path2)) continue;
|
|
15582
15582
|
for (const section of [meta3.dependencies, meta3.devDependencies, meta3.optionalDependencies]) {
|
|
15583
15583
|
if (!section || typeof section !== "object") continue;
|
|
15584
15584
|
for (const name2 of Object.keys(section)) {
|
|
@@ -15825,31 +15825,31 @@ function basename(p) {
|
|
|
15825
15825
|
const ix = p.lastIndexOf("/");
|
|
15826
15826
|
return ix === -1 ? p : p.slice(ix + 1);
|
|
15827
15827
|
}
|
|
15828
|
-
function parseCsproj(
|
|
15828
|
+
function parseCsproj(path2, text) {
|
|
15829
15829
|
const deps = [];
|
|
15830
15830
|
for (const m of text.matchAll(/<PackageReference\s+Include="([^"]+)"(?:\s+Version="([^"]+)")?/g)) {
|
|
15831
|
-
deps.push({ ecosystem: "nuget", name: m[1] ?? "", version: m[2] ?? null, kind: "package", manifest:
|
|
15831
|
+
deps.push({ ecosystem: "nuget", name: m[1] ?? "", version: m[2] ?? null, kind: "package", manifest: path2 });
|
|
15832
15832
|
}
|
|
15833
15833
|
for (const m of text.matchAll(/<Project\s+Sdk="([^"/]+)(?:\/([^"]+))?"/g)) {
|
|
15834
15834
|
const sdk = m[1] ?? "";
|
|
15835
15835
|
if (sdk.toLowerCase() !== "microsoft.net.sdk") {
|
|
15836
|
-
deps.push({ ecosystem: "nuget", name: sdk, version: m[2] ?? null, kind: "sdk", manifest:
|
|
15836
|
+
deps.push({ ecosystem: "nuget", name: sdk, version: m[2] ?? null, kind: "sdk", manifest: path2 });
|
|
15837
15837
|
}
|
|
15838
15838
|
}
|
|
15839
15839
|
for (const m of text.matchAll(/<TargetFramework>([^<]+)<\/TargetFramework>/g)) {
|
|
15840
15840
|
const mm = /^net(\d+\.\d+)/.exec((m[1] ?? "").trim());
|
|
15841
|
-
if (mm) deps.push({ ecosystem: "dotnet", name: "dotnet", version: mm[1] ?? null, kind: "runtime", manifest:
|
|
15841
|
+
if (mm) deps.push({ ecosystem: "dotnet", name: "dotnet", version: mm[1] ?? null, kind: "runtime", manifest: path2 });
|
|
15842
15842
|
}
|
|
15843
15843
|
return deps;
|
|
15844
15844
|
}
|
|
15845
|
-
function parseGodot(
|
|
15845
|
+
function parseGodot(path2, text) {
|
|
15846
15846
|
const m = /config\/features=PackedStringArray\(([^)]*)\)/.exec(text);
|
|
15847
15847
|
if (!m) return [];
|
|
15848
15848
|
const feats = (m[1] ?? "").split(",").map((f) => f.trim().replace(/^"|"$/g, "")).filter((f) => f.length > 0);
|
|
15849
15849
|
const version3 = feats.find((f) => /^\d+\.\d+/.test(f)) ?? null;
|
|
15850
|
-
return [{ ecosystem: "godot", name: "godot", version: version3, kind: "engine", manifest:
|
|
15850
|
+
return [{ ecosystem: "godot", name: "godot", version: version3, kind: "engine", manifest: path2 }];
|
|
15851
15851
|
}
|
|
15852
|
-
function parsePackageJson(
|
|
15852
|
+
function parsePackageJson(path2, text) {
|
|
15853
15853
|
let obj;
|
|
15854
15854
|
try {
|
|
15855
15855
|
obj = JSON.parse(text);
|
|
@@ -15863,7 +15863,7 @@ function parsePackageJson(path, text) {
|
|
|
15863
15863
|
if (!section || typeof section !== "object") continue;
|
|
15864
15864
|
for (const [name2, ver] of Object.entries(section)) {
|
|
15865
15865
|
const version3 = ver ? String(ver) : null;
|
|
15866
|
-
deps.push({ ecosystem: "npm", name: name2, version: version3, kind: "package", manifest:
|
|
15866
|
+
deps.push({ ecosystem: "npm", name: name2, version: version3, kind: "package", manifest: path2 });
|
|
15867
15867
|
}
|
|
15868
15868
|
}
|
|
15869
15869
|
return deps;
|
|
@@ -15871,7 +15871,7 @@ function parsePackageJson(path, text) {
|
|
|
15871
15871
|
function isPep508DirectRef(spec) {
|
|
15872
15872
|
return /^[A-Za-z0-9][A-Za-z0-9._-]*(?:\[[^\]]*\])?\s*@\s*\S/.test(spec.trim());
|
|
15873
15873
|
}
|
|
15874
|
-
function parseRequirements(
|
|
15874
|
+
function parseRequirements(path2, text) {
|
|
15875
15875
|
const deps = [];
|
|
15876
15876
|
for (const raw of text.split(/\r?\n/)) {
|
|
15877
15877
|
const line = (raw.split("#", 1)[0] ?? "").trim();
|
|
@@ -15883,14 +15883,14 @@ function parseRequirements(path, text) {
|
|
|
15883
15883
|
name: m[1] ?? "",
|
|
15884
15884
|
version: m[2] ? m[3] ?? null : null,
|
|
15885
15885
|
kind: "package",
|
|
15886
|
-
manifest:
|
|
15886
|
+
manifest: path2,
|
|
15887
15887
|
...isPep508DirectRef(line) ? { directRef: true } : {}
|
|
15888
15888
|
});
|
|
15889
15889
|
}
|
|
15890
15890
|
}
|
|
15891
15891
|
return deps;
|
|
15892
15892
|
}
|
|
15893
|
-
function parsePyproject(
|
|
15893
|
+
function parsePyproject(path2, text) {
|
|
15894
15894
|
const deps = [];
|
|
15895
15895
|
const m = /^\s*dependencies\s*=\s*\[([\s\S]*?)\]/m.exec(text);
|
|
15896
15896
|
if (!m) return deps;
|
|
@@ -15903,14 +15903,14 @@ function parsePyproject(path, text) {
|
|
|
15903
15903
|
name: mm[1] ?? "",
|
|
15904
15904
|
version: null,
|
|
15905
15905
|
kind: "package",
|
|
15906
|
-
manifest:
|
|
15906
|
+
manifest: path2,
|
|
15907
15907
|
...isPep508DirectRef(spec) ? { directRef: true } : {}
|
|
15908
15908
|
});
|
|
15909
15909
|
}
|
|
15910
15910
|
}
|
|
15911
15911
|
return deps;
|
|
15912
15912
|
}
|
|
15913
|
-
function parseDocker(
|
|
15913
|
+
function parseDocker(path2, text) {
|
|
15914
15914
|
const deps = [];
|
|
15915
15915
|
for (const m of text.matchAll(/^\s*(?:image:\s*|FROM\s+(?:--platform=\S+\s+)?)["']?([^\s"']+)/gm)) {
|
|
15916
15916
|
const ref = m[1] ?? "";
|
|
@@ -15920,32 +15920,32 @@ function parseDocker(path, text) {
|
|
|
15920
15920
|
const colon = undigested.indexOf(":");
|
|
15921
15921
|
const name2 = colon === -1 ? undigested : undigested.slice(0, colon);
|
|
15922
15922
|
const tag = colon === -1 ? "" : undigested.slice(colon + 1);
|
|
15923
|
-
deps.push({ ecosystem: "docker", name: name2, version: tag || null, kind: "image", manifest:
|
|
15923
|
+
deps.push({ ecosystem: "docker", name: name2, version: tag || null, kind: "image", manifest: path2 });
|
|
15924
15924
|
}
|
|
15925
15925
|
return deps;
|
|
15926
15926
|
}
|
|
15927
|
-
function parseGoMod(
|
|
15927
|
+
function parseGoMod(path2, text) {
|
|
15928
15928
|
const deps = [];
|
|
15929
15929
|
for (const m of text.matchAll(/^\s*(?:require\s+)?([a-z0-9.\-]+\.[a-z]+\/[^\s]+)\s+(v[^\s]+)/gm)) {
|
|
15930
15930
|
const end = (m.index ?? 0) + m[0].length;
|
|
15931
15931
|
if (text.slice(end, end + 20).includes("// indirect")) continue;
|
|
15932
|
-
deps.push({ ecosystem: "golang", name: m[1] ?? "", version: m[2] ?? null, kind: "package", manifest:
|
|
15932
|
+
deps.push({ ecosystem: "golang", name: m[1] ?? "", version: m[2] ?? null, kind: "package", manifest: path2 });
|
|
15933
15933
|
}
|
|
15934
15934
|
return deps;
|
|
15935
15935
|
}
|
|
15936
|
-
function parseCargo(
|
|
15936
|
+
function parseCargo(path2, text) {
|
|
15937
15937
|
const deps = [];
|
|
15938
15938
|
const sec = /^\[dependencies\]([\s\S]*?)(?=^\[|(?![\s\S]))/m.exec(text);
|
|
15939
15939
|
if (!sec) return deps;
|
|
15940
15940
|
for (const m of (sec[1] ?? "").matchAll(/^\s*([A-Za-z0-9_-]+)\s*=\s*(?:"([^"]+)"|\{[^}]*version\s*=\s*"([^"]+)")/gm)) {
|
|
15941
|
-
deps.push({ ecosystem: "cargo", name: m[1] ?? "", version: m[2] ?? m[3] ?? null, kind: "package", manifest:
|
|
15941
|
+
deps.push({ ecosystem: "cargo", name: m[1] ?? "", version: m[2] ?? m[3] ?? null, kind: "package", manifest: path2 });
|
|
15942
15942
|
}
|
|
15943
15943
|
return deps;
|
|
15944
15944
|
}
|
|
15945
15945
|
function mavenName(groupId, artifactId) {
|
|
15946
15946
|
return `${groupId.trim()}:${artifactId.trim()}`;
|
|
15947
15947
|
}
|
|
15948
|
-
function parsePom(
|
|
15948
|
+
function parsePom(path2, text) {
|
|
15949
15949
|
const deps = [];
|
|
15950
15950
|
const coord = (block) => {
|
|
15951
15951
|
const g = /<groupId>([^<]+)<\/groupId>/.exec(block)?.[1];
|
|
@@ -15955,15 +15955,15 @@ function parsePom(path, text) {
|
|
|
15955
15955
|
};
|
|
15956
15956
|
for (const m of text.matchAll(/<dependency>([\s\S]*?)<\/dependency>/g)) {
|
|
15957
15957
|
const c = coord(m[1] ?? "");
|
|
15958
|
-
if (c) deps.push({ ecosystem: "maven", name: mavenName(c.g, c.a), version: c.v, kind: "package", manifest:
|
|
15958
|
+
if (c) deps.push({ ecosystem: "maven", name: mavenName(c.g, c.a), version: c.v, kind: "package", manifest: path2 });
|
|
15959
15959
|
}
|
|
15960
15960
|
for (const m of text.matchAll(/<parent>([\s\S]*?)<\/parent>/g)) {
|
|
15961
15961
|
const c = coord(m[1] ?? "");
|
|
15962
|
-
if (c) deps.push({ ecosystem: "maven", name: mavenName(c.g, c.a), version: c.v, kind: "sdk", manifest:
|
|
15962
|
+
if (c) deps.push({ ecosystem: "maven", name: mavenName(c.g, c.a), version: c.v, kind: "sdk", manifest: path2 });
|
|
15963
15963
|
}
|
|
15964
15964
|
return deps;
|
|
15965
15965
|
}
|
|
15966
|
-
function parseGradle(
|
|
15966
|
+
function parseGradle(path2, text) {
|
|
15967
15967
|
const deps = [];
|
|
15968
15968
|
const re = new RegExp(`\\b(?:${GRADLE_CONFIGS})\\s*(?:\\(\\s*)?["']([^"']+)["']`, "g");
|
|
15969
15969
|
for (const m of text.matchAll(re)) {
|
|
@@ -15975,12 +15975,12 @@ function parseGradle(path, text) {
|
|
|
15975
15975
|
name: mavenName(group, artifact),
|
|
15976
15976
|
version: version3?.trim() || null,
|
|
15977
15977
|
kind: "package",
|
|
15978
|
-
manifest:
|
|
15978
|
+
manifest: path2
|
|
15979
15979
|
});
|
|
15980
15980
|
}
|
|
15981
15981
|
return deps;
|
|
15982
15982
|
}
|
|
15983
|
-
function parseVersionCatalog(
|
|
15983
|
+
function parseVersionCatalog(path2, text) {
|
|
15984
15984
|
const section = /^\s*\[libraries\]([\s\S]*?)(?=^\s*\[|(?![\s\S]))/m.exec(text);
|
|
15985
15985
|
if (!section) return [];
|
|
15986
15986
|
const deps = [];
|
|
@@ -15991,34 +15991,34 @@ function parseVersionCatalog(path, text) {
|
|
|
15991
15991
|
if (module2) {
|
|
15992
15992
|
const [group2, artifact2] = module2.split(":");
|
|
15993
15993
|
if (group2 && artifact2) {
|
|
15994
|
-
deps.push({ ecosystem: "maven", name: mavenName(group2, artifact2), version: version3, kind: "package", manifest:
|
|
15994
|
+
deps.push({ ecosystem: "maven", name: mavenName(group2, artifact2), version: version3, kind: "package", manifest: path2 });
|
|
15995
15995
|
}
|
|
15996
15996
|
continue;
|
|
15997
15997
|
}
|
|
15998
15998
|
const group = /\bgroup\s*=\s*["']([^"']+)["']/.exec(line)?.[1];
|
|
15999
15999
|
const artifact = /\bname\s*=\s*["']([^"']+)["']/.exec(line)?.[1];
|
|
16000
16000
|
if (group && artifact) {
|
|
16001
|
-
deps.push({ ecosystem: "maven", name: mavenName(group, artifact), version: version3, kind: "package", manifest:
|
|
16001
|
+
deps.push({ ecosystem: "maven", name: mavenName(group, artifact), version: version3, kind: "package", manifest: path2 });
|
|
16002
16002
|
}
|
|
16003
16003
|
}
|
|
16004
16004
|
return deps;
|
|
16005
16005
|
}
|
|
16006
|
-
function parseGemfile(
|
|
16006
|
+
function parseGemfile(path2, text) {
|
|
16007
16007
|
const deps = [];
|
|
16008
16008
|
for (const raw of text.split(/\r?\n/)) {
|
|
16009
16009
|
const line = (raw.split("#", 1)[0] ?? "").trim();
|
|
16010
16010
|
const m = /^gem\s+["']([^"']+)["']\s*(?:,\s*["']([^"']+)["'])?/.exec(line);
|
|
16011
16011
|
if (!m?.[1]) continue;
|
|
16012
|
-
deps.push({ ecosystem: "gem", name: m[1], version: m[2] ?? null, kind: "package", manifest:
|
|
16012
|
+
deps.push({ ecosystem: "gem", name: m[1], version: m[2] ?? null, kind: "package", manifest: path2 });
|
|
16013
16013
|
}
|
|
16014
16014
|
return deps;
|
|
16015
16015
|
}
|
|
16016
|
-
function parseGemspec(
|
|
16016
|
+
function parseGemspec(path2, text) {
|
|
16017
16017
|
const deps = [];
|
|
16018
16018
|
for (const m of text.matchAll(
|
|
16019
16019
|
/\.add_(?:runtime_|development_)?dependency\s*\(?\s*["']([^"']+)["']\s*(?:,\s*["']([^"']+)["'])?/g
|
|
16020
16020
|
)) {
|
|
16021
|
-
if (m[1]) deps.push({ ecosystem: "gem", name: m[1], version: m[2] ?? null, kind: "package", manifest:
|
|
16021
|
+
if (m[1]) deps.push({ ecosystem: "gem", name: m[1], version: m[2] ?? null, kind: "package", manifest: path2 });
|
|
16022
16022
|
}
|
|
16023
16023
|
return deps;
|
|
16024
16024
|
}
|
|
@@ -16039,8 +16039,8 @@ function pomSelf(text) {
|
|
|
16039
16039
|
if (artifact) self.push({ ecosystem: "maven", kind: "name", value: mavenName(group, artifact) });
|
|
16040
16040
|
return self;
|
|
16041
16041
|
}
|
|
16042
|
-
function parseManifestSelf(
|
|
16043
|
-
const file2 = basename(
|
|
16042
|
+
function parseManifestSelf(path2, text) {
|
|
16043
|
+
const file2 = basename(path2);
|
|
16044
16044
|
if (file2 === "package.json") {
|
|
16045
16045
|
let obj;
|
|
16046
16046
|
try {
|
|
@@ -16082,23 +16082,23 @@ function parseManifestSelf(path, text) {
|
|
|
16082
16082
|
}
|
|
16083
16083
|
return [];
|
|
16084
16084
|
}
|
|
16085
|
-
function parseManifest(
|
|
16086
|
-
const name2 = basename(
|
|
16087
|
-
if (name2.endsWith(".csproj") || name2.endsWith(".fsproj")) return parseCsproj(
|
|
16088
|
-
if (name2 === "project.godot") return parseGodot(
|
|
16089
|
-
if (name2 === "package.json") return parsePackageJson(
|
|
16090
|
-
if (name2.startsWith("requirements") && name2.endsWith(".txt")) return parseRequirements(
|
|
16091
|
-
if (name2 === "pyproject.toml") return parsePyproject(
|
|
16085
|
+
function parseManifest(path2, text) {
|
|
16086
|
+
const name2 = basename(path2);
|
|
16087
|
+
if (name2.endsWith(".csproj") || name2.endsWith(".fsproj")) return parseCsproj(path2, text);
|
|
16088
|
+
if (name2 === "project.godot") return parseGodot(path2, text);
|
|
16089
|
+
if (name2 === "package.json") return parsePackageJson(path2, text);
|
|
16090
|
+
if (name2.startsWith("requirements") && name2.endsWith(".txt")) return parseRequirements(path2, text);
|
|
16091
|
+
if (name2 === "pyproject.toml") return parsePyproject(path2, text);
|
|
16092
16092
|
if (name2.startsWith("docker-compose") || name2.startsWith("compose") || name2.startsWith("Dockerfile")) {
|
|
16093
|
-
return parseDocker(
|
|
16094
|
-
}
|
|
16095
|
-
if (name2 === "go.mod") return parseGoMod(
|
|
16096
|
-
if (name2 === "libs.versions.toml") return parseVersionCatalog(
|
|
16097
|
-
if (name2 === "Cargo.toml") return parseCargo(
|
|
16098
|
-
if (name2 === "pom.xml") return parsePom(
|
|
16099
|
-
if (name2 === "build.gradle" || name2 === "build.gradle.kts") return parseGradle(
|
|
16100
|
-
if (name2 === "Gemfile") return parseGemfile(
|
|
16101
|
-
if (name2.endsWith(".gemspec")) return parseGemspec(
|
|
16093
|
+
return parseDocker(path2, text);
|
|
16094
|
+
}
|
|
16095
|
+
if (name2 === "go.mod") return parseGoMod(path2, text);
|
|
16096
|
+
if (name2 === "libs.versions.toml") return parseVersionCatalog(path2, text);
|
|
16097
|
+
if (name2 === "Cargo.toml") return parseCargo(path2, text);
|
|
16098
|
+
if (name2 === "pom.xml") return parsePom(path2, text);
|
|
16099
|
+
if (name2 === "build.gradle" || name2 === "build.gradle.kts") return parseGradle(path2, text);
|
|
16100
|
+
if (name2 === "Gemfile") return parseGemfile(path2, text);
|
|
16101
|
+
if (name2.endsWith(".gemspec")) return parseGemspec(path2, text);
|
|
16102
16102
|
return [];
|
|
16103
16103
|
}
|
|
16104
16104
|
function buildSpine(sha2, manifestPaths2, readText) {
|
|
@@ -16172,7 +16172,7 @@ var init_history_read = __esm({
|
|
|
16172
16172
|
"../../packages/shared/src/history-read.ts"() {
|
|
16173
16173
|
"use strict";
|
|
16174
16174
|
init_history_dependencies();
|
|
16175
|
-
CLOUD_ATTR_DUPLICATE_KEYS = ["attrsJson", "embedding"];
|
|
16175
|
+
CLOUD_ATTR_DUPLICATE_KEYS = ["attrsJson", "embedding", "structEmbedding", "structEmbeddedAt"];
|
|
16176
16176
|
}
|
|
16177
16177
|
});
|
|
16178
16178
|
|
|
@@ -16184,7 +16184,7 @@ var init_history_temporal = __esm({
|
|
|
16184
16184
|
});
|
|
16185
16185
|
|
|
16186
16186
|
// ../../packages/shared/src/history-revision.ts
|
|
16187
|
-
var Sha, HistoryRevisionRequestSchema;
|
|
16187
|
+
var Sha, HistoryRevisionRequestSchema, HistoryCarryRequestSchema, HISTORY_REVIEW_BUDGET, HISTORY_REVIEW_POLICY;
|
|
16188
16188
|
var init_history_revision = __esm({
|
|
16189
16189
|
"../../packages/shared/src/history-revision.ts"() {
|
|
16190
16190
|
"use strict";
|
|
@@ -16204,6 +16204,17 @@ var init_history_revision = __esm({
|
|
|
16204
16204
|
dependencyContext: HistoryDependencyContextSchema.optional()
|
|
16205
16205
|
})).max(50)
|
|
16206
16206
|
});
|
|
16207
|
+
HistoryCarryRequestSchema = external_exports.strictObject({
|
|
16208
|
+
fromCommit: Sha,
|
|
16209
|
+
atCommit: Sha,
|
|
16210
|
+
parents: external_exports.array(external_exports.strictObject({ commit: Sha, parents: external_exports.array(Sha).max(32) })).max(8192),
|
|
16211
|
+
/** Every symbol in a file the range changed, both sides; `null` = too many to name, treat all as changed. */
|
|
16212
|
+
changedSymbols: external_exports.array(external_exports.string().regex(/^sym_[a-f0-9]{8,64}$/)).max(5e3).nullable(),
|
|
16213
|
+
/** Manifests the range changed; `null` = a lockfile (or too many manifests) changed, treat all as changed. */
|
|
16214
|
+
changedManifests: external_exports.array(external_exports.string().regex(/^hmanifest_[a-f0-9]{32}$/)).max(512).nullable()
|
|
16215
|
+
});
|
|
16216
|
+
HISTORY_REVIEW_BUDGET = 48e3;
|
|
16217
|
+
HISTORY_REVIEW_POLICY = `reviewer-view-${HISTORY_REVIEW_BUDGET}`;
|
|
16207
16218
|
}
|
|
16208
16219
|
});
|
|
16209
16220
|
|
|
@@ -16387,13 +16398,13 @@ function loadNodeSqlite() {
|
|
|
16387
16398
|
process.emitWarning = original;
|
|
16388
16399
|
}
|
|
16389
16400
|
}
|
|
16390
|
-
function openDatabase(
|
|
16391
|
-
const db = opts ? new DatabaseSync(
|
|
16401
|
+
function openDatabase(path2, opts) {
|
|
16402
|
+
const db = opts ? new DatabaseSync(path2, opts) : new DatabaseSync(path2);
|
|
16392
16403
|
try {
|
|
16393
16404
|
db.exec("PRAGMA foreign_keys = ON");
|
|
16394
16405
|
} catch {
|
|
16395
16406
|
}
|
|
16396
|
-
if (
|
|
16407
|
+
if (path2 !== ":memory:" && !opts?.readOnly) {
|
|
16397
16408
|
try {
|
|
16398
16409
|
db.exec("PRAGMA journal_mode = WAL");
|
|
16399
16410
|
db.exec("PRAGMA synchronous = NORMAL");
|
|
@@ -16402,20 +16413,20 @@ function openDatabase(path, opts) {
|
|
|
16402
16413
|
} catch {
|
|
16403
16414
|
}
|
|
16404
16415
|
}
|
|
16405
|
-
const wrapStmt = (
|
|
16416
|
+
const wrapStmt = (stmt2) => ({
|
|
16406
16417
|
run: (...params) => {
|
|
16407
|
-
const r =
|
|
16418
|
+
const r = stmt2.run(...coerceParams(params));
|
|
16408
16419
|
return {
|
|
16409
16420
|
changes: Number(r.changes),
|
|
16410
16421
|
lastInsertRowid: Number(r.lastInsertRowid)
|
|
16411
16422
|
};
|
|
16412
16423
|
},
|
|
16413
16424
|
get: (...params) => {
|
|
16414
|
-
const r =
|
|
16425
|
+
const r = stmt2.get(...coerceParams(params));
|
|
16415
16426
|
return r;
|
|
16416
16427
|
},
|
|
16417
16428
|
all: (...params) => {
|
|
16418
|
-
const r =
|
|
16429
|
+
const r = stmt2.all(...coerceParams(params));
|
|
16419
16430
|
return r;
|
|
16420
16431
|
}
|
|
16421
16432
|
});
|
|
@@ -16476,6 +16487,548 @@ var init_sqlite_adapter = __esm({
|
|
|
16476
16487
|
}
|
|
16477
16488
|
});
|
|
16478
16489
|
|
|
16490
|
+
// ../../packages/local-shared/src/tag-grammar.ts
|
|
16491
|
+
function tagGrammarCopy() {
|
|
16492
|
+
const copy = [];
|
|
16493
|
+
for (const ref of ["its-handle", "its-id", "h", "id"]) {
|
|
16494
|
+
for (const build of Object.values(TAG_EXAMPLES)) copy.push(build(ref));
|
|
16495
|
+
}
|
|
16496
|
+
for (const v of TAG_VERBS) {
|
|
16497
|
+
copy.push(v.hint);
|
|
16498
|
+
if (v.taughtBy) copy.push(v.taughtBy);
|
|
16499
|
+
for (const q of v.qualifiers ?? []) copy.push(q.note);
|
|
16500
|
+
for (const l of v.lowering) {
|
|
16501
|
+
if (l.note) copy.push(l.note);
|
|
16502
|
+
if (l.knownViolation) copy.push(l.knownViolation.finding);
|
|
16503
|
+
}
|
|
16504
|
+
}
|
|
16505
|
+
for (const f of TAG_FENCES) copy.push(f.hint);
|
|
16506
|
+
return copy;
|
|
16507
|
+
}
|
|
16508
|
+
function normalizeTemplateBody(body2) {
|
|
16509
|
+
return body2.replace(/…|\.{3}/g, " ").replace(/\s+/g, " ").trim().replace(/^[\s,;:—–-]+|[\s,;:.—–-]+$/g, "").toLowerCase();
|
|
16510
|
+
}
|
|
16511
|
+
function deriveTagTemplates(copy) {
|
|
16512
|
+
const bodies = /* @__PURE__ */ new Set();
|
|
16513
|
+
const handles = new Set([...TAG_PLACEHOLDER_HANDLES].map((h) => h.toLowerCase()));
|
|
16514
|
+
for (const text of copy) {
|
|
16515
|
+
for (const m of text.matchAll(BRACKET_TOKEN)) {
|
|
16516
|
+
const token = m[1].trim().toLowerCase();
|
|
16517
|
+
if (!/^[!?]/.test(token) && !REAL_HANDLE_SHAPE.test(token)) handles.add(token);
|
|
16518
|
+
}
|
|
16519
|
+
}
|
|
16520
|
+
for (const text of copy) {
|
|
16521
|
+
for (const m of text.matchAll(TEMPLATE_VERB_GROUP)) {
|
|
16522
|
+
const body2 = normalizeTemplateBody(m[2]);
|
|
16523
|
+
bodies.add(body2);
|
|
16524
|
+
const unthreaded = normalizeTemplateBody(body2.replace(/^#[a-z][\w-]*/i, ""));
|
|
16525
|
+
bodies.add(unthreaded);
|
|
16526
|
+
const rest = normalizeTemplateBody(unthreaded.replace(BRACKET_TOKEN, (t, h) => handles.has(h.trim().toLowerCase()) ? " " : t).replace(/,/g, " "));
|
|
16527
|
+
bodies.add(rest);
|
|
16528
|
+
}
|
|
16529
|
+
}
|
|
16530
|
+
bodies.delete("");
|
|
16531
|
+
return { bodies, handles };
|
|
16532
|
+
}
|
|
16533
|
+
var TAG_CANONICAL_ID_PREFIXES, TAG_HANDLE_SOURCE, TAG_THREAD_SOURCE, TAG_PLACEHOLDER_HANDLES, TAG_EXAMPLES, P, S, RC, CITE_LOWERING, INSTANCE_LOWERING, TRIAGE_TO_RC, TAG_VERBS, TAG_FENCES, TAG_VERB_BY_NAME, TAG_PAREN_VERBS, TAG_GRAMMAR_OPENERS, TAG_QUOTED_PAREN_SOURCE, TAG_QUOTED_RE, TAG_QUOTED_PAREN_RE, TEMPLATE_VERB_GROUP, BRACKET_TOKEN, REAL_HANDLE_SHAPE, TAG_TEMPLATES, VERB_WORDS_SOURCE, JOINED_VERB, LEADING_VERB, ANY_VERB_WORD, QUOTED_TAG_OPENER;
|
|
16534
|
+
var init_tag_grammar = __esm({
|
|
16535
|
+
"../../packages/local-shared/src/tag-grammar.ts"() {
|
|
16536
|
+
"use strict";
|
|
16537
|
+
TAG_CANONICAL_ID_PREFIXES = ["pat", "sol", "drc", "dcause", "dfix", "dprob", "dvuln", "prob", "claim", "err"];
|
|
16538
|
+
TAG_HANDLE_SOURCE = String.raw`\[([a-z0-9][\w-]{0,80})\]|((?:${TAG_CANONICAL_ID_PREFIXES.join("|")})_[0-9a-f]{6,})`;
|
|
16539
|
+
TAG_THREAD_SOURCE = String.raw`#([a-z][\w-]{0,63})`;
|
|
16540
|
+
TAG_PLACEHOLDER_HANDLES = /* @__PURE__ */ new Set([
|
|
16541
|
+
"handle",
|
|
16542
|
+
"its-handle",
|
|
16543
|
+
"another",
|
|
16544
|
+
"a-third",
|
|
16545
|
+
"h",
|
|
16546
|
+
"h1",
|
|
16547
|
+
"h2",
|
|
16548
|
+
"id",
|
|
16549
|
+
"its-id",
|
|
16550
|
+
"a",
|
|
16551
|
+
"b",
|
|
16552
|
+
"\u2026",
|
|
16553
|
+
"..."
|
|
16554
|
+
]);
|
|
16555
|
+
TAG_EXAMPLES = {
|
|
16556
|
+
prior: (ref) => `([${ref}])`,
|
|
16557
|
+
problem: () => `[!one line on what's wrong]`,
|
|
16558
|
+
vulnerability: () => `[!vuln: who can do what; observed basis @ path:line]`,
|
|
16559
|
+
suspectedVulnerability: () => `[?vuln: consequence; missing check]`,
|
|
16560
|
+
vulnerabilityThread: () => `[!#v vuln: finding here]`,
|
|
16561
|
+
fix: (ref) => `(fix:[${ref}])`,
|
|
16562
|
+
// CONSTRAINT — the native capture path for a DIFFUSE problem (a constraint/tension/
|
|
16563
|
+
// conflict that shaped a decision). Free text, attaches to the decision sentence the
|
|
16564
|
+
// agent is already writing. Full rationale in the constraint note below + prior-tags.ts.
|
|
16565
|
+
constraint: () => `(constraint: the limitation that forced it)`,
|
|
16566
|
+
// TRIAGE — routes the problem/symptom in scope to its cause. `(cause:[handle])`
|
|
16567
|
+
// links to a cited RootCause/Solution node; `(cause: free text)` mints a RootCause
|
|
16568
|
+
// from the text. Harvested in prior-tags.ts → engine.ts (see the triage note below).
|
|
16569
|
+
triage: (ref) => `(cause:[${ref}])`,
|
|
16570
|
+
// Unlike a bare cause citation, this handle names the Problem being explained.
|
|
16571
|
+
triageExplain: (ref) => `(cause:[${ref}] the mechanism)`,
|
|
16572
|
+
// The MINT form — free-text cause that mints a RootCause. Prime-time surfacing
|
|
16573
|
+
// (recallForFile) pastes this next to an open Problem that has no cause yet, so the
|
|
16574
|
+
// diagnostic moment is cued when the agent is looking at the problem. Kept here so
|
|
16575
|
+
// the grammar can't drift from parseInlineTags' `(cause: …)` acceptance.
|
|
16576
|
+
triageMint: () => `(cause: why it happens)`,
|
|
16577
|
+
// THREAD forms — the witness-declared symptom↔cause/fix binding. A `#id` on the
|
|
16578
|
+
// flag names the diagnosis thread; the same `#id` on a later verb binds to it
|
|
16579
|
+
// explicitly (across sentences AND turns), replacing proximity guessing. Adjacent
|
|
16580
|
+
// tags never need one — the id is for diagnoses that unfold over distance.
|
|
16581
|
+
problemThread: () => `[!#slug one line on what's wrong]`,
|
|
16582
|
+
causeThread: () => `(cause:#slug the mechanism)`,
|
|
16583
|
+
fixThread: (thread = "slug") => `(fix:#${thread} what you changed)`,
|
|
16584
|
+
// ATTEMPT arc — what's being tried and what didn't work. A failed attempt is a
|
|
16585
|
+
// witnessed NEGATIVE result: it saves the next agent the same dead end.
|
|
16586
|
+
tried: () => `(tried: the approach you're attempting)`,
|
|
16587
|
+
failed: () => `(failed: what didn't work and the dead end it rules out)`,
|
|
16588
|
+
// `(failed:[h] …)` on a shown SOLUTION — the refute-by-failure form the context render teaches.
|
|
16589
|
+
failedCite: (ref) => `(failed:[${ref}] what came back)`,
|
|
16590
|
+
// LINK verbs (AC epic) — bind the problem in scope to CANONICAL priors and
|
|
16591
|
+
// abstractions. Multi-target by design: single links are weak; ~3 links to 3
|
|
16592
|
+
// DIFFERENT relevant priors is the target shape (see linkBullet).
|
|
16593
|
+
instance: (ref) => `(instance:[${ref}],[another],[a-third])`,
|
|
16594
|
+
pattern: () => `(pattern: name the general shape)`,
|
|
16595
|
+
// DOMAIN — the abstract area a problem is about; the concept layer an
|
|
16596
|
+
// anchor-less problem clusters on. Mints/resolves a Domain by name.
|
|
16597
|
+
domain: () => `(domain: The Area)`,
|
|
16598
|
+
// PACKAGE/COMPONENT — agent-named public anchors (OM-agent-anchors): the
|
|
16599
|
+
// package a problem is ABOUT (even when not a dependency) and the
|
|
16600
|
+
// framework/product-level unit it concerns. Public spine anchors → the
|
|
16601
|
+
// knowledge can cross to the collective.
|
|
16602
|
+
package: () => `(package: the-package-name)`,
|
|
16603
|
+
component: () => `(component: The Component)`,
|
|
16604
|
+
aids: (ref) => `(aids:[${ref}])`,
|
|
16605
|
+
// SOLUTION-STRUCTURE (SS-structure) — taught in the link bullet; Solution→Solution.
|
|
16606
|
+
buildsOn: (ref) => `(builds-on:[${ref}])`,
|
|
16607
|
+
supersedes: (ref) => `(supersedes:[${ref}])`,
|
|
16608
|
+
alternative: (ref) => `(alternative:[${ref}])`,
|
|
16609
|
+
// VERDICT (VS-verdict-tags) — a judgment on a SHOWN prior, distinct from leaning on it.
|
|
16610
|
+
// `confirm` rides the corroborate channel (it held); `refute` the contradict channel
|
|
16611
|
+
// (it's wrong / didn't hold) and REQUIRES a reason. This is the inline producer the
|
|
16612
|
+
// evidence channels lacked — refute sat dead (contradictionPressure 0) until now, while
|
|
16613
|
+
// lean-on corroboration lived, because only corroboration had an inline path. Grammar
|
|
16614
|
+
// single-sourced with parseInlineTags' CONFIRM_PREFIX/REFUTE_PREFIX.
|
|
16615
|
+
confirm: (ref) => `(confirm:[${ref}])`,
|
|
16616
|
+
refute: (ref) => `(refute:[${ref}] one line why)`,
|
|
16617
|
+
// RETRACT — "not a real problem": the errata-resolved block with a disposition
|
|
16618
|
+
// (parseDesignResolutions, turn.ts). No inline form exists; the block is the grammar.
|
|
16619
|
+
retract: (id) => ["```errata-resolved", `id: ${id}`, "disposition: false_positive", "reason: why it was never a real problem", "```"].join("\n")
|
|
16620
|
+
};
|
|
16621
|
+
P = "Problem";
|
|
16622
|
+
S = "Solution";
|
|
16623
|
+
RC = "RootCause";
|
|
16624
|
+
CITE_LOWERING = [
|
|
16625
|
+
{ target: RC, writes: [{ from: P, edge: "CAUSED_BY", to: RC }] },
|
|
16626
|
+
{ target: S, writes: [{ from: P, edge: "SOLVED_BY", to: S }] },
|
|
16627
|
+
{ target: "Pattern", writes: [{ from: P, edge: "INSTANCE_OF", to: "Pattern" }, { from: "Pattern", edge: "ANCHORED_AT", to: "File" }], note: "cite-anchor promotion after 2 sessions", decision: "d", knownViolation: { edge: "ANCHORED_AT", finding: "motif cite-anchor promotion writes a refused ANCHORED_AT" } },
|
|
16628
|
+
{ target: "Package", writes: [{ from: P, edge: "DEPENDS_ON", to: "Package" }] },
|
|
16629
|
+
{ target: "Language", writes: [{ from: P, edge: "OCCURS_IN", to: "Language" }] },
|
|
16630
|
+
{ target: "Tool", writes: [{ from: P, edge: "CONCERNS", to: "Tool" }] },
|
|
16631
|
+
{ target: "Component", writes: [{ from: P, edge: "CONCERNS", to: "Component" }] },
|
|
16632
|
+
{ target: P, writes: [{ from: P, edge: "RELATES_TO", to: P }, { from: P, edge: "ANCHORED_AT", to: "File" }], note: "corroboration witness + cite-anchor promotion" },
|
|
16633
|
+
{ target: "AntiPattern", writes: [{ from: P, edge: "RELATES_TO", to: "AntiPattern" }, { from: "AntiPattern", edge: "ANCHORED_AT", to: "File" }], decision: "e", knownViolation: { edge: "ANCHORED_AT", finding: "motif cite-anchor promotion writes a refused ANCHORED_AT" } },
|
|
16634
|
+
{ target: "Technique", writes: [{ from: P, edge: "RELATES_TO", to: "Technique" }, { from: "Technique", edge: "ANCHORED_AT", to: "File" }], decision: "e", knownViolation: { edge: "ANCHORED_AT", finding: "motif cite-anchor promotion writes a refused ANCHORED_AT" } },
|
|
16635
|
+
{ target: "Weakness", writes: [{ from: P, edge: "RELATES_TO", to: "Weakness" }], decision: "e" },
|
|
16636
|
+
{ target: "Domain", writes: [{ from: P, edge: "RELATES_TO", to: "Domain" }], decision: "e" },
|
|
16637
|
+
{ target: "Claim", writes: [{ from: P, edge: "RELATES_TO", to: "Claim" }, { from: "Claim", edge: "ANCHORED_AT", to: "File" }] }
|
|
16638
|
+
];
|
|
16639
|
+
INSTANCE_LOWERING = [
|
|
16640
|
+
{ target: "Pattern", writes: [{ from: P, edge: "INSTANCE_OF", to: "Pattern" }] },
|
|
16641
|
+
{ target: P, writes: [{ from: P, edge: "RELATES_TO", to: P }] },
|
|
16642
|
+
{ target: S, writes: [{ from: P, edge: "SOLVED_BY", to: S }], note: "an instance cite on a Solution claims the problem is solved by it", decision: "a" },
|
|
16643
|
+
{ target: RC, writes: [{ from: P, edge: "CAUSED_BY", to: RC }] },
|
|
16644
|
+
{ target: "AntiPattern", writes: [{ from: P, edge: "RELATES_TO", to: "AntiPattern" }], decision: "e" },
|
|
16645
|
+
{ target: "Technique", writes: [{ from: P, edge: "RELATES_TO", to: "Technique" }], decision: "e" },
|
|
16646
|
+
{ target: "Weakness", writes: [{ from: P, edge: "RELATES_TO", to: "Weakness" }], decision: "e" },
|
|
16647
|
+
{ target: "Domain", writes: [{ from: P, edge: "RELATES_TO", to: "Domain" }], decision: "e" },
|
|
16648
|
+
{ target: "Package", writes: [{ from: P, edge: "DEPENDS_ON", to: "Package" }] },
|
|
16649
|
+
{ target: "Component", writes: [{ from: P, edge: "CONCERNS", to: "Component" }] }
|
|
16650
|
+
];
|
|
16651
|
+
TRIAGE_TO_RC = [
|
|
16652
|
+
{ from: P, edge: "TRIAGED_BY", to: "Triage" },
|
|
16653
|
+
{ from: "Triage", edge: "INDICATES", to: RC },
|
|
16654
|
+
{ from: P, edge: "CAUSED_BY", to: RC }
|
|
16655
|
+
];
|
|
16656
|
+
TAG_VERBS = [
|
|
16657
|
+
{
|
|
16658
|
+
verb: "cite",
|
|
16659
|
+
form: "citation",
|
|
16660
|
+
opener: "(",
|
|
16661
|
+
kinds: ["prior"],
|
|
16662
|
+
args: "handles",
|
|
16663
|
+
thread: false,
|
|
16664
|
+
handleRequired: true,
|
|
16665
|
+
lowering: CITE_LOWERING,
|
|
16666
|
+
examples: ["prior"],
|
|
16667
|
+
hint: "cite a shown prior you leaned on: ([its-handle]) \u2014 handles only, no prose inside the parens",
|
|
16668
|
+
taught: "taught"
|
|
16669
|
+
},
|
|
16670
|
+
{
|
|
16671
|
+
verb: "!",
|
|
16672
|
+
form: "flag",
|
|
16673
|
+
opener: "[!",
|
|
16674
|
+
kinds: ["problem", "triage", "vulnerability", "fix", "prior"],
|
|
16675
|
+
args: "flag-body",
|
|
16676
|
+
thread: true,
|
|
16677
|
+
minText: 8,
|
|
16678
|
+
qualifiers: [
|
|
16679
|
+
{ name: "vuln", taught: true, note: "[!vuln: \u2026] \u2192 Vulnerability (asserted)" },
|
|
16680
|
+
{ name: "fixed", taught: false, note: "[!fixed: X] \u2192 read as a self-found fix" },
|
|
16681
|
+
{ name: "resolved", taught: false, note: "[!resolved: X] \u2192 read as a self-found fix" }
|
|
16682
|
+
],
|
|
16683
|
+
lowering: [
|
|
16684
|
+
{ target: P, writes: [], note: "mints a Problem; `@ path[:line]` anchors it (ANCHORED_AT to code)" },
|
|
16685
|
+
{ target: RC, writes: [...TRIAGE_TO_RC, { from: RC, edge: "CAUSED_BY", to: RC }], note: "`<-` segments: first \u2192 triage route, later \u2192 RootCause chain" },
|
|
16686
|
+
{ target: "Vulnerability", writes: [], note: "[!vuln: \u2026]; `<-` causes stay on the finding (vulnerabilityCauses)" }
|
|
16687
|
+
],
|
|
16688
|
+
examples: ["problem", "problemThread", "vulnerability", "vulnerabilityThread"],
|
|
16689
|
+
hint: "flag a problem on one line: [!what's wrong @ path:42]; fold causes with <- ; a fix goes in (fix: \u2026), not [!fixed: \u2026]",
|
|
16690
|
+
taught: "taught"
|
|
16691
|
+
},
|
|
16692
|
+
{
|
|
16693
|
+
verb: "?",
|
|
16694
|
+
form: "flag",
|
|
16695
|
+
opener: "[?",
|
|
16696
|
+
kinds: ["todo", "vulnerability"],
|
|
16697
|
+
args: "flag-body",
|
|
16698
|
+
thread: true,
|
|
16699
|
+
minText: 8,
|
|
16700
|
+
qualifiers: [{ name: "vuln", taught: true, note: "[?vuln: \u2026] \u2192 Vulnerability (suspected)" }],
|
|
16701
|
+
lowering: [{ target: P, writes: [], note: "mints a todo Problem; a fix can close it today", decision: "g" }],
|
|
16702
|
+
examples: ["suspectedVulnerability"],
|
|
16703
|
+
hint: "[?\u2026] marks an open question / todo; [?vuln: consequence; missing check] a suspected vulnerability",
|
|
16704
|
+
taught: "untaught",
|
|
16705
|
+
taughtBy: "only the [?vuln:] form is taught; the bare [?todo] form is not",
|
|
16706
|
+
decisions: ["g"]
|
|
16707
|
+
},
|
|
16708
|
+
{
|
|
16709
|
+
verb: "fix",
|
|
16710
|
+
form: "verb",
|
|
16711
|
+
opener: "(fix:",
|
|
16712
|
+
kinds: ["fix"],
|
|
16713
|
+
args: "handles-text",
|
|
16714
|
+
thread: true,
|
|
16715
|
+
minText: 12,
|
|
16716
|
+
lowering: [
|
|
16717
|
+
{ target: P, writes: [{ from: P, edge: "SOLVED_BY", to: S }], note: "closes the Problem; a note \u226512 chars mints the Solution" },
|
|
16718
|
+
{ target: "Vulnerability", writes: [{ from: "Vulnerability", edge: "SOLVED_BY", to: S }] },
|
|
16719
|
+
{ target: RC, writes: [], note: "dropped: 'fix tag went nowhere' (not a Problem); no tag writes FIXED_BY", decision: "f" }
|
|
16720
|
+
],
|
|
16721
|
+
examples: ["fix", "fixThread"],
|
|
16722
|
+
hint: "one fix per tag: (fix:[its-handle]) or (fix:#thread what you changed) \u2014 put the cause in its own (cause: \u2026)",
|
|
16723
|
+
taught: "taught",
|
|
16724
|
+
decisions: ["f"]
|
|
16725
|
+
},
|
|
16726
|
+
{
|
|
16727
|
+
verb: "cause",
|
|
16728
|
+
form: "verb",
|
|
16729
|
+
opener: "(cause:",
|
|
16730
|
+
kinds: ["triage"],
|
|
16731
|
+
args: "handles-text",
|
|
16732
|
+
thread: true,
|
|
16733
|
+
minText: 8,
|
|
16734
|
+
lowering: [
|
|
16735
|
+
{ target: RC, writes: TRIAGE_TO_RC },
|
|
16736
|
+
{ target: P, writes: [{ from: P, edge: "TRIAGED_BY", to: "Triage" }, { from: "Triage", edge: "INDICATES", to: RC }, { from: P, edge: "CAUSED_BY", to: RC }], note: "(cause:[problem] mechanism) \u2014 the cited Problem is the symptom; the mechanism mints the RootCause" },
|
|
16737
|
+
{ target: S, writes: [{ from: P, edge: "TRIAGED_BY", to: "Triage" }, { from: "Triage", edge: "INDICATES", to: S }], decision: "b", knownViolation: { edge: "INDICATES", finding: "(cause:[solution]) writes a refused INDICATES \u2192 Solution" } }
|
|
16738
|
+
],
|
|
16739
|
+
examples: ["triage", "triageExplain", "triageMint", "causeThread"],
|
|
16740
|
+
hint: "(cause: the mechanism you'd change) or (cause:[root-cause-handle]); bind a distant symptom with (cause:#thread \u2026)",
|
|
16741
|
+
taught: "taught",
|
|
16742
|
+
decisions: ["b"]
|
|
16743
|
+
},
|
|
16744
|
+
{
|
|
16745
|
+
verb: "constraint",
|
|
16746
|
+
form: "verb",
|
|
16747
|
+
opener: "(constraint:",
|
|
16748
|
+
kinds: ["constraint"],
|
|
16749
|
+
args: "text",
|
|
16750
|
+
thread: false,
|
|
16751
|
+
minText: 8,
|
|
16752
|
+
textRequired: true,
|
|
16753
|
+
lowering: [{ target: P, writes: [], note: "mints a constraint-kind Problem" }],
|
|
16754
|
+
examples: ["constraint"],
|
|
16755
|
+
hint: "(constraint: the limitation that forced the decision)",
|
|
16756
|
+
taught: "taught"
|
|
16757
|
+
},
|
|
16758
|
+
{
|
|
16759
|
+
verb: "tried",
|
|
16760
|
+
form: "verb",
|
|
16761
|
+
opener: "(tried:",
|
|
16762
|
+
kinds: ["attempt"],
|
|
16763
|
+
args: "text",
|
|
16764
|
+
thread: true,
|
|
16765
|
+
minText: 8,
|
|
16766
|
+
textRequired: true,
|
|
16767
|
+
lowering: [{ target: P, writes: [], note: "journals an attempt on the bound problem" }],
|
|
16768
|
+
examples: ["tried"],
|
|
16769
|
+
hint: "(tried: the approach) \u2014 bind with (tried:#thread \u2026)",
|
|
16770
|
+
taught: "taught"
|
|
16771
|
+
},
|
|
16772
|
+
{
|
|
16773
|
+
verb: "failed",
|
|
16774
|
+
form: "verb",
|
|
16775
|
+
opener: "(failed:",
|
|
16776
|
+
kinds: ["failure"],
|
|
16777
|
+
args: "handles-text",
|
|
16778
|
+
thread: true,
|
|
16779
|
+
minText: 8,
|
|
16780
|
+
textRequired: true,
|
|
16781
|
+
lowering: [
|
|
16782
|
+
{ target: P, writes: [], note: "journals a failed attempt on the bound problem" },
|
|
16783
|
+
{ target: S, writes: [], note: "(failed:[h]) refutes the cited prior (contradictionPressure); skips the label gate today", decision: "j" }
|
|
16784
|
+
],
|
|
16785
|
+
examples: ["failed", "failedCite"],
|
|
16786
|
+
hint: "(failed: what didn't work); (failed:[solution] what came back) refutes a shown fix",
|
|
16787
|
+
taught: "taught",
|
|
16788
|
+
decisions: ["j"]
|
|
16789
|
+
},
|
|
16790
|
+
{
|
|
16791
|
+
verb: "instance",
|
|
16792
|
+
form: "verb",
|
|
16793
|
+
opener: "(instance:",
|
|
16794
|
+
kinds: ["instance"],
|
|
16795
|
+
args: "handles",
|
|
16796
|
+
thread: false,
|
|
16797
|
+
handleRequired: true,
|
|
16798
|
+
lowering: INSTANCE_LOWERING,
|
|
16799
|
+
examples: ["instance"],
|
|
16800
|
+
hint: "(instance:[h1],[h2],[h3]) \u2014 ~3 DIFFERENT shown priors this problem instantiates; handles only",
|
|
16801
|
+
taught: "taught",
|
|
16802
|
+
decisions: ["a", "e"]
|
|
16803
|
+
},
|
|
16804
|
+
{
|
|
16805
|
+
verb: "pattern",
|
|
16806
|
+
form: "verb",
|
|
16807
|
+
opener: "(pattern:",
|
|
16808
|
+
kinds: ["pattern"],
|
|
16809
|
+
args: "handle-or-text",
|
|
16810
|
+
thread: false,
|
|
16811
|
+
minText: 3,
|
|
16812
|
+
lowering: [{ target: "Pattern", writes: [{ from: P, edge: "INSTANCE_OF", to: "Pattern" }], note: "a handle must be a Pattern; free text resolves-or-mints one" }],
|
|
16813
|
+
examples: ["pattern"],
|
|
16814
|
+
hint: "(pattern: name the general shape) or (pattern:[shown-pattern])",
|
|
16815
|
+
taught: "taught"
|
|
16816
|
+
},
|
|
16817
|
+
{
|
|
16818
|
+
verb: "domain",
|
|
16819
|
+
form: "verb",
|
|
16820
|
+
opener: "(domain:",
|
|
16821
|
+
kinds: ["domain"],
|
|
16822
|
+
args: "text",
|
|
16823
|
+
thread: false,
|
|
16824
|
+
minText: 2,
|
|
16825
|
+
textRequired: true,
|
|
16826
|
+
lowering: [{ target: "Domain", writes: [{ from: P, edge: "PERTAIN_TO", to: "Domain" }], note: '(domain:#t X) mints a Domain named "#t X" today', decision: "k" }],
|
|
16827
|
+
examples: ["domain"],
|
|
16828
|
+
hint: "(domain: The Area) \u2014 Title Case, no thread",
|
|
16829
|
+
taught: "taught",
|
|
16830
|
+
decisions: ["k"]
|
|
16831
|
+
},
|
|
16832
|
+
{
|
|
16833
|
+
verb: "package",
|
|
16834
|
+
form: "verb",
|
|
16835
|
+
opener: "(package:",
|
|
16836
|
+
kinds: ["package"],
|
|
16837
|
+
args: "text",
|
|
16838
|
+
thread: false,
|
|
16839
|
+
minText: 2,
|
|
16840
|
+
textRequired: true,
|
|
16841
|
+
lowering: [{ target: "Package", writes: [{ from: P, edge: "DEPENDS_ON", to: "Package" }], note: "means 'concerns', written as DEPENDS_ON", decision: "c" }],
|
|
16842
|
+
examples: ["package"],
|
|
16843
|
+
hint: "(package: the-package-name) \u2014 the public package it's about",
|
|
16844
|
+
taught: "taught",
|
|
16845
|
+
decisions: ["c"]
|
|
16846
|
+
},
|
|
16847
|
+
{
|
|
16848
|
+
verb: "component",
|
|
16849
|
+
form: "verb",
|
|
16850
|
+
opener: "(component:",
|
|
16851
|
+
kinds: ["component"],
|
|
16852
|
+
args: "text",
|
|
16853
|
+
thread: false,
|
|
16854
|
+
minText: 2,
|
|
16855
|
+
textRequired: true,
|
|
16856
|
+
lowering: [{ target: "Component", writes: [{ from: P, edge: "CONCERNS", to: "Component" }] }],
|
|
16857
|
+
examples: ["component"],
|
|
16858
|
+
hint: "(component: The Component) \u2014 the framework/product unit",
|
|
16859
|
+
taught: "taught"
|
|
16860
|
+
},
|
|
16861
|
+
{
|
|
16862
|
+
verb: "aids",
|
|
16863
|
+
form: "verb",
|
|
16864
|
+
opener: "(aids:",
|
|
16865
|
+
kinds: ["transfer"],
|
|
16866
|
+
args: "handles",
|
|
16867
|
+
thread: true,
|
|
16868
|
+
handleRequired: true,
|
|
16869
|
+
lowering: [{ target: P, writes: [{ from: S, edge: "MAY_RESOLVE", to: P }], note: "source = the Solution in scope; non-Problem targets are skipped" }],
|
|
16870
|
+
examples: ["aids"],
|
|
16871
|
+
hint: "(aids:[problem-handle],\u2026) \u2014 other shown problems your fix might also resolve",
|
|
16872
|
+
taught: "taught"
|
|
16873
|
+
},
|
|
16874
|
+
{
|
|
16875
|
+
verb: "builds-on",
|
|
16876
|
+
form: "verb",
|
|
16877
|
+
opener: "(builds-on:",
|
|
16878
|
+
kinds: ["builds-on"],
|
|
16879
|
+
args: "handles",
|
|
16880
|
+
thread: true,
|
|
16881
|
+
handleRequired: true,
|
|
16882
|
+
lowering: [{ target: S, writes: [{ from: S, edge: "BUILDS_ON", to: S }] }],
|
|
16883
|
+
examples: ["buildsOn"],
|
|
16884
|
+
hint: "(builds-on:[shown-solution]) \u2014 your fix layers on it",
|
|
16885
|
+
taught: "taught",
|
|
16886
|
+
taughtBy: "linkBullet instruction text"
|
|
16887
|
+
},
|
|
16888
|
+
{
|
|
16889
|
+
verb: "supersedes",
|
|
16890
|
+
form: "verb",
|
|
16891
|
+
opener: "(supersedes:",
|
|
16892
|
+
kinds: ["supersedes"],
|
|
16893
|
+
args: "handles",
|
|
16894
|
+
thread: true,
|
|
16895
|
+
handleRequired: true,
|
|
16896
|
+
lowering: [{ target: S, writes: [{ from: S, edge: "SUPERSEDES", to: S }] }],
|
|
16897
|
+
examples: ["supersedes"],
|
|
16898
|
+
hint: "(supersedes:[shown-solution]) \u2014 your fix replaces it",
|
|
16899
|
+
taught: "taught",
|
|
16900
|
+
taughtBy: "linkBullet instruction text"
|
|
16901
|
+
},
|
|
16902
|
+
{
|
|
16903
|
+
verb: "alternative",
|
|
16904
|
+
form: "verb",
|
|
16905
|
+
opener: "(alternative:",
|
|
16906
|
+
kinds: ["alternative"],
|
|
16907
|
+
args: "handles",
|
|
16908
|
+
thread: true,
|
|
16909
|
+
handleRequired: true,
|
|
16910
|
+
lowering: [{ target: S, writes: [{ from: S, edge: "ALTERNATIVE_TO", to: S }] }],
|
|
16911
|
+
examples: ["alternative"],
|
|
16912
|
+
hint: "(alternative:[shown-solution]) \u2014 a different valid approach",
|
|
16913
|
+
taught: "taught",
|
|
16914
|
+
taughtBy: "linkBullet instruction text"
|
|
16915
|
+
},
|
|
16916
|
+
{
|
|
16917
|
+
verb: "confirm",
|
|
16918
|
+
form: "verb",
|
|
16919
|
+
opener: "(confirm:",
|
|
16920
|
+
kinds: ["confirm"],
|
|
16921
|
+
args: "handles",
|
|
16922
|
+
thread: false,
|
|
16923
|
+
handleRequired: true,
|
|
16924
|
+
lowering: [{ target: S, writes: [], note: "corroboration witness (corroborationCount), no edge" }],
|
|
16925
|
+
examples: ["confirm"],
|
|
16926
|
+
hint: "(confirm:[shown-prior]) \u2014 it held",
|
|
16927
|
+
taught: "taught",
|
|
16928
|
+
taughtBy: "verdictBullet + context render",
|
|
16929
|
+
decisions: ["j"]
|
|
16930
|
+
},
|
|
16931
|
+
{
|
|
16932
|
+
verb: "refute",
|
|
16933
|
+
form: "verb",
|
|
16934
|
+
opener: "(refute:",
|
|
16935
|
+
kinds: ["refute"],
|
|
16936
|
+
args: "handles-text",
|
|
16937
|
+
thread: false,
|
|
16938
|
+
minText: 4,
|
|
16939
|
+
handleRequired: true,
|
|
16940
|
+
textRequired: true,
|
|
16941
|
+
lowering: [{ target: S, writes: [], note: "contradiction witness (contradictionPressure), no edge; reason stays local" }],
|
|
16942
|
+
examples: ["refute"],
|
|
16943
|
+
hint: "(refute:[shown-prior] one line why) \u2014 the reason is required",
|
|
16944
|
+
taught: "taught",
|
|
16945
|
+
taughtBy: "verdictBullet + context render",
|
|
16946
|
+
decisions: ["j"]
|
|
16947
|
+
}
|
|
16948
|
+
];
|
|
16949
|
+
TAG_FENCES = [
|
|
16950
|
+
{
|
|
16951
|
+
fence: "errata-problem",
|
|
16952
|
+
fields: ["problem", "cause", "fix", "anchor", "kind"],
|
|
16953
|
+
required: [["problem"]],
|
|
16954
|
+
taught: "untaught",
|
|
16955
|
+
parser: "apps/daemon/src/turn.ts parseDesignFlags",
|
|
16956
|
+
decisions: ["h"],
|
|
16957
|
+
hint: "use an inline [!\u2026] flag instead (errata-problem cause ids never meet inline ids)"
|
|
16958
|
+
},
|
|
16959
|
+
{
|
|
16960
|
+
fence: "errata-resolved",
|
|
16961
|
+
fields: ["id", "problem", "fix", "disposition", "reason"],
|
|
16962
|
+
required: [["id", "problem"]],
|
|
16963
|
+
taught: "taught",
|
|
16964
|
+
taughtBy: "retract form only (TAG_EXAMPLE.retract); the fix form is untaught",
|
|
16965
|
+
parser: "apps/daemon/src/turn.ts parseDesignResolutions",
|
|
16966
|
+
hint: "```errata-resolved with id: and disposition: false_positive | invalid, reason: \u2026"
|
|
16967
|
+
},
|
|
16968
|
+
{
|
|
16969
|
+
fence: "errata-triage",
|
|
16970
|
+
fields: ["presenting", "cause", "test", "fix", "drift", "route"],
|
|
16971
|
+
required: [["presenting", "route"], ["cause", "test"]],
|
|
16972
|
+
taught: "taught",
|
|
16973
|
+
taughtBy: "MCP errata_triage tool copy",
|
|
16974
|
+
parser: "packages/local-graph/src/triage.ts parseTriageFences",
|
|
16975
|
+
hint: "```errata-triage with presenting: and cause: (or route: + test: to answer one)"
|
|
16976
|
+
},
|
|
16977
|
+
{
|
|
16978
|
+
fence: "errata-abstraction",
|
|
16979
|
+
fields: ["candidate", "principle", "covers"],
|
|
16980
|
+
required: [["candidate"], ["principle"], ["covers"]],
|
|
16981
|
+
taught: "taught",
|
|
16982
|
+
taughtBy: "MCP errata_pending_abstractions tool copy",
|
|
16983
|
+
parser: "packages/local-graph/src/abstraction.ts parseAbstractionFences",
|
|
16984
|
+
hint: "```errata-abstraction with candidate:, principle:, covers: (all required)"
|
|
16985
|
+
},
|
|
16986
|
+
{
|
|
16987
|
+
fence: "errata-intent",
|
|
16988
|
+
fields: ["intent"],
|
|
16989
|
+
required: [["intent"]],
|
|
16990
|
+
taught: "taught",
|
|
16991
|
+
parser: "packages/local-graph/src/intent.ts parseIntentFences",
|
|
16992
|
+
hint: "```errata-intent with intent: what you're about to do"
|
|
16993
|
+
},
|
|
16994
|
+
{
|
|
16995
|
+
fence: "errata-share",
|
|
16996
|
+
fields: ["priors"],
|
|
16997
|
+
required: [["priors"]],
|
|
16998
|
+
taught: "taught",
|
|
16999
|
+
taughtBy: "subagent hand-off copy",
|
|
17000
|
+
parser: "packages/local-graph/src/intent.ts parseShareFence",
|
|
17001
|
+
hint: "```errata-share with priors: all | [h1], [h2]"
|
|
17002
|
+
}
|
|
17003
|
+
];
|
|
17004
|
+
TAG_VERB_BY_NAME = new Map(
|
|
17005
|
+
TAG_VERBS.filter((v) => v.form === "verb").map((v) => [v.verb, v])
|
|
17006
|
+
);
|
|
17007
|
+
TAG_PAREN_VERBS = [...TAG_VERB_BY_NAME.keys()].sort((a, b) => b.length - a.length);
|
|
17008
|
+
TAG_GRAMMAR_OPENERS = [
|
|
17009
|
+
...new Set(TAG_VERBS.filter((v) => v.form !== "citation").map((v) => v.opener))
|
|
17010
|
+
];
|
|
17011
|
+
TAG_QUOTED_PAREN_SOURCE = String.raw`\(\s*(?:${TAG_PAREN_VERBS.map((v) => v.replace(/-/g, "\\-")).join("|")})\s*:|\(\[`;
|
|
17012
|
+
TAG_QUOTED_RE = new RegExp(String.raw`\[[!?]|` + TAG_QUOTED_PAREN_SOURCE, "i");
|
|
17013
|
+
TAG_QUOTED_PAREN_RE = new RegExp(TAG_QUOTED_PAREN_SOURCE, "i");
|
|
17014
|
+
TEMPLATE_VERB_GROUP = new RegExp(
|
|
17015
|
+
String.raw`\(\s*(${TAG_PAREN_VERBS.map((v) => v.replace(/-/g, "\\-")).join("|")})\s*:((?:[^()\n]|\([^()\n]*\))*)\)`,
|
|
17016
|
+
"gi"
|
|
17017
|
+
);
|
|
17018
|
+
BRACKET_TOKEN = /\[([^\]\n]{1,80})\]/g;
|
|
17019
|
+
REAL_HANDLE_SHAPE = new RegExp(
|
|
17020
|
+
String.raw`^(?:[a-z0-9]+(?:-[a-z0-9]+)*-(?=[0-9a-f]{0,3}\d)[0-9a-f]{4}|(?:${TAG_CANONICAL_ID_PREFIXES.join("|")})_[0-9a-f]{6,})$`,
|
|
17021
|
+
"i"
|
|
17022
|
+
);
|
|
17023
|
+
TAG_TEMPLATES = deriveTagTemplates(tagGrammarCopy());
|
|
17024
|
+
VERB_WORDS_SOURCE = TAG_PAREN_VERBS.map((v) => v.replace(/-/g, "\\-")).join("|");
|
|
17025
|
+
JOINED_VERB = new RegExp(String.raw`[,;]\s*(?=(?:${VERB_WORDS_SOURCE})\s*:)`, "i");
|
|
17026
|
+
LEADING_VERB = new RegExp(String.raw`^\s*(${VERB_WORDS_SOURCE})\s*:`, "i");
|
|
17027
|
+
ANY_VERB_WORD = new RegExp(String.raw`\b(?:${VERB_WORDS_SOURCE})\s*:`, "gi");
|
|
17028
|
+
QUOTED_TAG_OPENER = new RegExp(String.raw`\(\s*(?:(${VERB_WORDS_SOURCE})\s*:|(?=\[))`, "gi");
|
|
17029
|
+
}
|
|
17030
|
+
});
|
|
17031
|
+
|
|
16479
17032
|
// ../../packages/local-shared/src/index.ts
|
|
16480
17033
|
var init_src2 = __esm({
|
|
16481
17034
|
"../../packages/local-shared/src/index.ts"() {
|
|
@@ -16489,6 +17042,7 @@ var init_src2 = __esm({
|
|
|
16489
17042
|
init_review();
|
|
16490
17043
|
init_anchorable();
|
|
16491
17044
|
init_sqlite_adapter();
|
|
17045
|
+
init_tag_grammar();
|
|
16492
17046
|
}
|
|
16493
17047
|
});
|
|
16494
17048
|
|
|
@@ -16874,11 +17428,12 @@ function purgeWorkspaceCodeGraph(store2, workspaceId) {
|
|
|
16874
17428
|
const edgesBefore = store2.edgeCount();
|
|
16875
17429
|
store2.transaction(() => {
|
|
16876
17430
|
for (const label of INDEXER_OWNED_LABELS) {
|
|
16877
|
-
const nodes = store2.
|
|
17431
|
+
const nodes = store2.selectNodes({
|
|
17432
|
+
labels: [label],
|
|
17433
|
+
includeClosed: true,
|
|
17434
|
+
where: [{ attr: "workspaceId", eq: workspaceId }]
|
|
17435
|
+
});
|
|
16878
17436
|
for (const n of nodes) {
|
|
16879
|
-
if (n.attrs.workspaceId !== workspaceId) {
|
|
16880
|
-
continue;
|
|
16881
|
-
}
|
|
16882
17437
|
store2.deleteNode(n.id);
|
|
16883
17438
|
nodesDeleted++;
|
|
16884
17439
|
}
|
|
@@ -16895,10 +17450,20 @@ function isReExportBarrel(relPath) {
|
|
|
16895
17450
|
function graphExportMap(store2, workspaceId, relPath) {
|
|
16896
17451
|
const out2 = /* @__PURE__ */ new Map();
|
|
16897
17452
|
const fileId = fileNodeId(workspaceId, relPath);
|
|
16898
|
-
if (
|
|
16899
|
-
|
|
16900
|
-
|
|
16901
|
-
|
|
17453
|
+
if (store2.nodeLabels([fileId]).size === 0) return out2;
|
|
17454
|
+
const defines = store2.outEdges(fileId, ["DEFINES"]);
|
|
17455
|
+
const rows = /* @__PURE__ */ new Map();
|
|
17456
|
+
for (let i2 = 0; i2 < defines.length; i2 += 500) {
|
|
17457
|
+
for (const r of store2.selectNodes({
|
|
17458
|
+
labels: [...EXPORTABLE_LABELS],
|
|
17459
|
+
attrs: ["workspaceId", "name"],
|
|
17460
|
+
where: [{ idIn: defines.slice(i2, i2 + 500).map((e) => e.to) }],
|
|
17461
|
+
includeClosed: true
|
|
17462
|
+
})) rows.set(r.id, r);
|
|
17463
|
+
}
|
|
17464
|
+
for (const e of defines) {
|
|
17465
|
+
const n = rows.get(e.to);
|
|
17466
|
+
if (!n) continue;
|
|
16902
17467
|
if (n.attrs["workspaceId"] !== workspaceId) continue;
|
|
16903
17468
|
const name2 = n.attrs["name"] ?? n.description;
|
|
16904
17469
|
if (!name2 || name2.startsWith("<") || name2.includes(":")) continue;
|
|
@@ -17065,14 +17630,37 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
17065
17630
|
signatureAfter: a.signature
|
|
17066
17631
|
});
|
|
17067
17632
|
}
|
|
17633
|
+
const repointable = /* @__PURE__ */ new Map();
|
|
17068
17634
|
store2.transaction(() => {
|
|
17069
17635
|
for (const nid of changedIds) store2.freezeNode(nid, t);
|
|
17070
17636
|
for (const nid of removedIds) {
|
|
17637
|
+
repointable.set(nid, store2.inEdges(nid, ["CALLS", "ACCESSES", "ANCHORED_AT"]));
|
|
17071
17638
|
for (const e of store2.outEdges(nid)) store2.closeEdge(e.id, t);
|
|
17072
17639
|
store2.closeNode(nid, t);
|
|
17073
17640
|
}
|
|
17074
17641
|
});
|
|
17075
|
-
const
|
|
17642
|
+
const seenNodes = /* @__PURE__ */ new Set();
|
|
17643
|
+
const seenEdges = /* @__PURE__ */ new Set();
|
|
17644
|
+
const writeStore = store2.mergeNodeIfChanged && store2.mergeEdgeIfChanged ? new Proxy(store2, {
|
|
17645
|
+
get(target, prop) {
|
|
17646
|
+
if (prop === "mergeNode") {
|
|
17647
|
+
return (n) => {
|
|
17648
|
+
seenNodes.add(n.id);
|
|
17649
|
+
if (n.label === "File" || n.label === "Folder") target.mergeNode(n);
|
|
17650
|
+
else target.mergeNodeIfChanged(n);
|
|
17651
|
+
};
|
|
17652
|
+
}
|
|
17653
|
+
if (prop === "mergeEdge") {
|
|
17654
|
+
return (e) => {
|
|
17655
|
+
seenEdges.add(edgeId(e.from, e.type, e.to));
|
|
17656
|
+
target.mergeEdgeIfChanged(e);
|
|
17657
|
+
};
|
|
17658
|
+
}
|
|
17659
|
+
const v = Reflect.get(target, prop, target);
|
|
17660
|
+
return typeof v === "function" ? v.bind(target) : v;
|
|
17661
|
+
}
|
|
17662
|
+
}) : store2;
|
|
17663
|
+
const r = await runIndexer(writeStore, {
|
|
17076
17664
|
workspaceId,
|
|
17077
17665
|
rootPath,
|
|
17078
17666
|
providers,
|
|
@@ -17090,14 +17678,14 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
17090
17678
|
if (typeof rel !== "string" || !changedRelPaths.has(rel)) continue;
|
|
17091
17679
|
if (versionedAndFile.has(n.label)) {
|
|
17092
17680
|
ownedLive.push(n.id);
|
|
17093
|
-
} else if (n.label === "Comment" && n.lastUpdatedAt < t) {
|
|
17681
|
+
} else if (n.label === "Comment" && n.lastUpdatedAt < t && !seenNodes.has(n.id)) {
|
|
17094
17682
|
store2.closeNode(n.id, t);
|
|
17095
17683
|
}
|
|
17096
17684
|
}
|
|
17097
17685
|
for (const id of ownedLive) {
|
|
17098
17686
|
for (const e of store2.outEdges(id)) {
|
|
17099
17687
|
if (e.type === "SUPERSEDED_BY") continue;
|
|
17100
|
-
if (e.lastSeenAt < t) {
|
|
17688
|
+
if (e.lastSeenAt < t && !seenEdges.has(edgeId(e.from, e.type, e.to))) {
|
|
17101
17689
|
store2.closeEdge(e.id, t);
|
|
17102
17690
|
delta.edgesRemoved++;
|
|
17103
17691
|
} else if (e.createdAt >= t) {
|
|
@@ -17156,8 +17744,8 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
17156
17744
|
version: b.version + 1
|
|
17157
17745
|
});
|
|
17158
17746
|
store2.mergeEdge(continuesEdge(m, t));
|
|
17159
|
-
for (const e of
|
|
17160
|
-
if (e.
|
|
17747
|
+
for (const e of repointable.get(m.fromNid) ?? []) {
|
|
17748
|
+
if (e.from === m.fromNid || !store2.liveNodeIds([e.from]).has(e.from)) continue;
|
|
17161
17749
|
const newId = edgeId(e.from, e.type, m.toNid);
|
|
17162
17750
|
if (!store2.getEdge(newId)) {
|
|
17163
17751
|
store2.mergeEdge({ ...e, id: newId, to: m.toNid, lastSeenAt: t });
|
|
@@ -17214,6 +17802,7 @@ async function runIndexer(store2, opts) {
|
|
|
17214
17802
|
const maxBytes = opts.maxFileBytes ?? 1024 * 1024;
|
|
17215
17803
|
const scope = opts.changedRelPaths;
|
|
17216
17804
|
const inScope = (relPath) => scope === void 0 || scope.has(relPath) || isReExportBarrel(relPath);
|
|
17805
|
+
const emits = (relPath) => scope === void 0 || scope.has(relPath);
|
|
17217
17806
|
let perfLast = started;
|
|
17218
17807
|
const perfFile = process.env["ERRATA_PERF"];
|
|
17219
17808
|
const perf = (label) => {
|
|
@@ -17265,7 +17854,7 @@ async function runIndexer(store2, opts) {
|
|
|
17265
17854
|
upsertFolder(store2, rootId, ".", opts.workspaceId, rootRel);
|
|
17266
17855
|
report.nodesEmitted++;
|
|
17267
17856
|
for (const f of files) {
|
|
17268
|
-
if (!
|
|
17857
|
+
if (!emits(f.relPath)) continue;
|
|
17269
17858
|
const segments = f.relPath.split(/[\\/]/).slice(0, -1);
|
|
17270
17859
|
let parentId = rootId;
|
|
17271
17860
|
let acc = "";
|
|
@@ -17348,7 +17937,13 @@ async function runIndexer(store2, opts) {
|
|
|
17348
17937
|
}
|
|
17349
17938
|
}
|
|
17350
17939
|
perf(`parse (${report.filesParsed} parsed, ${report.filesSkipped} skipped)`);
|
|
17351
|
-
const
|
|
17940
|
+
const pendingExports = /* @__PURE__ */ new Set();
|
|
17941
|
+
const exportsByFile = new class extends Map {
|
|
17942
|
+
get(relPath) {
|
|
17943
|
+
if (pendingExports.delete(relPath)) super.set(relPath, graphExportMap(store2, opts.workspaceId, relPath));
|
|
17944
|
+
return super.get(relPath);
|
|
17945
|
+
}
|
|
17946
|
+
}();
|
|
17352
17947
|
const importsByFile = /* @__PURE__ */ new Map();
|
|
17353
17948
|
for (const [relPath, symbols] of symbolsByFile) {
|
|
17354
17949
|
const localExports = /* @__PURE__ */ new Map();
|
|
@@ -17372,7 +17967,8 @@ async function runIndexer(store2, opts) {
|
|
|
17372
17967
|
if (scope !== void 0) {
|
|
17373
17968
|
for (const f of files) {
|
|
17374
17969
|
if (inScope(f.relPath) || exportsByFile.has(f.relPath)) continue;
|
|
17375
|
-
exportsByFile.set(f.relPath,
|
|
17970
|
+
exportsByFile.set(f.relPath, /* @__PURE__ */ new Map());
|
|
17971
|
+
pendingExports.add(f.relPath);
|
|
17376
17972
|
}
|
|
17377
17973
|
}
|
|
17378
17974
|
const workspacePackages = /* @__PURE__ */ new Map();
|
|
@@ -17513,6 +18109,7 @@ async function runIndexer(store2, opts) {
|
|
|
17513
18109
|
const providerByRel = new Map(files.map((x) => [x.relPath, x.provider]));
|
|
17514
18110
|
let emittedFiles = 0;
|
|
17515
18111
|
for (const [relPath, symbols] of symbolsByFile) {
|
|
18112
|
+
if (!emits(relPath)) continue;
|
|
17516
18113
|
if (emittedFiles > 0 && emittedFiles % 50 === 0) {
|
|
17517
18114
|
await new Promise((r) => setImmediate(r));
|
|
17518
18115
|
}
|
|
@@ -17714,9 +18311,10 @@ async function runIndexer(store2, opts) {
|
|
|
17714
18311
|
if (ei > 0) await new Promise((r) => setImmediate(r));
|
|
17715
18312
|
const slice = pendingEdges.slice(ei, ei + EDGE_BATCH);
|
|
17716
18313
|
store2.transaction(() => {
|
|
18314
|
+
const present = store2.nodeLabels([...new Set(slice.flatMap((e) => [e.from, e.to]))]);
|
|
17717
18315
|
for (const e of slice) {
|
|
17718
18316
|
if (e.from === e.to) continue;
|
|
17719
|
-
if (!
|
|
18317
|
+
if (!present.has(e.from) || !present.has(e.to)) continue;
|
|
17720
18318
|
upsertEdge(
|
|
17721
18319
|
store2,
|
|
17722
18320
|
e.from,
|
|
@@ -17732,6 +18330,7 @@ async function runIndexer(store2, opts) {
|
|
|
17732
18330
|
}
|
|
17733
18331
|
perf("flush cross-file edges");
|
|
17734
18332
|
for (const relPath of symbolsByFile.keys()) {
|
|
18333
|
+
if (!emits(relPath)) continue;
|
|
17735
18334
|
const fid = fileNodeId(opts.workspaceId, relPath);
|
|
17736
18335
|
if (store2.outEdges(fid, ["DEFINES", "CONTAINS"]).length === 0) {
|
|
17737
18336
|
stampIndexOutcome(store2, fid, { symbolCount: 0 });
|
|
@@ -18328,14 +18927,14 @@ function unmarshalNode(tree, address = TRANSFER_BUFFER) {
|
|
|
18328
18927
|
address += SIZE_OF_INT;
|
|
18329
18928
|
const row = C.getValue(address, "i32");
|
|
18330
18929
|
address += SIZE_OF_INT;
|
|
18331
|
-
const
|
|
18930
|
+
const column2 = C.getValue(address, "i32");
|
|
18332
18931
|
address += SIZE_OF_INT;
|
|
18333
18932
|
const other = C.getValue(address, "i32");
|
|
18334
18933
|
const result = new Node(INTERNAL, {
|
|
18335
18934
|
id,
|
|
18336
18935
|
tree,
|
|
18337
18936
|
startIndex: index,
|
|
18338
|
-
startPosition: { row, column },
|
|
18937
|
+
startPosition: { row, column: column2 },
|
|
18339
18938
|
other
|
|
18340
18939
|
});
|
|
18341
18940
|
return result;
|
|
@@ -18425,11 +19024,11 @@ async function Module2(moduleArg = {}) {
|
|
|
18425
19024
|
}, "quit_");
|
|
18426
19025
|
var _scriptName = import.meta.url;
|
|
18427
19026
|
var scriptDirectory = "";
|
|
18428
|
-
function locateFile(
|
|
19027
|
+
function locateFile(path2) {
|
|
18429
19028
|
if (Module["locateFile"]) {
|
|
18430
|
-
return Module["locateFile"](
|
|
19029
|
+
return Module["locateFile"](path2, scriptDirectory);
|
|
18431
19030
|
}
|
|
18432
|
-
return scriptDirectory +
|
|
19031
|
+
return scriptDirectory + path2;
|
|
18433
19032
|
}
|
|
18434
19033
|
__name(locateFile, "locateFile");
|
|
18435
19034
|
var readAsync, readBinary;
|
|
@@ -19581,11 +20180,11 @@ async function Module2(moduleArg = {}) {
|
|
|
19581
20180
|
}
|
|
19582
20181
|
}
|
|
19583
20182
|
__name(_tree_sitter_log_callback, "_tree_sitter_log_callback");
|
|
19584
|
-
function _tree_sitter_parse_callback(inputBufferAddress, index, row,
|
|
20183
|
+
function _tree_sitter_parse_callback(inputBufferAddress, index, row, column2, lengthAddress) {
|
|
19585
20184
|
const INPUT_BUFFER_SIZE = 10 * 1024;
|
|
19586
20185
|
const string4 = Module.currentParseCallback(index, {
|
|
19587
20186
|
row,
|
|
19588
|
-
column
|
|
20187
|
+
column: column2
|
|
19589
20188
|
});
|
|
19590
20189
|
if (typeof string4 === "string") {
|
|
19591
20190
|
setValue(lengthAddress, string4.length, "i32");
|
|
@@ -23249,10 +23848,10 @@ function collectDocstrings(root, records, out2) {
|
|
|
23249
23848
|
}
|
|
23250
23849
|
}
|
|
23251
23850
|
}
|
|
23252
|
-
function docstringIn(
|
|
23253
|
-
if (!
|
|
23254
|
-
if (
|
|
23255
|
-
const expr =
|
|
23851
|
+
function docstringIn(stmt2) {
|
|
23852
|
+
if (!stmt2) return null;
|
|
23853
|
+
if (stmt2.type !== "expression_statement") return null;
|
|
23854
|
+
const expr = stmt2.namedChild(0);
|
|
23256
23855
|
if (!expr) return null;
|
|
23257
23856
|
if (expr.type !== "string") return null;
|
|
23258
23857
|
const text = expr.text;
|
|
@@ -23523,9 +24122,9 @@ function extractNode3(node, containerQname, containerIsClass, source) {
|
|
|
23523
24122
|
];
|
|
23524
24123
|
}
|
|
23525
24124
|
case "preproc_include": {
|
|
23526
|
-
const
|
|
23527
|
-
if (!
|
|
23528
|
-
const src =
|
|
24125
|
+
const path2 = node.childForFieldName("path")?.text;
|
|
24126
|
+
if (!path2) return [];
|
|
24127
|
+
const src = path2.replace(/^[<"]|[>"]$/g, "");
|
|
23529
24128
|
return [
|
|
23530
24129
|
{
|
|
23531
24130
|
kind: "import",
|
|
@@ -23913,9 +24512,9 @@ function importRecords(node) {
|
|
|
23913
24512
|
if (n.type === "import_spec") specs.push(n);
|
|
23914
24513
|
});
|
|
23915
24514
|
for (const spec of specs) {
|
|
23916
|
-
const
|
|
23917
|
-
if (!
|
|
23918
|
-
const src =
|
|
24515
|
+
const path2 = spec.childForFieldName("path")?.text;
|
|
24516
|
+
if (!path2) continue;
|
|
24517
|
+
const src = path2.replace(/^["`]|["`]$/g, "");
|
|
23919
24518
|
out2.push({
|
|
23920
24519
|
kind: "import",
|
|
23921
24520
|
name: src,
|
|
@@ -24998,16 +25597,16 @@ function baseTypeName(node) {
|
|
|
24998
25597
|
}
|
|
24999
25598
|
function usingPath(node) {
|
|
25000
25599
|
const alias = node.childForFieldName("name");
|
|
25001
|
-
let
|
|
25600
|
+
let path2 = null;
|
|
25002
25601
|
for (let i2 = 0; i2 < node.namedChildCount; i2++) {
|
|
25003
25602
|
const c = node.namedChild(i2);
|
|
25004
25603
|
if (!c) continue;
|
|
25005
25604
|
if (alias && c.startIndex === alias.startIndex && c.endIndex === alias.endIndex) continue;
|
|
25006
25605
|
if (c.type === "qualified_name" || c.type === "identifier" || c.type === "generic_name") {
|
|
25007
|
-
|
|
25606
|
+
path2 = c;
|
|
25008
25607
|
}
|
|
25009
25608
|
}
|
|
25010
|
-
return
|
|
25609
|
+
return path2?.text ?? null;
|
|
25011
25610
|
}
|
|
25012
25611
|
function calleeName4(fn) {
|
|
25013
25612
|
if (!fn) return null;
|
|
@@ -25274,6 +25873,110 @@ import { parentPort, workerData } from "node:worker_threads";
|
|
|
25274
25873
|
init_src2();
|
|
25275
25874
|
init_src();
|
|
25276
25875
|
|
|
25876
|
+
// ../../packages/local-graph/src/wm-calibration.ts
|
|
25877
|
+
var WM_CALIBRATION_LABELS = [
|
|
25878
|
+
"Problem",
|
|
25879
|
+
"Solution",
|
|
25880
|
+
"RootCause",
|
|
25881
|
+
"Pattern",
|
|
25882
|
+
"Technique",
|
|
25883
|
+
"AntiPattern"
|
|
25884
|
+
];
|
|
25885
|
+
var WM_CALIBRATION_SCHEMA = 2;
|
|
25886
|
+
var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
25887
|
+
var WM_CALIBRATION_KEYS = [
|
|
25888
|
+
"shownCount",
|
|
25889
|
+
"evictedCount",
|
|
25890
|
+
"reinforceShownCount",
|
|
25891
|
+
"reinforceRecentCount",
|
|
25892
|
+
"reinforceEvictedCount",
|
|
25893
|
+
"reinforceUnshownCount",
|
|
25894
|
+
"reinforceUnshownQueryOnlyCount"
|
|
25895
|
+
];
|
|
25896
|
+
var WM_COUNTED_PREDICATE = WM_CALIBRATION_KEYS.map((k) => `json_type(attrs_json, '$.${k}') IS NOT NULL`).join(" OR ");
|
|
25897
|
+
var wmCounterSql = (k) => `CASE WHEN json_type(attrs_json, '$.${k}') IN ('integer', 'real') THEN json_extract(attrs_json, '$.${k}') END`;
|
|
25898
|
+
function wmCalibrationCells(store2) {
|
|
25899
|
+
const cells = {
|
|
25900
|
+
calibrationSchema: WM_CALIBRATION_SCHEMA,
|
|
25901
|
+
shownNodes: 0,
|
|
25902
|
+
shownImpressions: 0,
|
|
25903
|
+
evictedNodes: 0,
|
|
25904
|
+
evictedImpressions: 0,
|
|
25905
|
+
reinforceShown: 0,
|
|
25906
|
+
reinforceRecent: 0,
|
|
25907
|
+
reinforceEvicted: 0,
|
|
25908
|
+
reinforceUnshown: 0,
|
|
25909
|
+
reinforceUnshownQueryOnly: 0,
|
|
25910
|
+
reinforcedNodes: 0,
|
|
25911
|
+
reinforcedShownNodes: 0
|
|
25912
|
+
};
|
|
25913
|
+
const newest = /* @__PURE__ */ new Map();
|
|
25914
|
+
const rows = store2.wmCalibrationRows?.(WM_CALIBRATION_LABELS) ?? (function* () {
|
|
25915
|
+
for (const label of WM_CALIBRATION_LABELS) {
|
|
25916
|
+
yield* store2.selectNodes({ labels: [label], attrs: WM_CALIBRATION_KEYS, includeClosed: true });
|
|
25917
|
+
}
|
|
25918
|
+
})();
|
|
25919
|
+
for (const n of rows) {
|
|
25920
|
+
const live = n.validTo === null;
|
|
25921
|
+
const prev = newest.get(n.logicalId);
|
|
25922
|
+
if (prev && (prev.live || !live && prev.version >= n.version)) continue;
|
|
25923
|
+
newest.set(n.logicalId, { version: n.version, live, attrs: n.attrs });
|
|
25924
|
+
}
|
|
25925
|
+
for (const { attrs } of newest.values()) {
|
|
25926
|
+
const shown = num(attrs["shownCount"]);
|
|
25927
|
+
const evicted = num(attrs["evictedCount"]);
|
|
25928
|
+
if (shown > 0) {
|
|
25929
|
+
cells.shownNodes++;
|
|
25930
|
+
cells.shownImpressions += shown;
|
|
25931
|
+
}
|
|
25932
|
+
if (evicted > 0) {
|
|
25933
|
+
cells.evictedNodes++;
|
|
25934
|
+
cells.evictedImpressions += evicted;
|
|
25935
|
+
}
|
|
25936
|
+
const rs = num(attrs["reinforceShownCount"]);
|
|
25937
|
+
const rr = num(attrs["reinforceRecentCount"]);
|
|
25938
|
+
const re = num(attrs["reinforceEvictedCount"]);
|
|
25939
|
+
const ru = num(attrs["reinforceUnshownCount"]);
|
|
25940
|
+
cells.reinforceShown += rs;
|
|
25941
|
+
cells.reinforceRecent += rr;
|
|
25942
|
+
cells.reinforceEvicted += re;
|
|
25943
|
+
cells.reinforceUnshown += ru;
|
|
25944
|
+
cells.reinforceUnshownQueryOnly += num(attrs["reinforceUnshownQueryOnlyCount"]);
|
|
25945
|
+
if (rs + rr + re + ru > 0) cells.reinforcedNodes++;
|
|
25946
|
+
if (rs > 0) cells.reinforcedShownNodes++;
|
|
25947
|
+
}
|
|
25948
|
+
return cells;
|
|
25949
|
+
}
|
|
25950
|
+
|
|
25951
|
+
// ../../packages/local-graph/src/exposure-attrs.ts
|
|
25952
|
+
var EXPOSURE_ATTRS = [
|
|
25953
|
+
"shownCount",
|
|
25954
|
+
"evictedCount",
|
|
25955
|
+
"firstShownAt",
|
|
25956
|
+
"firstEvictedAt",
|
|
25957
|
+
"firstShownAtSeq",
|
|
25958
|
+
"lastShownAtSeq",
|
|
25959
|
+
"lastEvictedAtSeq",
|
|
25960
|
+
"shownSinceTouch",
|
|
25961
|
+
"shownSinceTouchAt",
|
|
25962
|
+
"exposureLog",
|
|
25963
|
+
"exposureLogStart",
|
|
25964
|
+
"exposureLogSince",
|
|
25965
|
+
"exposureFirst",
|
|
25966
|
+
"exposureTrimmed"
|
|
25967
|
+
];
|
|
25968
|
+
var EXPOSURE_SCOPE = "exposure";
|
|
25969
|
+
var EXPOSURE_KEYS = new Set(EXPOSURE_ATTRS);
|
|
25970
|
+
var isExposureAttr = (key) => EXPOSURE_KEYS.has(key);
|
|
25971
|
+
function pickExposure(attrs) {
|
|
25972
|
+
const out2 = {};
|
|
25973
|
+
for (const k of EXPOSURE_ATTRS) if (attrs[k] !== void 0) out2[k] = attrs[k];
|
|
25974
|
+
return out2;
|
|
25975
|
+
}
|
|
25976
|
+
function exposureDocSql(key) {
|
|
25977
|
+
return `coalesce((SELECT x.attrs_json FROM node_exposure x WHERE x.id = nodes.id AND json_type(x.attrs_json, '$.${key}') IS NOT NULL), attrs_json)`;
|
|
25978
|
+
}
|
|
25979
|
+
|
|
25277
25980
|
// ../../packages/local-graph/src/history-cache-floor.ts
|
|
25278
25981
|
var HISTORY_CACHE_FLOOR_KEY = "history-cache-withdrawal-floor-3";
|
|
25279
25982
|
function installHistoryCacheFloor(db) {
|
|
@@ -25314,7 +26017,7 @@ function installHistoryCacheFloor(db) {
|
|
|
25314
26017
|
OR EXISTS(SELECT 1 FROM history_cache_withdrawals w WHERE w.id = nodes.id))
|
|
25315
26018
|
AND (embedding_blob IS NOT NULL OR embedding_json <> '[]' OR is_landmark <> 0
|
|
25316
26019
|
OR description <> ${description} OR attrs_json <> ${attrs});`;
|
|
25317
|
-
const internal = `key IN ('orgId','teamId','groupId','projectId','aliasTexts','aliasCanonicalIds')
|
|
26020
|
+
const internal = `key IN ('orgId','teamId','groupId','projectId','aliasTexts','aliasCanonicalIds','residueAudienceVerified')
|
|
25318
26021
|
OR key GLOB 'mergedFrom*' OR key GLOB '*WitnessKeys' OR key GLOB '*Sessions'
|
|
25319
26022
|
OR key GLOB 'authoringSession*' OR key GLOB 'contributor*' OR key GLOB 'witnessKeys*'`;
|
|
25320
26023
|
const stripMetadata = (scope) => `UPDATE nodes SET attrs_json=(
|
|
@@ -25450,7 +26153,9 @@ function installEditPlaceholderRetirement(db) {
|
|
|
25450
26153
|
|
|
25451
26154
|
// ../../packages/local-graph/src/cloud-attr-compaction.ts
|
|
25452
26155
|
init_src2();
|
|
25453
|
-
var CLOUD_ATTR_COMPACTION_KEY = "cloud-attr-compaction-
|
|
26156
|
+
var CLOUD_ATTR_COMPACTION_KEY = "cloud-attr-compaction-2";
|
|
26157
|
+
var V1_KEY = "cloud-attr-compaction-1";
|
|
26158
|
+
var STRUCT_LABELS = "('Problem', 'Solution', 'RootCause')";
|
|
25454
26159
|
var DUPES = CLOUD_ATTR_DUPLICATE_KEYS.map((k) => `'${k}'`).join(",");
|
|
25455
26160
|
function compacted(row) {
|
|
25456
26161
|
const packed = `json_extract(${row}.attrs_json, '$.attrsJson')`;
|
|
@@ -25469,14 +26174,17 @@ function dirty(row) {
|
|
|
25469
26174
|
return `json_extract(${row}.attrs_json, '$.source') = 'cloud' AND (${has})`;
|
|
25470
26175
|
}
|
|
25471
26176
|
function installCloudAttrCompaction(db) {
|
|
25472
|
-
|
|
26177
|
+
const marked = (key) => Boolean(db.prepare("SELECT value FROM store_meta WHERE key = ?").get(key));
|
|
26178
|
+
if (marked(CLOUD_ATTR_COMPACTION_KEY)) return;
|
|
26179
|
+
const scope = marked(V1_KEY) ? ` AND label IN ${STRUCT_LABELS}` : "";
|
|
25473
26180
|
db.transaction(() => {
|
|
25474
26181
|
for (const operation of ["INSERT", "UPDATE"]) {
|
|
25475
|
-
db.exec(`
|
|
26182
|
+
db.exec(`DROP TRIGGER IF EXISTS cloud_attr_compact_${operation.toLowerCase()}`);
|
|
26183
|
+
db.exec(`CREATE TRIGGER cloud_attr_compact_${operation.toLowerCase()}
|
|
25476
26184
|
AFTER ${operation} ON nodes WHEN ${dirty("NEW")}
|
|
25477
26185
|
BEGIN UPDATE nodes SET attrs_json = ${compacted("nodes")} WHERE id = NEW.id; END;`);
|
|
25478
26186
|
}
|
|
25479
|
-
db.exec(`UPDATE nodes SET attrs_json = ${compacted("nodes")} WHERE ${dirty("nodes")}`);
|
|
26187
|
+
db.exec(`UPDATE nodes SET attrs_json = ${compacted("nodes")} WHERE ${dirty("nodes")}${scope}`);
|
|
25480
26188
|
db.prepare("INSERT INTO store_meta(key,value) VALUES (?,?)").run(CLOUD_ATTR_COMPACTION_KEY, "installed");
|
|
25481
26189
|
});
|
|
25482
26190
|
}
|
|
@@ -25515,6 +26223,313 @@ function installHistoryAttrSlim(db) {
|
|
|
25515
26223
|
db.prepare("INSERT INTO store_meta(key,value) VALUES (?,?)").run(HISTORY_ATTR_SLIM_KEY, "done");
|
|
25516
26224
|
}
|
|
25517
26225
|
|
|
26226
|
+
// ../../packages/local-graph/src/text-index.ts
|
|
26227
|
+
var TEXT_INDEX_KEY = "text-index-trigram-2";
|
|
26228
|
+
var TEXT_INDEX_MIN_TERM = 3;
|
|
26229
|
+
function installTextIndex(db) {
|
|
26230
|
+
if (db.prepare("SELECT value FROM store_meta WHERE key = ?").get(TEXT_INDEX_KEY)) return;
|
|
26231
|
+
try {
|
|
26232
|
+
db.exec(`CREATE VIRTUAL TABLE temp.fts5_probe USING fts5(x, tokenize='trigram'); DROP TABLE temp.fts5_probe`);
|
|
26233
|
+
} catch {
|
|
26234
|
+
db.prepare("INSERT INTO store_meta(key,value) VALUES (?,?)").run(TEXT_INDEX_KEY, "unavailable");
|
|
26235
|
+
return;
|
|
26236
|
+
}
|
|
26237
|
+
db.transaction(() => {
|
|
26238
|
+
db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS nodes_text
|
|
26239
|
+
USING fts5(description, content='nodes', content_rowid='rowid', tokenize='trigram')`);
|
|
26240
|
+
db.exec(`DROP TRIGGER IF EXISTS nodes_text_update_out; DROP TRIGGER IF EXISTS nodes_text_update_in;
|
|
26241
|
+
DROP TRIGGER IF EXISTS nodes_text_update`);
|
|
26242
|
+
db.exec(`CREATE TRIGGER IF NOT EXISTS nodes_text_insert AFTER INSERT ON nodes WHEN NEW.valid_to IS NULL
|
|
26243
|
+
BEGIN INSERT INTO nodes_text(rowid, description) VALUES (NEW.rowid, NEW.description); END;`);
|
|
26244
|
+
db.exec(`CREATE TRIGGER nodes_text_update AFTER UPDATE OF description, valid_to ON nodes
|
|
26245
|
+
WHEN OLD.description IS NOT NEW.description OR OLD.valid_to IS NOT NEW.valid_to
|
|
26246
|
+
BEGIN
|
|
26247
|
+
INSERT INTO nodes_text(nodes_text, rowid, description)
|
|
26248
|
+
SELECT 'delete', OLD.rowid, OLD.description WHERE OLD.valid_to IS NULL;
|
|
26249
|
+
INSERT INTO nodes_text(rowid, description)
|
|
26250
|
+
SELECT NEW.rowid, NEW.description WHERE NEW.valid_to IS NULL;
|
|
26251
|
+
END;`);
|
|
26252
|
+
db.exec(`CREATE TRIGGER IF NOT EXISTS nodes_text_delete AFTER DELETE ON nodes WHEN OLD.valid_to IS NULL
|
|
26253
|
+
BEGIN INSERT INTO nodes_text(nodes_text, rowid, description) VALUES ('delete', OLD.rowid, OLD.description); END;`);
|
|
26254
|
+
db.exec(`INSERT INTO nodes_text(nodes_text) VALUES ('delete-all')`);
|
|
26255
|
+
db.exec(`INSERT INTO nodes_text(rowid, description) SELECT rowid, description FROM nodes WHERE valid_to IS NULL`);
|
|
26256
|
+
db.prepare("INSERT OR REPLACE INTO store_meta(key,value) VALUES (?,?)").run("text-index-trigram-1", "installed");
|
|
26257
|
+
db.prepare("INSERT INTO store_meta(key,value) VALUES (?,?)").run(TEXT_INDEX_KEY, "installed");
|
|
26258
|
+
});
|
|
26259
|
+
}
|
|
26260
|
+
function textIndexReady(db) {
|
|
26261
|
+
return db.prepare("SELECT value FROM store_meta WHERE key = ?").get(TEXT_INDEX_KEY)?.["value"] === "installed";
|
|
26262
|
+
}
|
|
26263
|
+
|
|
26264
|
+
// ../../packages/local-graph/src/change-feed.ts
|
|
26265
|
+
var CHANGE_FEED_KEY = "change-feed";
|
|
26266
|
+
var ANY_CHANGE = "*";
|
|
26267
|
+
var nodeScope = (label) => `node:${label}`;
|
|
26268
|
+
var CHANGE_LOG_KEEP = 25e4;
|
|
26269
|
+
var PRUNED_TO_KEY = "change-log-pruned-to";
|
|
26270
|
+
function installChangeFeed(db) {
|
|
26271
|
+
if (changeFeedInstalled(db)) return;
|
|
26272
|
+
db.transaction(() => {
|
|
26273
|
+
db.exec("CREATE TABLE IF NOT EXISTS change_seq (scope TEXT PRIMARY KEY, seq INTEGER NOT NULL) WITHOUT ROWID");
|
|
26274
|
+
db.exec(`CREATE TABLE IF NOT EXISTS change_log (change_seq INTEGER PRIMARY KEY, kind TEXT NOT NULL, id TEXT NOT NULL,
|
|
26275
|
+
label TEXT, from_id TEXT, to_id TEXT)`);
|
|
26276
|
+
for (const name2 of ["nodes", "edges"].flatMap((t) => ["insert", "update", "delete"].map((op) => `change_feed_${t}_${op}`))) {
|
|
26277
|
+
db.exec(`DROP TRIGGER IF EXISTS ${name2}`);
|
|
26278
|
+
}
|
|
26279
|
+
const changed = (table) => columns(db, table).map((c) => `OLD."${c}" IS NOT NEW."${c}"`).join(" OR ");
|
|
26280
|
+
const strip = (row) => `json_remove(${row}.attrs_json, ${EXPOSURE_ATTRS.map((a) => `'$.${a}'`).join(", ")})`;
|
|
26281
|
+
const exposureOnly = `(${columns(db, "nodes").filter((c) => c !== "attrs_json").map((c) => `OLD."${c}" IS NEW."${c}"`).join(" AND ")}
|
|
26282
|
+
AND CASE WHEN json_valid(OLD.attrs_json) AND json_valid(NEW.attrs_json) THEN ${strip("OLD")} IS ${strip("NEW")} ELSE 0 END)`;
|
|
26283
|
+
const bump = (...scopes) => `INSERT INTO change_seq(scope, seq) VALUES ${scopes.map((s) => `(${s}, 1)`).join(", ")}
|
|
26284
|
+
ON CONFLICT(scope) DO UPDATE SET seq = seq + 1;`;
|
|
26285
|
+
const seq = `(SELECT seq FROM change_seq WHERE scope = '*')`;
|
|
26286
|
+
const logNode = (row) => `INSERT INTO change_log(change_seq, kind, id, label) VALUES (${seq}, 'node', ${row}.id, ${row}.label);`;
|
|
26287
|
+
const logEdge = (row) => `INSERT INTO change_log(change_seq, kind, id, label, from_id, to_id) VALUES (${seq}, 'edge', ${row}.id, ${row}.type, ${row}.from_id, ${row}.to_id);`;
|
|
26288
|
+
const node = (row) => `'node:' || ${row}.label`;
|
|
26289
|
+
const edge = (row) => `'edge:' || ${row}.type`;
|
|
26290
|
+
db.exec(`CREATE TRIGGER change_feed_nodes_insert AFTER INSERT ON nodes BEGIN ${bump(`'*'`, node("NEW"))} ${logNode("NEW")} END;`);
|
|
26291
|
+
db.exec(`CREATE TRIGGER change_feed_nodes_update AFTER UPDATE ON nodes WHEN ${changed("nodes")} BEGIN
|
|
26292
|
+
${bump(`'*'`)}
|
|
26293
|
+
INSERT INTO change_seq(scope, seq) SELECT CASE WHEN ${exposureOnly} THEN '${EXPOSURE_SCOPE}' ELSE ${node("NEW")} END, 1 WHERE 1
|
|
26294
|
+
ON CONFLICT(scope) DO UPDATE SET seq = seq + 1;
|
|
26295
|
+
INSERT INTO change_seq(scope, seq) SELECT ${node("OLD")}, 1 WHERE OLD.label IS NOT NEW.label
|
|
26296
|
+
ON CONFLICT(scope) DO UPDATE SET seq = seq + 1;
|
|
26297
|
+
${logNode("NEW")}
|
|
26298
|
+
END;`);
|
|
26299
|
+
db.exec(`CREATE TRIGGER change_feed_nodes_delete AFTER DELETE ON nodes BEGIN ${bump(`'*'`, node("OLD"))} ${logNode("OLD")} END;`);
|
|
26300
|
+
db.exec(`CREATE TRIGGER change_feed_edges_insert AFTER INSERT ON edges BEGIN ${bump(`'*'`, edge("NEW"))} ${logEdge("NEW")} END;`);
|
|
26301
|
+
db.exec(`CREATE TRIGGER change_feed_edges_update AFTER UPDATE ON edges WHEN ${changed("edges")} BEGIN
|
|
26302
|
+
${bump(`'*'`, edge("NEW"))}
|
|
26303
|
+
INSERT INTO change_seq(scope, seq) SELECT ${edge("OLD")}, 1 WHERE OLD.type IS NOT NEW.type
|
|
26304
|
+
ON CONFLICT(scope) DO UPDATE SET seq = seq + 1;
|
|
26305
|
+
${logEdge("NEW")}
|
|
26306
|
+
END;`);
|
|
26307
|
+
db.exec(`CREATE TRIGGER change_feed_edges_delete AFTER DELETE ON edges BEGIN ${bump(`'*'`, edge("OLD"))} ${logEdge("OLD")} END;`);
|
|
26308
|
+
db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, ?)").run(PRUNED_TO_KEY, String(readChangeSeq(db)));
|
|
26309
|
+
db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, ?)").run(CHANGE_FEED_KEY, columnSignature(db));
|
|
26310
|
+
});
|
|
26311
|
+
}
|
|
26312
|
+
function readChangeSeq(db, scopes = [ANY_CHANGE]) {
|
|
26313
|
+
if (!scopes.length) return 0;
|
|
26314
|
+
const row = db.prepare(`SELECT coalesce(sum(seq), 0) AS n FROM change_seq WHERE scope IN (${scopes.map(() => "?").join(",")})`).get(...scopes);
|
|
26315
|
+
return Number(row?.n ?? 0);
|
|
26316
|
+
}
|
|
26317
|
+
function changeLogCovers(db, sinceSeq) {
|
|
26318
|
+
const prunedTo = Number(db.prepare("SELECT value FROM store_meta WHERE key = ?").get(PRUNED_TO_KEY)?.["value"] ?? NaN);
|
|
26319
|
+
return Number.isFinite(prunedTo) && sinceSeq >= prunedTo;
|
|
26320
|
+
}
|
|
26321
|
+
function readChangedNodeIds(db, sinceSeq, labels) {
|
|
26322
|
+
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
26323
|
+
if (!labels.length) return [];
|
|
26324
|
+
const rows = db.prepare(`SELECT DISTINCT id FROM change_log WHERE change_seq > ? AND kind = 'node' AND label IN (${labels.map(() => "?").join(",")})`).all(sinceSeq, ...labels);
|
|
26325
|
+
return rows.map((r) => r.id);
|
|
26326
|
+
}
|
|
26327
|
+
function readChangedAmong(db, sinceSeq, ids) {
|
|
26328
|
+
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
26329
|
+
const out2 = [];
|
|
26330
|
+
for (let i2 = 0; i2 < ids.length; i2 += 500) {
|
|
26331
|
+
const chunk = ids.slice(i2, i2 + 500);
|
|
26332
|
+
const rows = db.prepare(`SELECT DISTINCT id FROM change_log WHERE change_seq > ? AND kind = 'node' AND id IN (${chunk.map(() => "?").join(",")})`).all(sinceSeq, ...chunk);
|
|
26333
|
+
for (const r of rows) out2.push(r.id);
|
|
26334
|
+
}
|
|
26335
|
+
return out2;
|
|
26336
|
+
}
|
|
26337
|
+
function readLineageChangedFiles(db, sinceSeq) {
|
|
26338
|
+
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
26339
|
+
const rows = db.prepare(LINEAGE_CHANGED_FILES_SQL).all({ s: sinceSeq });
|
|
26340
|
+
return rows.map((r) => r.id);
|
|
26341
|
+
}
|
|
26342
|
+
var LINEAGE_CHANGED_FILES_SQL = `
|
|
26343
|
+
SELECT id FROM change_log WHERE change_seq > :s AND kind = 'node' AND label = 'File'
|
|
26344
|
+
UNION SELECT from_id FROM change_log WHERE change_seq > :s AND kind = 'edge' AND label IN ('DEFINES','CONTAINS')
|
|
26345
|
+
UNION SELECT e.from_id FROM (SELECT DISTINCT id FROM change_log WHERE change_seq > :s AND kind = 'node') c
|
|
26346
|
+
CROSS JOIN edges e ON e.to_id = c.id AND e.type IN ('DEFINES','CONTAINS')
|
|
26347
|
+
`;
|
|
26348
|
+
function pruneChangeLog(db) {
|
|
26349
|
+
const max = Number(db.prepare("SELECT coalesce(max(change_seq), 0) AS n FROM change_log").get()?.["n"] ?? 0);
|
|
26350
|
+
const cut = max - CHANGE_LOG_KEEP;
|
|
26351
|
+
if (cut <= 0) return 0;
|
|
26352
|
+
let removed = 0;
|
|
26353
|
+
db.transaction(() => {
|
|
26354
|
+
removed = Number(db.prepare("DELETE FROM change_log WHERE change_seq <= ?").run(cut).changes);
|
|
26355
|
+
const prev = Number(db.prepare("SELECT value FROM store_meta WHERE key = ?").get(PRUNED_TO_KEY)?.["value"] ?? 0);
|
|
26356
|
+
db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, ?)").run(PRUNED_TO_KEY, String(Math.max(prev, cut)));
|
|
26357
|
+
});
|
|
26358
|
+
return removed;
|
|
26359
|
+
}
|
|
26360
|
+
function changeFeedInstalled(db) {
|
|
26361
|
+
return db.prepare("SELECT value FROM store_meta WHERE key = ?").get(CHANGE_FEED_KEY)?.["value"] === columnSignature(db);
|
|
26362
|
+
}
|
|
26363
|
+
function columns(db, table) {
|
|
26364
|
+
return db.prepare(`SELECT name FROM pragma_table_info('${table}') ORDER BY cid`).all().map((r) => r.name);
|
|
26365
|
+
}
|
|
26366
|
+
function columnSignature(db) {
|
|
26367
|
+
return `5|${columns(db, "nodes").join(",")}|${columns(db, "edges").join(",")}|${EXPOSURE_ATTRS.join(",")}`;
|
|
26368
|
+
}
|
|
26369
|
+
|
|
26370
|
+
// ../../packages/local-graph/src/lineage-integrity.ts
|
|
26371
|
+
var statements = /* @__PURE__ */ new WeakMap();
|
|
26372
|
+
function stmt(db, sql) {
|
|
26373
|
+
let byDb = statements.get(db);
|
|
26374
|
+
if (!byDb) statements.set(db, byDb = /* @__PURE__ */ new Map());
|
|
26375
|
+
let s = byDb.get(sql);
|
|
26376
|
+
if (!s) byDb.set(sql, s = db.prepare(sql));
|
|
26377
|
+
return s;
|
|
26378
|
+
}
|
|
26379
|
+
var HISTORY_EDGE_TYPES = [
|
|
26380
|
+
"SUPERSEDED_BY",
|
|
26381
|
+
"CONTINUES",
|
|
26382
|
+
"FORKED_FROM",
|
|
26383
|
+
"REGRESSION_OF",
|
|
26384
|
+
"SPLIT_INTO",
|
|
26385
|
+
"REVEALED_BY"
|
|
26386
|
+
];
|
|
26387
|
+
var LINEAGE_INTEGRITY_KEY = "lineage-integrity-v1";
|
|
26388
|
+
var HISTORY_SQL = HISTORY_EDGE_TYPES.map((t) => `'${t}'`).join(",");
|
|
26389
|
+
var NOT_HISTORY = `type NOT IN (${HISTORY_SQL})`;
|
|
26390
|
+
var NOT_HISTORICAL_ROW = `json_type(attrs_json, '$.historical') IS NOT 'true'`;
|
|
26391
|
+
var RETAINS_EDGES = (n) => `(${n}.label = 'Problem'
|
|
26392
|
+
AND json_type(${n}.attrs_json, '$.resolvedAt') IS NOT NULL
|
|
26393
|
+
AND COALESCE(json_extract(${n}.attrs_json, '$.resolvedAs'), '') NOT IN ('duplicate', 'false_positive'))`;
|
|
26394
|
+
var PRESENT = (n) => `(${n}.valid_to IS NULL OR ${RETAINS_EDGES(n)})`;
|
|
26395
|
+
var STRANDED_EDGE_PREDICATE = `e.valid_to IS NULL AND e.${NOT_HISTORY}
|
|
26396
|
+
AND (NOT EXISTS (SELECT 1 FROM nodes f WHERE f.id = e.from_id AND ${PRESENT("f")})
|
|
26397
|
+
OR NOT EXISTS (SELECT 1 FROM nodes t WHERE t.id = e.to_id AND ${PRESENT("t")}))`;
|
|
26398
|
+
function countStrandedEdges(db) {
|
|
26399
|
+
const rows = db.prepare(`SELECT e.type AS type, count(*) AS n FROM edges e WHERE ${STRANDED_EDGE_PREDICATE} GROUP BY e.type ORDER BY n DESC`).all();
|
|
26400
|
+
const byType = {};
|
|
26401
|
+
let total = 0;
|
|
26402
|
+
for (const r of rows) {
|
|
26403
|
+
byType[r.type] = Number(r.n);
|
|
26404
|
+
total += Number(r.n);
|
|
26405
|
+
}
|
|
26406
|
+
return { total, byType };
|
|
26407
|
+
}
|
|
26408
|
+
function countLiveHeadConflicts(db) {
|
|
26409
|
+
const row = db.prepare(
|
|
26410
|
+
`SELECT count(*) AS n FROM (
|
|
26411
|
+
SELECT COALESCE(logical_id, id) AS lid FROM nodes
|
|
26412
|
+
WHERE valid_to IS NULL AND ${NOT_HISTORICAL_ROW}
|
|
26413
|
+
GROUP BY lid HAVING count(*) > 1)`
|
|
26414
|
+
).get();
|
|
26415
|
+
return Number(row?.n ?? 0);
|
|
26416
|
+
}
|
|
26417
|
+
function closeIncidentEdges(db, id, t) {
|
|
26418
|
+
let closed = 0;
|
|
26419
|
+
for (const side of ["from_id", "to_id"]) {
|
|
26420
|
+
const r = stmt(db, `UPDATE edges SET valid_to = MAX(valid_from, ?) WHERE ${side} = ? AND valid_to IS NULL AND ${NOT_HISTORY}`).run(t, id);
|
|
26421
|
+
closed += Number(r.changes);
|
|
26422
|
+
}
|
|
26423
|
+
return closed;
|
|
26424
|
+
}
|
|
26425
|
+
function reopenCascadeClosedEdges(db, id, closedAt) {
|
|
26426
|
+
let reopened = 0;
|
|
26427
|
+
for (const [side, other] of [["from_id", "to_id"], ["to_id", "from_id"]]) {
|
|
26428
|
+
const r = stmt(
|
|
26429
|
+
db,
|
|
26430
|
+
`UPDATE edges SET valid_to = NULL
|
|
26431
|
+
WHERE ${side} = ? AND valid_to = ? AND ${NOT_HISTORY}
|
|
26432
|
+
AND EXISTS (SELECT 1 FROM nodes o WHERE o.id = edges.${other} AND o.valid_to IS NULL)`
|
|
26433
|
+
).run(id, closedAt);
|
|
26434
|
+
reopened += Number(r.changes);
|
|
26435
|
+
}
|
|
26436
|
+
return reopened;
|
|
26437
|
+
}
|
|
26438
|
+
function collapseLiveHeads(db, logicalIds, t) {
|
|
26439
|
+
const headsOf = db.prepare(
|
|
26440
|
+
`SELECT id, version, valid_from FROM (
|
|
26441
|
+
SELECT id, version, valid_from, attrs_json FROM nodes WHERE logical_id = ?1 AND valid_to IS NULL
|
|
26442
|
+
UNION
|
|
26443
|
+
SELECT id, version, valid_from, attrs_json FROM nodes WHERE id = ?1 AND logical_id IS NULL AND valid_to IS NULL
|
|
26444
|
+
) WHERE ${NOT_HISTORICAL_ROW}`
|
|
26445
|
+
);
|
|
26446
|
+
const continuesInto = db.prepare(
|
|
26447
|
+
`SELECT to_id FROM edges WHERE from_id = ? AND type = 'CONTINUES' AND valid_to IS NULL`
|
|
26448
|
+
);
|
|
26449
|
+
const rekeyHead = db.prepare(`UPDATE nodes SET logical_id = ?1 WHERE id = ?1`);
|
|
26450
|
+
const rekeyCopies = db.prepare(
|
|
26451
|
+
`UPDATE nodes SET logical_id = ?1 WHERE logical_id = ?2 AND substr(id, 1, length(?1) + 2) = ?1 || '@v'`
|
|
26452
|
+
);
|
|
26453
|
+
const lineageMembers = db.prepare(
|
|
26454
|
+
`SELECT id FROM nodes WHERE logical_id = ?1 UNION SELECT id FROM nodes WHERE id = ?1`
|
|
26455
|
+
);
|
|
26456
|
+
const closeContinues = db.prepare(
|
|
26457
|
+
`UPDATE edges SET valid_to = MAX(valid_from, ?) WHERE from_id = ? AND to_id = ? AND type = 'CONTINUES' AND valid_to IS NULL`
|
|
26458
|
+
);
|
|
26459
|
+
let split = 0;
|
|
26460
|
+
const queue = [...new Set(logicalIds)];
|
|
26461
|
+
for (let i2 = 0; i2 < queue.length; i2++) {
|
|
26462
|
+
const lid = queue[i2];
|
|
26463
|
+
const heads = headsOf.all(lid);
|
|
26464
|
+
if (heads.length < 2) continue;
|
|
26465
|
+
const members = new Set(lineageMembers.all(lid).map((r) => r.id));
|
|
26466
|
+
const splitOff = (h) => {
|
|
26467
|
+
for (const { to_id } of continuesInto.all(h.id)) {
|
|
26468
|
+
if (members.has(to_id)) closeContinues.run(t, h.id, to_id);
|
|
26469
|
+
}
|
|
26470
|
+
rekeyHead.run(h.id);
|
|
26471
|
+
rekeyCopies.run(h.id, lid);
|
|
26472
|
+
queue.push(h.id);
|
|
26473
|
+
split++;
|
|
26474
|
+
};
|
|
26475
|
+
const kept = new Set(heads.map((h) => h.id));
|
|
26476
|
+
for (const h of heads) {
|
|
26477
|
+
if (h.id === lid) continue;
|
|
26478
|
+
const refuted = continuesInto.all(h.id).some(
|
|
26479
|
+
(e) => e.to_id !== h.id && kept.has(e.to_id)
|
|
26480
|
+
);
|
|
26481
|
+
if (refuted && kept.size > 1) {
|
|
26482
|
+
splitOff(h);
|
|
26483
|
+
kept.delete(h.id);
|
|
26484
|
+
}
|
|
26485
|
+
}
|
|
26486
|
+
let remaining = heads.filter((h) => kept.has(h.id));
|
|
26487
|
+
if (remaining.length < 2) continue;
|
|
26488
|
+
remaining = [...remaining].sort(
|
|
26489
|
+
(a, b) => Number(b.id === lid) - Number(a.id === lid) || Number(b.version) - Number(a.version) || Number(b.valid_from) - Number(a.valid_from) || (a.id < b.id ? -1 : 1)
|
|
26490
|
+
);
|
|
26491
|
+
for (const h of remaining.slice(1)) splitOff(h);
|
|
26492
|
+
}
|
|
26493
|
+
return split;
|
|
26494
|
+
}
|
|
26495
|
+
function repairLineageIntegrity(db, now) {
|
|
26496
|
+
const historicalRowsClosed = Number(
|
|
26497
|
+
db.prepare(
|
|
26498
|
+
`UPDATE nodes SET valid_to = MAX(valid_from, COALESCE(
|
|
26499
|
+
(SELECT s.valid_from FROM edges s WHERE s.id = 'edge_superseded_' || nodes.id), ?))
|
|
26500
|
+
WHERE valid_to IS NULL AND json_type(attrs_json, '$.historical') = 'true'`
|
|
26501
|
+
).run(now).changes
|
|
26502
|
+
);
|
|
26503
|
+
const conflicted = db.prepare(
|
|
26504
|
+
`SELECT COALESCE(logical_id, id) AS lid FROM nodes
|
|
26505
|
+
WHERE valid_to IS NULL AND ${NOT_HISTORICAL_ROW}
|
|
26506
|
+
GROUP BY lid HAVING count(*) > 1`
|
|
26507
|
+
).all().map((r) => r.lid);
|
|
26508
|
+
const headsSplit = collapseLiveHeads(db, conflicted, now);
|
|
26509
|
+
const strandedEdgesClosed = Number(
|
|
26510
|
+
db.prepare(
|
|
26511
|
+
`UPDATE edges SET valid_to = MAX(valid_from, COALESCE(
|
|
26512
|
+
(SELECT min(v) FROM (
|
|
26513
|
+
SELECT f.valid_to AS v FROM nodes f WHERE f.id = edges.from_id AND NOT ${PRESENT("f")}
|
|
26514
|
+
UNION ALL
|
|
26515
|
+
SELECT t.valid_to FROM nodes t WHERE t.id = edges.to_id AND NOT ${PRESENT("t")})), ?))
|
|
26516
|
+
WHERE id IN (SELECT e.id FROM edges e WHERE ${STRANDED_EDGE_PREDICATE})`
|
|
26517
|
+
).run(now).changes
|
|
26518
|
+
);
|
|
26519
|
+
return { historicalRowsClosed, headsSplit, strandedEdgesClosed };
|
|
26520
|
+
}
|
|
26521
|
+
function installLineageIntegrityRepair(db, now = Date.now()) {
|
|
26522
|
+
if (db.prepare("SELECT value FROM store_meta WHERE key = ?").get(LINEAGE_INTEGRITY_KEY)) return null;
|
|
26523
|
+
return db.transaction(() => {
|
|
26524
|
+
const report = repairLineageIntegrity(db, now);
|
|
26525
|
+
db.prepare("INSERT OR REPLACE INTO store_meta (key, value) VALUES (?, ?)").run(
|
|
26526
|
+
LINEAGE_INTEGRITY_KEY,
|
|
26527
|
+
JSON.stringify({ at: now, ...report })
|
|
26528
|
+
);
|
|
26529
|
+
return report;
|
|
26530
|
+
});
|
|
26531
|
+
}
|
|
26532
|
+
|
|
25518
26533
|
// ../../packages/math/src/pagerank.ts
|
|
25519
26534
|
function pagerank(input) {
|
|
25520
26535
|
const damping = input.damping ?? 0.85;
|
|
@@ -25961,6 +26976,177 @@ function pagerankLocal(input) {
|
|
|
25961
26976
|
return { scores, iterations: iter, converged };
|
|
25962
26977
|
}
|
|
25963
26978
|
|
|
26979
|
+
// ../../packages/local-graph/src/node-select.ts
|
|
26980
|
+
var SELECT_INDEXES = [
|
|
26981
|
+
"nodes_live_local_community",
|
|
26982
|
+
"nodes_live_fold_candidate",
|
|
26983
|
+
"nodes_live_resolved",
|
|
26984
|
+
"nodes_live_fix_candidate",
|
|
26985
|
+
"nodes_live_anchor_hint"
|
|
26986
|
+
];
|
|
26987
|
+
var EMBEDDING_DIM_SQL = "(CASE WHEN length(embedding_blob) >= 4 THEN length(embedding_blob) / 4 ELSE COALESCE(json_array_length(embedding_json), 0) END)";
|
|
26988
|
+
var KEY = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
26989
|
+
var COLUMNS = /* @__PURE__ */ new Set([
|
|
26990
|
+
"created_at",
|
|
26991
|
+
"last_updated_at",
|
|
26992
|
+
"page_rank",
|
|
26993
|
+
"created_at_seq",
|
|
26994
|
+
"last_reinforced_at_seq",
|
|
26995
|
+
"cumulative_hits",
|
|
26996
|
+
"extraction_confidence"
|
|
26997
|
+
]);
|
|
26998
|
+
function path(key) {
|
|
26999
|
+
if (!KEY.test(key)) throw new Error(`selectNodes: bad attr key ${key}`);
|
|
27000
|
+
return `'$.${key}'`;
|
|
27001
|
+
}
|
|
27002
|
+
function selectIndex(i2) {
|
|
27003
|
+
if (!SELECT_INDEXES.includes(i2)) throw new Error(`selectNodes: bad index ${i2}`);
|
|
27004
|
+
return i2;
|
|
27005
|
+
}
|
|
27006
|
+
function column(c) {
|
|
27007
|
+
if (!COLUMNS.has(c)) throw new Error(`selectNodes: bad column ${c}`);
|
|
27008
|
+
return c;
|
|
27009
|
+
}
|
|
27010
|
+
function compileNodeWhere(q) {
|
|
27011
|
+
if (q.labels.length === 0) throw new Error("selectNodes: no labels");
|
|
27012
|
+
const params = [...q.labels];
|
|
27013
|
+
const byId = (q.where ?? []).some((w) => "idIn" in w || "idPrefix" in w && w.idPrefix.length > 0);
|
|
27014
|
+
const unindexed = byId ? "+" : "";
|
|
27015
|
+
const conds = [`${unindexed}label IN (${q.labels.map(() => "?").join(", ")})`];
|
|
27016
|
+
if (!q.includeClosed) conds.push(`${unindexed}valid_to IS NULL`);
|
|
27017
|
+
for (const w of q.where ?? []) {
|
|
27018
|
+
if ("embedding" in w) {
|
|
27019
|
+
conds.push(`${EMBEDDING_DIM_SQL} ${w.embedding === "present" ? ">" : "="} 0`);
|
|
27020
|
+
} else if ("idPrefix" in w) {
|
|
27021
|
+
conds.push("substr(id, 1, ?) = ?");
|
|
27022
|
+
params.push(w.idPrefix.length, w.idPrefix);
|
|
27023
|
+
if (w.idPrefix.length > 0) {
|
|
27024
|
+
const last = w.idPrefix.charCodeAt(w.idPrefix.length - 1);
|
|
27025
|
+
conds.push("id >= ? AND id < ?");
|
|
27026
|
+
params.push(w.idPrefix, w.idPrefix.slice(0, -1) + String.fromCharCode(last + 1));
|
|
27027
|
+
}
|
|
27028
|
+
} else if ("idIn" in w) {
|
|
27029
|
+
if (w.idIn.length === 0) conds.push("0");
|
|
27030
|
+
else {
|
|
27031
|
+
conds.push(`id IN (${w.idIn.map(() => "?").join(", ")})`);
|
|
27032
|
+
params.push(...w.idIn);
|
|
27033
|
+
}
|
|
27034
|
+
} else if ("descriptionIn" in w) {
|
|
27035
|
+
if (w.descriptionIn.length === 0) conds.push("0");
|
|
27036
|
+
else {
|
|
27037
|
+
conds.push(`description IN (${w.descriptionIn.map(() => "?").join(", ")})`);
|
|
27038
|
+
params.push(...w.descriptionIn);
|
|
27039
|
+
}
|
|
27040
|
+
} else if ("column" in w) {
|
|
27041
|
+
const c = column(w.column);
|
|
27042
|
+
for (const [op, sym] of [["gte", ">="], ["gt", ">"], ["lte", "<="], ["lt", "<"]]) {
|
|
27043
|
+
const v = w[op];
|
|
27044
|
+
if (v !== void 0) {
|
|
27045
|
+
conds.push(`${c} ${sym} ?`);
|
|
27046
|
+
params.push(v);
|
|
27047
|
+
}
|
|
27048
|
+
}
|
|
27049
|
+
} else {
|
|
27050
|
+
const p = path(w.attr);
|
|
27051
|
+
if ("absent" in w) conds.push(`json_type(attrs_json, ${p}) IS NULL`);
|
|
27052
|
+
else if ("present" in w) conds.push(`json_type(attrs_json, ${p}) IS NOT NULL`);
|
|
27053
|
+
else if ("nullish" in w) conds.push(`json_extract(attrs_json, ${p}) IS NULL`);
|
|
27054
|
+
else if ("notNullish" in w) conds.push(`json_extract(attrs_json, ${p}) IS NOT NULL`);
|
|
27055
|
+
else if ("eq" in w || "ne" in w) {
|
|
27056
|
+
const v = "eq" in w ? w.eq : w.ne;
|
|
27057
|
+
const neg = "ne" in w;
|
|
27058
|
+
if (typeof v === "boolean") {
|
|
27059
|
+
conds.push(`json_type(attrs_json, ${p}) ${neg ? "IS NOT" : "="} ?`);
|
|
27060
|
+
params.push(v ? "true" : "false");
|
|
27061
|
+
} else {
|
|
27062
|
+
conds.push(neg ? `json_extract(attrs_json, ${p}) IS NOT ?` : `json_extract(attrs_json, ${p}) = ?`);
|
|
27063
|
+
params.push(v);
|
|
27064
|
+
}
|
|
27065
|
+
}
|
|
27066
|
+
}
|
|
27067
|
+
}
|
|
27068
|
+
return { where: conds.join(" AND "), params };
|
|
27069
|
+
}
|
|
27070
|
+
function attrValueSql(p, doc = "attrs_json") {
|
|
27071
|
+
return `CASE json_type(${doc}, ${p}) WHEN 'integer' THEN CAST(json_extract(${doc}, ${p}) AS REAL) ELSE json_extract(${doc}, ${p}) END`;
|
|
27072
|
+
}
|
|
27073
|
+
function attrDocSql(key) {
|
|
27074
|
+
return isExposureAttr(key) ? exposureDocSql(key) : "attrs_json";
|
|
27075
|
+
}
|
|
27076
|
+
function nodeSelectFrom(q) {
|
|
27077
|
+
if (q.indexedBy) return `nodes INDEXED BY ${selectIndex(q.indexedBy)}`;
|
|
27078
|
+
const where = q.where ?? [];
|
|
27079
|
+
if (!q.includeClosed && where.some((w) => "descriptionIn" in w && w.descriptionIn.length > 0) && !where.some((w) => "idIn" in w)) {
|
|
27080
|
+
return "nodes INDEXED BY nodes_live_description";
|
|
27081
|
+
}
|
|
27082
|
+
return "nodes";
|
|
27083
|
+
}
|
|
27084
|
+
function compileNodeSelect(q) {
|
|
27085
|
+
const { where, params } = compileNodeWhere(q);
|
|
27086
|
+
const attrs = q.attrs ?? [];
|
|
27087
|
+
const attrCols = attrs.map((k, i2) => `, ${attrValueSql(path(k), attrDocSql(k))} AS a${i2}, json_type(${attrDocSql(k)}, ${path(k)}) AS t${i2}`).join("");
|
|
27088
|
+
let order;
|
|
27089
|
+
if (q.orderBy && "column" in q.orderBy) {
|
|
27090
|
+
order = `${column(q.orderBy.column)} ${q.orderBy.desc ? "DESC" : "ASC"}, rowid`;
|
|
27091
|
+
} else if (q.orderBy) {
|
|
27092
|
+
const p = path(q.orderBy.attr);
|
|
27093
|
+
order = `json_extract(attrs_json, ${p}) IS NULL, json_extract(attrs_json, ${p}) ${q.orderBy.desc ? "DESC" : "ASC"}, rowid`;
|
|
27094
|
+
} else if (q.labels.length > 1) {
|
|
27095
|
+
order = `CASE label ${q.labels.map((_, i2) => `WHEN ? THEN ${i2}`).join(" ")} END, rowid`;
|
|
27096
|
+
} else {
|
|
27097
|
+
order = "rowid";
|
|
27098
|
+
}
|
|
27099
|
+
const orderParams = !q.orderBy && q.labels.length > 1 ? [...q.labels] : [];
|
|
27100
|
+
const limit = q.limit !== void 0 ? ` LIMIT ${Math.max(0, Math.floor(q.limit))}` : "";
|
|
27101
|
+
const sql = `SELECT id, label, description, created_at, last_updated_at, page_rank, cumulative_hits, extraction_confidence, extraction_source, community, created_at_seq, last_reinforced_at_seq, valid_to, logical_id, version, ${EMBEDDING_DIM_SQL} AS embedding_dim${q.withEmbedding ? ", embedding_blob, embedding_json" : ""}${attrCols} FROM ${nodeSelectFrom(q)} WHERE ${where} ORDER BY ${order}${limit}`;
|
|
27102
|
+
return { sql, params: [...params, ...orderParams] };
|
|
27103
|
+
}
|
|
27104
|
+
function decodeAttr(value, type) {
|
|
27105
|
+
switch (type) {
|
|
27106
|
+
case null:
|
|
27107
|
+
case void 0:
|
|
27108
|
+
return void 0;
|
|
27109
|
+
case "true":
|
|
27110
|
+
return true;
|
|
27111
|
+
case "false":
|
|
27112
|
+
return false;
|
|
27113
|
+
case "null":
|
|
27114
|
+
return null;
|
|
27115
|
+
case "object":
|
|
27116
|
+
case "array":
|
|
27117
|
+
return typeof value === "string" ? JSON.parse(value) : value;
|
|
27118
|
+
default:
|
|
27119
|
+
return value;
|
|
27120
|
+
}
|
|
27121
|
+
}
|
|
27122
|
+
function decodeSelectedRow(r, attrs, decodeVector) {
|
|
27123
|
+
const out2 = {};
|
|
27124
|
+
attrs.forEach((k, i2) => {
|
|
27125
|
+
const v = decodeAttr(r[`a${i2}`], r[`t${i2}`]);
|
|
27126
|
+
if (v !== void 0) out2[k] = v;
|
|
27127
|
+
});
|
|
27128
|
+
return {
|
|
27129
|
+
id: r["id"],
|
|
27130
|
+
label: r["label"],
|
|
27131
|
+
description: r["description"] ?? "",
|
|
27132
|
+
createdAt: r["created_at"],
|
|
27133
|
+
lastUpdatedAt: r["last_updated_at"],
|
|
27134
|
+
pageRank: r["page_rank"],
|
|
27135
|
+
cumulativeHits: r["cumulative_hits"],
|
|
27136
|
+
extractionConfidence: r["extraction_confidence"],
|
|
27137
|
+
extractionSource: r["extraction_source"],
|
|
27138
|
+
community: r["community"] ?? null,
|
|
27139
|
+
...r["created_at_seq"] != null ? { createdAtSeq: r["created_at_seq"] } : {},
|
|
27140
|
+
...r["last_reinforced_at_seq"] != null ? { lastReinforcedAtSeq: r["last_reinforced_at_seq"] } : {},
|
|
27141
|
+
validTo: r["valid_to"] ?? null,
|
|
27142
|
+
logicalId: r["logical_id"] ?? r["id"],
|
|
27143
|
+
version: Number(r["version"] ?? 1),
|
|
27144
|
+
embeddingDim: Number(r["embedding_dim"] ?? 0),
|
|
27145
|
+
...decodeVector ? { embedding: decodeVector(r["embedding_blob"], r["embedding_json"] ?? "[]") } : {},
|
|
27146
|
+
attrs: out2
|
|
27147
|
+
};
|
|
27148
|
+
}
|
|
27149
|
+
|
|
25964
27150
|
// ../../packages/local-graph/src/store.ts
|
|
25965
27151
|
var PAGE_RANK_SCALE_KEY = "pageRankScale.p95.v1";
|
|
25966
27152
|
var TEXT_SEARCH_COLUMNS = `id, label, description, page_rank,
|
|
@@ -26015,6 +27201,7 @@ function localEdgeViolation(fromLabel, type, toLabel) {
|
|
|
26015
27201
|
}
|
|
26016
27202
|
var SCHEMA_VERSION = 6;
|
|
26017
27203
|
var SESSION_REPAIR_KEY = "session-evidence-v1";
|
|
27204
|
+
var EXPOSURE_SIDE_KEY = "exposure-side-rows-1";
|
|
26018
27205
|
var SESSION_EVIDENCE_PREDICATE = SESSION_EVIDENCE_FIELDS.map((field) => `instr(attrs_json, '${JSON.stringify(field)}') > 0`).join(" OR ");
|
|
26019
27206
|
var SCHEMA_SQL = `
|
|
26020
27207
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
@@ -26078,8 +27265,13 @@ CREATE TABLE IF NOT EXISTS nodes (
|
|
|
26078
27265
|
);
|
|
26079
27266
|
|
|
26080
27267
|
CREATE INDEX IF NOT EXISTS nodes_label ON nodes(label);
|
|
26081
|
-
|
|
26082
|
-
|
|
27268
|
+
-- Dropped: no query plans through them (every prepared statement across the
|
|
27269
|
+
-- local-graph, indexer, context-writer, generalizer and daemon suites was
|
|
27270
|
+
-- planned with and without them, 2026-10-21), and each write that touches
|
|
27271
|
+
-- memory_tier / page_rank (every node upsert, every rescore) paid to maintain
|
|
27272
|
+
-- them. nodes_live_page_rank serves the live page-rank orderings.
|
|
27273
|
+
DROP INDEX IF EXISTS nodes_memory_tier;
|
|
27274
|
+
DROP INDEX IF EXISTS nodes_page_rank;
|
|
26083
27275
|
|
|
26084
27276
|
CREATE TABLE IF NOT EXISTS edges (
|
|
26085
27277
|
id TEXT PRIMARY KEY,
|
|
@@ -26101,7 +27293,10 @@ CREATE TABLE IF NOT EXISTS edges (
|
|
|
26101
27293
|
FOREIGN KEY (to_id) REFERENCES nodes(id) ON DELETE CASCADE
|
|
26102
27294
|
);
|
|
26103
27295
|
|
|
26104
|
-
|
|
27296
|
+
-- Dropped: a prefix of edges_triple (from_id, type, to_id), which every
|
|
27297
|
+
-- from-side lookup and foreign-key check already plans through; it cost 47 MB
|
|
27298
|
+
-- and an index write on every edge write.
|
|
27299
|
+
DROP INDEX IF EXISTS edges_from;
|
|
26105
27300
|
CREATE INDEX IF NOT EXISTS edges_to ON edges(to_id, type);
|
|
26106
27301
|
CREATE INDEX IF NOT EXISTS edges_type ON edges(type);
|
|
26107
27302
|
CREATE UNIQUE INDEX IF NOT EXISTS edges_triple ON edges(from_id, type, to_id);
|
|
@@ -26109,8 +27304,41 @@ CREATE UNIQUE INDEX IF NOT EXISTS edges_triple ON edges(from_id, type, to_id);
|
|
|
26109
27304
|
var TEMPORAL_INDEX_SQL = `
|
|
26110
27305
|
CREATE INDEX IF NOT EXISTS nodes_live ON nodes(label, valid_to);
|
|
26111
27306
|
CREATE INDEX IF NOT EXISTS nodes_live_count ON nodes(valid_to);
|
|
26112
|
-
|
|
27307
|
+
-- Problems by recency (the only label read newest-first). Over every live row,
|
|
27308
|
+
-- each write that stamps last_updated_at moved a code symbol's entry from a
|
|
27309
|
+
-- scattered leaf: ~19 KB per position-only symbol rewrite on a 1.1 GB store.
|
|
27310
|
+
-- label stays the leading column so label = 'Problem' plans without a sort.
|
|
27311
|
+
DROP INDEX IF EXISTS nodes_live_recent;
|
|
27312
|
+
CREATE INDEX IF NOT EXISTS nodes_live_problem_recent ON nodes(label, last_updated_at DESC) WHERE valid_to IS NULL AND label = 'Problem';
|
|
27313
|
+
-- "Anything created since the watermark?" guards (the fold, regression and
|
|
27314
|
+
-- canonical-bind sweeps run one per session boundary). Without it the planner
|
|
27315
|
+
-- walks nodes_live and reads every live row of the label to test created_at:
|
|
27316
|
+
-- 56 MB per guard on a 1.1 GB store, to learn there was nothing to do. valid_to
|
|
27317
|
+
-- is a key column so the planner prefers this over nodes_live (one more term).
|
|
27318
|
+
CREATE INDEX IF NOT EXISTS nodes_live_created ON nodes(label, valid_to, created_at) WHERE valid_to IS NULL;
|
|
27319
|
+
-- Community members (the skills pull reads them twice per session boundary):
|
|
27320
|
+
-- ~15% of semantic nodes carry a localCommunity, and without this every live
|
|
27321
|
+
-- semantic row was read to find them (168 MB \u2192 18 MB per read).
|
|
27322
|
+
CREATE INDEX IF NOT EXISTS nodes_live_local_community ON nodes(label, valid_to, json_extract(attrs_json, '$.localCommunity')) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.localCommunity') IS NOT NULL;
|
|
27323
|
+
-- Problems parked for the fold judge (read every settle boundary): ~1 in 5 open
|
|
27324
|
+
-- Problems; without it every live Problem was read to find them (56 MB).
|
|
27325
|
+
CREATE INDEX IF NOT EXISTS nodes_live_fold_candidate ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_type(attrs_json, '$.foldCandidate') IS NOT NULL;
|
|
27326
|
+
-- Render impressions, one row per node id (exposure-attrs.ts): a render bumps
|
|
27327
|
+
-- ~40 of these, which in the node row rewrote each whole row and its indexes.
|
|
27328
|
+
CREATE TABLE IF NOT EXISTS node_exposure (
|
|
27329
|
+
id TEXT PRIMARY KEY,
|
|
27330
|
+
attrs_json TEXT NOT NULL DEFAULT '{}'
|
|
27331
|
+
) WITHOUT ROWID;
|
|
27332
|
+
-- The semantic-maintenance sweeps' own filters (closed, asked, hinted): each read
|
|
27333
|
+
-- every Problem row (~30 MB on a 1.1 GB store) for the few hundred that match,
|
|
27334
|
+
-- several times a run, and a run follows most writes (2026-10-20).
|
|
27335
|
+
CREATE INDEX IF NOT EXISTS nodes_live_resolved ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.resolvedAt') IS NOT NULL;
|
|
27336
|
+
CREATE INDEX IF NOT EXISTS nodes_live_fix_candidate ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_type(attrs_json, '$.fixCandidateAt') IS NOT NULL;
|
|
27337
|
+
CREATE INDEX IF NOT EXISTS nodes_live_anchor_hint ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.anchorHint') IS NOT NULL;
|
|
26113
27338
|
CREATE INDEX IF NOT EXISTS nodes_session_evidence ON nodes(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
|
|
27339
|
+
-- Rows carrying a calibration counter, any version: the logical nodes wm
|
|
27340
|
+
-- calibration can count (~3k), found without reading every citable row.
|
|
27341
|
+
CREATE INDEX IF NOT EXISTS nodes_wm_counted ON nodes(logical_id, version, valid_to, label, ${WM_CALIBRATION_KEYS.map(wmCounterSql).join(", ")}) WHERE ${WM_COUNTED_PREDICATE};
|
|
26114
27342
|
CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
|
|
26115
27343
|
-- Covering + partial index for labelFingerprint over the instance labels: the
|
|
26116
27344
|
-- live-push probe runs it per workspace on a timer, and each hit used to read
|
|
@@ -26124,31 +27352,70 @@ CREATE INDEX IF NOT EXISTS nodes_instance_fingerprint_v2 ON nodes(label, last_up
|
|
|
26124
27352
|
CREATE INDEX IF NOT EXISTS nodes_live_page_rank ON nodes(valid_to, page_rank);
|
|
26125
27353
|
CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
|
|
26126
27354
|
CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
|
|
27355
|
+
-- Newest live edges first, so a "recent N matching edges" read stops early instead
|
|
27356
|
+
-- of matching and sorting every live edge (the hourly liveness check: 163 MB \u2192
|
|
27357
|
+
-- 69 MB). Named by recentEdgeAttrCoverage; the planner keeps edges_live otherwise.
|
|
27358
|
+
CREATE INDEX IF NOT EXISTS edges_live_created ON edges(created_at) WHERE valid_to IS NULL;
|
|
27359
|
+
-- Live prior-tag edges by recency: the liveness corroboration check reads the
|
|
27360
|
+
-- newest 100 of ~300, and walking all live edges newest-first to find them read
|
|
27361
|
+
-- 69 MB. Its predicate is the one recentEdgeAttrCoverage uses for that marker.
|
|
27362
|
+
CREATE INDEX IF NOT EXISTS edges_live_prior_tag ON edges(created_at) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.priorTag') = 1;
|
|
26127
27363
|
-- Expression index on the per-node relPath (stored in attrs_json) so the
|
|
26128
27364
|
-- incremental reindex can fetch just the changed files' symbols instead of
|
|
26129
27365
|
-- scanning every versioned node. Partial: only live rows are ever queried.
|
|
26130
27366
|
CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.relPath')) WHERE valid_to IS NULL;
|
|
27367
|
+
-- Symbols by qname and doc comments by the symbol they attach to: the drain's
|
|
27368
|
+
-- symbol lexicon resolves the names in the text it ships one at a time, instead
|
|
27369
|
+
-- of reading every Function/Method/Class/Interface/Const and Comment row on each
|
|
27370
|
+
-- live push (~300 MB of a 1.1 GB store per push, 2026-10-20).
|
|
27371
|
+
CREATE INDEX IF NOT EXISTS nodes_live_qname ON nodes(json_extract(attrs_json, '$.qname')) WHERE valid_to IS NULL;
|
|
27372
|
+
CREATE INDEX IF NOT EXISTS nodes_live_attached_to ON nodes(json_extract(attrs_json, '$.attachedTo')) WHERE valid_to IS NULL AND label = 'Comment';
|
|
27373
|
+
-- The attrs the cloud drain gates every instance row on, so it reads them from
|
|
27374
|
+
-- the index alone: pulled cloud rows are most of these labels (7,105 of 7,708
|
|
27375
|
+
-- Solutions on the live store) at ~10 KB of attrs each, and five full scans for
|
|
27376
|
+
-- two attrs read ~220 MB per push to find the few local rows that can ship.
|
|
27377
|
+
-- created_at leads so the index order is findNodeFieldsByLabel's.
|
|
27378
|
+
CREATE INDEX IF NOT EXISTS nodes_live_instance_gate ON nodes(label, created_at, json_extract(attrs_json, '$.source'), json_extract(attrs_json, '$.privateFinding'), json_extract(attrs_json, '$.resolvedAs'), id) WHERE valid_to IS NULL AND label IN ('Problem', 'Vulnerability', 'Solution', 'RootCause');
|
|
27379
|
+
-- Closed rows by relPath, for reviveReobserved (every incremental reindex): the
|
|
27380
|
+
-- live-only index above cannot serve it, so it scanned the whole table (~275 MB
|
|
27381
|
+
-- of a 1.1 GB store per reindex, 2026-10-20).
|
|
27382
|
+
CREATE INDEX IF NOT EXISTS nodes_closed_relpath ON nodes(json_extract(attrs_json, '$.relPath'), last_updated_at) WHERE valid_to IS NOT NULL;
|
|
26131
27383
|
-- Live rows by name, for looking up the few symbols a statement mentions
|
|
26132
27384
|
-- instead of hydrating every code symbol per minted problem.
|
|
26133
27385
|
CREATE INDEX IF NOT EXISTS nodes_live_description ON nodes(description, label) WHERE valid_to IS NULL;
|
|
27386
|
+
-- The six single-attr lookups below are partial on the attr being present: each
|
|
27387
|
+
-- is read only by equality (or IN), which implies it. Keyed on NULL for the ~95%
|
|
27388
|
+
-- of rows that lack the attr, every write to such a row's attrs rewrote its NULL
|
|
27389
|
+
-- entry: ~25 KB per position-only symbol rewrite on a 1.1 GB store (2026-10-21).
|
|
27390
|
+
DROP INDEX IF EXISTS nodes_anchor_hint_path;
|
|
27391
|
+
DROP INDEX IF EXISTS nodes_command_signature;
|
|
27392
|
+
DROP INDEX IF EXISTS nodes_diag_command;
|
|
27393
|
+
DROP INDEX IF EXISTS nodes_lsp_rel_path;
|
|
27394
|
+
DROP INDEX IF EXISTS nodes_cloud_node_id;
|
|
27395
|
+
DROP INDEX IF EXISTS nodes_enrichment_pending;
|
|
26134
27396
|
-- Problems by the file their anchor hint names: the harvest upgrades hints for
|
|
26135
27397
|
-- every edited file of every turn, and a scan parses every Problem's attrs.
|
|
26136
|
-
CREATE INDEX IF NOT EXISTS
|
|
27398
|
+
CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path_v2 ON nodes(json_extract(attrs_json, '$.anchorHint.path')) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.anchorHint.path') IS NOT NULL;
|
|
26137
27399
|
-- Problems by the command / checker / file that opened them. The generalizer
|
|
26138
27400
|
-- looked these up per shell or tool EVENT by hydrating every Problem: ~12s of
|
|
26139
27401
|
-- every minute of daemon main-thread time on a 3.4k-Problem store (2026-10-05).
|
|
26140
|
-
CREATE INDEX IF NOT EXISTS
|
|
26141
|
-
CREATE INDEX IF NOT EXISTS
|
|
26142
|
-
CREATE INDEX IF NOT EXISTS
|
|
27402
|
+
CREATE INDEX IF NOT EXISTS nodes_command_signature_v2 ON nodes(json_extract(attrs_json, '$.commandSignature')) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.commandSignature') IS NOT NULL;
|
|
27403
|
+
CREATE INDEX IF NOT EXISTS nodes_diag_command_v2 ON nodes(json_extract(attrs_json, '$.diagCommand')) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.diagCommand') IS NOT NULL;
|
|
27404
|
+
CREATE INDEX IF NOT EXISTS nodes_lsp_rel_path_v2 ON nodes(json_extract(attrs_json, '$.lspRelPath')) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.lspRelPath') IS NOT NULL;
|
|
26143
27405
|
-- Local nodes by the cloud twin their upload receipt recorded. Every burst /
|
|
26144
27406
|
-- search fusion folds the Network copy of the caller's own knowledge back onto
|
|
26145
27407
|
-- the local node; unindexed, the reverse lookup was a ~300ms attrs scan per call.
|
|
26146
|
-
CREATE INDEX IF NOT EXISTS
|
|
27408
|
+
CREATE INDEX IF NOT EXISTS nodes_cloud_node_id_v2 ON nodes(json_extract(attrs_json, '$.cloudNodeId')) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.cloudNodeId') IS NOT NULL;
|
|
26147
27409
|
-- Nodes still waiting on a root-cause ask. The MCP server checks this on EVERY
|
|
26148
27410
|
-- tool response (the enrichment piggyback), so it must not parse every
|
|
26149
|
-
-- Solution's and Problem's attrs per call.
|
|
27411
|
+
-- Solution's and Problem's attrs per call. Any version: the Problem pull reads
|
|
26150
27412
|
-- closed versions too.
|
|
26151
|
-
CREATE INDEX IF NOT EXISTS
|
|
27413
|
+
CREATE INDEX IF NOT EXISTS nodes_enrichment_pending_v2 ON nodes(json_extract(attrs_json, '$.enrichmentPending')) WHERE json_extract(attrs_json, '$.enrichmentPending') IS NOT NULL;
|
|
27414
|
+
-- Revisit flags, any label, any version: listNeedsRevisit's as-of read runs on
|
|
27415
|
+
-- every context render and otherwise json-checks every row (10.3 s on a 324k-row
|
|
27416
|
+
-- store, to return zero flags). The predicate must stay textually identical to
|
|
27417
|
+
-- nodesAsOfWhereTrue's or the planner will not use it.
|
|
27418
|
+
CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_type(attrs_json, '$.revisit') = 'true';
|
|
26152
27419
|
`;
|
|
26153
27420
|
function encodeEmbedding(emb) {
|
|
26154
27421
|
if (!emb || emb.length === 0) return null;
|
|
@@ -26209,12 +27476,39 @@ function rowToEdge(r) {
|
|
|
26209
27476
|
}
|
|
26210
27477
|
var FINGERPRINT_INDEXED_LABELS = ["Problem", "Vulnerability", "Solution", "RootCause"];
|
|
26211
27478
|
var NODE_LABELS_CHUNK = 256;
|
|
27479
|
+
var NODE_UPSERT_COLUMNS = [
|
|
27480
|
+
"label",
|
|
27481
|
+
"description",
|
|
27482
|
+
"extraction_confidence",
|
|
27483
|
+
"embedding_json",
|
|
27484
|
+
"embedding_blob",
|
|
27485
|
+
"cumulative_surprise",
|
|
27486
|
+
"peak_surprise",
|
|
27487
|
+
"cumulative_hits",
|
|
27488
|
+
"last_updated_at",
|
|
27489
|
+
"memory_tier",
|
|
27490
|
+
"page_rank",
|
|
27491
|
+
"is_landmark",
|
|
27492
|
+
"community",
|
|
27493
|
+
"stability",
|
|
27494
|
+
"last_reinforced_at_seq",
|
|
27495
|
+
"attrs_json"
|
|
27496
|
+
];
|
|
27497
|
+
var EDGE_UPSERT_COLUMNS = ["confidence", "last_seen_at", "nav_successes", "nav_failures", "valid_to", "attrs_json"];
|
|
27498
|
+
function sameSqlValue(a, b) {
|
|
27499
|
+
if (a === b || a == null && b == null) return true;
|
|
27500
|
+
if (a instanceof Uint8Array && b instanceof Uint8Array) return Buffer.compare(a, b) === 0;
|
|
27501
|
+
return false;
|
|
27502
|
+
}
|
|
27503
|
+
var PRIOR_TAG_MARKER = '"priorTag":true';
|
|
26212
27504
|
var SqliteGraphStore = class {
|
|
26213
27505
|
db;
|
|
26214
27506
|
// Typed edge lookups build their SQL from the number of types asked for, and
|
|
26215
27507
|
// used to prepare it on every call: ~600ms of a 2.5s drain build on a live
|
|
26216
27508
|
// 225k-node store was statement compilation alone. Keyed by the SQL text.
|
|
26217
27509
|
typedEdgeStmts = /* @__PURE__ */ new Map();
|
|
27510
|
+
/** Narrow merge updates, keyed by table and the columns they set. */
|
|
27511
|
+
changedColumnStmts = /* @__PURE__ */ new Map();
|
|
26218
27512
|
stmts;
|
|
26219
27513
|
/** Unified graph-mutation counter (see GraphStore.mutationCount). Bumped on
|
|
26220
27514
|
* every node/edge upsert — code-index and capture alike — so the consolidation
|
|
@@ -26233,6 +27527,8 @@ var SqliteGraphStore = class {
|
|
|
26233
27527
|
initialPageRankScale;
|
|
26234
27528
|
constructor(opts) {
|
|
26235
27529
|
this.db = openDatabase(opts.path);
|
|
27530
|
+
this.db.exec("PRAGMA cache_size = -32768");
|
|
27531
|
+
this.db.exec("PRAGMA mmap_size = 2147483648");
|
|
26236
27532
|
this.db.exec(SCHEMA_SQL);
|
|
26237
27533
|
const versionRow = this.db.prepare("SELECT version FROM schema_version LIMIT 1").get();
|
|
26238
27534
|
if (!versionRow) {
|
|
@@ -26249,12 +27545,15 @@ var SqliteGraphStore = class {
|
|
|
26249
27545
|
installEditPlaceholderRetirement(this.db);
|
|
26250
27546
|
installCloudAttrCompaction(this.db);
|
|
26251
27547
|
installHistoryAttrSlim(this.db);
|
|
27548
|
+
installTextIndex(this.db);
|
|
26252
27549
|
if (!this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(SESSION_REPAIR_KEY)) {
|
|
26253
27550
|
this.db.transaction(() => {
|
|
26254
27551
|
this.repairSessionAliases();
|
|
26255
27552
|
this.db.prepare("INSERT OR REPLACE INTO store_meta (key, value) VALUES (?, 'done')").run(SESSION_REPAIR_KEY);
|
|
26256
27553
|
});
|
|
26257
27554
|
}
|
|
27555
|
+
installLineageIntegrityRepair(this.db);
|
|
27556
|
+
installChangeFeed(this.db);
|
|
26258
27557
|
this.stmts = {
|
|
26259
27558
|
insertNode: this.db.prepare(`
|
|
26260
27559
|
INSERT INTO nodes (
|
|
@@ -26293,7 +27592,19 @@ var SqliteGraphStore = class {
|
|
|
26293
27592
|
last_reinforced_at_seq = excluded.last_reinforced_at_seq,
|
|
26294
27593
|
attrs_json = excluded.attrs_json
|
|
26295
27594
|
`),
|
|
26296
|
-
|
|
27595
|
+
nodeUpsertTarget: this.db.prepare(`SELECT rowid AS rid, ${NODE_UPSERT_COLUMNS.join(", ")} FROM nodes WHERE id = ?`),
|
|
27596
|
+
edgeUpsertTarget: this.db.prepare(`SELECT rowid AS rid, ${EDGE_UPSERT_COLUMNS.join(", ")} FROM edges WHERE from_id = ? AND type = ? AND to_id = ?`),
|
|
27597
|
+
getNode: this.db.prepare("SELECT nodes.*, (SELECT x.attrs_json FROM node_exposure x WHERE x.id = nodes.id) AS exposure_json FROM nodes WHERE id = ?"),
|
|
27598
|
+
exposureOf: this.db.prepare("SELECT attrs_json FROM node_exposure WHERE id = ?"),
|
|
27599
|
+
writeExposure: this.db.prepare("INSERT INTO node_exposure(id, attrs_json) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET attrs_json = excluded.attrs_json"),
|
|
27600
|
+
lastUpdatedOf: this.db.prepare("SELECT last_updated_at AS t, attrs_json FROM nodes WHERE id = ?"),
|
|
27601
|
+
labelOf: this.db.prepare("SELECT label FROM nodes WHERE id = ?"),
|
|
27602
|
+
nodeContent: this.db.prepare(
|
|
27603
|
+
"SELECT label, description, extraction_confidence, extraction_source, stability, valid_to, attrs_json FROM nodes WHERE id = ?"
|
|
27604
|
+
),
|
|
27605
|
+
edgeContent: this.db.prepare(
|
|
27606
|
+
"SELECT confidence, valid_to, attrs_json FROM edges WHERE from_id = ? AND type = ? AND to_id = ?"
|
|
27607
|
+
),
|
|
26297
27608
|
// Single-column fast paths for the nightly pass — writing 100k pageRank
|
|
26298
27609
|
// values through the 18-column COALESCE updateNode took ~30s; these are ~10x
|
|
26299
27610
|
// cheaper (one bound column, no COALESCE evaluation per row).
|
|
@@ -26450,6 +27761,7 @@ var SqliteGraphStore = class {
|
|
|
26450
27761
|
"INSERT INTO store_meta (key, value) VALUES ('ingest_seq', :v) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
|
26451
27762
|
)
|
|
26452
27763
|
};
|
|
27764
|
+
this.moveExposureToSideRows();
|
|
26453
27765
|
const seqRow = this.db.prepare("SELECT value FROM store_meta WHERE key = 'ingest_seq'").get();
|
|
26454
27766
|
this.ingestSeq = seqRow ? Number(seqRow.value) : 0;
|
|
26455
27767
|
}
|
|
@@ -26465,6 +27777,93 @@ var SqliteGraphStore = class {
|
|
|
26465
27777
|
AND (t.id IS NULL OR t.valid_to IS NOT NULL)
|
|
26466
27778
|
`).all();
|
|
26467
27779
|
}
|
|
27780
|
+
strandedSymbolsForFiles(fileIds) {
|
|
27781
|
+
const out2 = [];
|
|
27782
|
+
for (let i2 = 0; i2 < fileIds.length; i2 += 500) {
|
|
27783
|
+
const slice = fileIds.slice(i2, i2 + 500);
|
|
27784
|
+
if (slice.length === 0) continue;
|
|
27785
|
+
out2.push(...this.db.prepare(`
|
|
27786
|
+
SELECT f.id AS fileId, e.to_id AS symbolId
|
|
27787
|
+
FROM nodes f CROSS JOIN edges e ON e.from_id = f.id
|
|
27788
|
+
LEFT JOIN nodes t ON t.id = e.to_id
|
|
27789
|
+
WHERE f.id IN (${slice.map(() => "?").join(",")}) AND f.label = 'File' AND f.valid_to IS NULL AND e.valid_to IS NULL
|
|
27790
|
+
AND e.type IN ('DEFINES', 'CONTAINS')
|
|
27791
|
+
AND (t.id IS NULL OR t.valid_to IS NOT NULL)
|
|
27792
|
+
`).all(...slice));
|
|
27793
|
+
}
|
|
27794
|
+
return out2;
|
|
27795
|
+
}
|
|
27796
|
+
firstLiveByNameOrQname(labels, name2) {
|
|
27797
|
+
if (labels.length === 0) return null;
|
|
27798
|
+
const inLabels = labels.map(() => "?").join(",");
|
|
27799
|
+
const probe = (index, match) => this.db.prepare(
|
|
27800
|
+
`SELECT rowid AS rid, created_at AS at, label, description, json_extract(attrs_json, '$.qname') AS qname FROM nodes INDEXED BY ${index}
|
|
27801
|
+
WHERE ${match} = ? AND label IN (${inLabels}) AND valid_to IS NULL`
|
|
27802
|
+
).all(name2, ...labels);
|
|
27803
|
+
const rank = (r) => [labels.indexOf(r.label), r.at, r.rid];
|
|
27804
|
+
const before = (a, b) => a[0] < b[0] || a[0] === b[0] && (a[1] < b[1] || a[1] === b[1] && a[2] < b[2]);
|
|
27805
|
+
let best;
|
|
27806
|
+
for (const r of [...probe("nodes_live_description", "description"), ...probe("nodes_live_qname", "json_extract(attrs_json, '$.qname')")]) {
|
|
27807
|
+
if (!best || before(rank(r), rank(best))) best = r;
|
|
27808
|
+
}
|
|
27809
|
+
return best ? { label: best.label, description: best.description, qname: typeof best.qname === "string" ? best.qname : null } : null;
|
|
27810
|
+
}
|
|
27811
|
+
instanceGateRows(label) {
|
|
27812
|
+
return this.db.prepare(
|
|
27813
|
+
`SELECT id, json_extract(attrs_json, '$.source') AS source, json_extract(attrs_json, '$.privateFinding') AS privateFinding,
|
|
27814
|
+
json_extract(attrs_json, '$.resolvedAs') AS resolvedAs
|
|
27815
|
+
FROM nodes INDEXED BY nodes_live_instance_gate
|
|
27816
|
+
WHERE label IN ('Problem', 'Vulnerability', 'Solution', 'RootCause') AND label = ? AND valid_to IS NULL
|
|
27817
|
+
ORDER BY label, created_at, rowid`
|
|
27818
|
+
).all(label);
|
|
27819
|
+
}
|
|
27820
|
+
wmCalibrationRows(labels) {
|
|
27821
|
+
const counted = this.db.prepare(
|
|
27822
|
+
`SELECT rowid AS rid, logical_id, version, valid_to, label, ${WM_CALIBRATION_KEYS.map((k, i2) => `${wmCounterSql(k)} AS c${i2}`).join(", ")}
|
|
27823
|
+
FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}`
|
|
27824
|
+
).all();
|
|
27825
|
+
const exposed = this.db.prepare(
|
|
27826
|
+
`SELECT n.rowid AS rid, n.logical_id, n.version, n.valid_to, n.label, x.attrs_json AS exposure
|
|
27827
|
+
FROM node_exposure x JOIN nodes n ON n.id = x.id`
|
|
27828
|
+
).all();
|
|
27829
|
+
const others = this.db.prepare(
|
|
27830
|
+
`SELECT rowid AS rid, logical_id, version, valid_to, label FROM nodes WHERE rowid IN (
|
|
27831
|
+
SELECT n.rowid FROM nodes AS n INDEXED BY nodes_logical
|
|
27832
|
+
WHERE n.logical_id IN (SELECT logical_id FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}
|
|
27833
|
+
UNION SELECT n2.logical_id FROM node_exposure x2 JOIN nodes n2 ON n2.id = x2.id)
|
|
27834
|
+
EXCEPT SELECT rowid FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}
|
|
27835
|
+
EXCEPT SELECT n3.rowid FROM node_exposure x3 JOIN nodes n3 ON n3.id = x3.id)`
|
|
27836
|
+
).all();
|
|
27837
|
+
const byRid = /* @__PURE__ */ new Map();
|
|
27838
|
+
for (const r of [...counted, ...others]) {
|
|
27839
|
+
const attrs = {};
|
|
27840
|
+
WM_CALIBRATION_KEYS.forEach((k, i2) => {
|
|
27841
|
+
const v = r[`c${i2}`];
|
|
27842
|
+
if (typeof v === "number") attrs[k] = v;
|
|
27843
|
+
});
|
|
27844
|
+
byRid.set(r.rid, { row: r, attrs });
|
|
27845
|
+
}
|
|
27846
|
+
for (const r of exposed) {
|
|
27847
|
+
const side = JSON.parse(r.exposure);
|
|
27848
|
+
const entry = byRid.get(r.rid) ?? { row: r, attrs: {} };
|
|
27849
|
+
for (const k of WM_CALIBRATION_KEYS) if (typeof side[k] === "number" && Number.isFinite(side[k])) entry.attrs[k] = side[k];
|
|
27850
|
+
byRid.set(r.rid, entry);
|
|
27851
|
+
}
|
|
27852
|
+
const order = new Map(labels.map((l, i2) => [l, i2]));
|
|
27853
|
+
return [...byRid.values()].filter(({ row }) => order.has(row.label)).sort((a, b) => order.get(a.row.label) - order.get(b.row.label) || a.row.rid - b.row.rid).map(({ row, attrs }) => ({ logicalId: row.logical_id, version: Number(row.version), validTo: row.valid_to ?? null, attrs }));
|
|
27854
|
+
}
|
|
27855
|
+
liveCommentsAttachedTo(name2) {
|
|
27856
|
+
return this.db.prepare(
|
|
27857
|
+
`SELECT json_extract(attrs_json, '$.kind') AS kind, json_extract(attrs_json, '$.text') AS text, description FROM nodes INDEXED BY nodes_live_attached_to
|
|
27858
|
+
WHERE label = 'Comment' AND valid_to IS NULL AND json_extract(attrs_json, '$.attachedTo') = ? ORDER BY created_at, rowid`
|
|
27859
|
+
).all(name2);
|
|
27860
|
+
}
|
|
27861
|
+
strandedEdges() {
|
|
27862
|
+
return countStrandedEdges(this.db);
|
|
27863
|
+
}
|
|
27864
|
+
liveHeadConflicts() {
|
|
27865
|
+
return countLiveHeadConflicts(this.db);
|
|
27866
|
+
}
|
|
26468
27867
|
fileIndexStates(ids) {
|
|
26469
27868
|
if (ids.length === 0) return [];
|
|
26470
27869
|
if (ids.length > 500) throw new RangeError("fileIndexStates accepts at most 500 ids per read");
|
|
@@ -26563,7 +27962,7 @@ var SqliteGraphStore = class {
|
|
|
26563
27962
|
}
|
|
26564
27963
|
mergeNode(node) {
|
|
26565
27964
|
this.mutations++;
|
|
26566
|
-
|
|
27965
|
+
const values = {
|
|
26567
27966
|
id: node.id,
|
|
26568
27967
|
label: node.label,
|
|
26569
27968
|
description: node.description,
|
|
@@ -26589,8 +27988,37 @@ var SqliteGraphStore = class {
|
|
|
26589
27988
|
logical_id: node.logicalId ?? node.id,
|
|
26590
27989
|
created_at_seq: node.createdAtSeq ?? null,
|
|
26591
27990
|
last_reinforced_at_seq: node.lastReinforcedAtSeq ?? null,
|
|
26592
|
-
attrs_json:
|
|
26593
|
-
}
|
|
27991
|
+
attrs_json: this.nodeAttrsJson(node.id, node.attrs ?? {})
|
|
27992
|
+
};
|
|
27993
|
+
const current = this.stmts.nodeUpsertTarget.get(node.id);
|
|
27994
|
+
if (current) this.writeChangedColumns("nodes", NODE_UPSERT_COLUMNS, current, values);
|
|
27995
|
+
else this.stmts.insertNode.run(values);
|
|
27996
|
+
}
|
|
27997
|
+
/** What the upsert's ON CONFLICT would write over `current`, setting only the
|
|
27998
|
+
* columns whose value moves (NODE_UPSERT_COLUMNS says why). */
|
|
27999
|
+
writeChangedColumns(table, columns2, current, next) {
|
|
28000
|
+
const changed = columns2.filter((c) => !sameSqlValue(current[c], next[c]));
|
|
28001
|
+
if (changed.length === 0) return;
|
|
28002
|
+
const key = `${table}:${changed.join(",")}`;
|
|
28003
|
+
let stmt2 = this.changedColumnStmts.get(key);
|
|
28004
|
+
if (!stmt2) {
|
|
28005
|
+
stmt2 = this.db.prepare(`UPDATE ${table} SET ${changed.map((c) => `${c} = :${c}`).join(", ")} WHERE rowid = :rid`);
|
|
28006
|
+
this.changedColumnStmts.set(key, stmt2);
|
|
28007
|
+
}
|
|
28008
|
+
stmt2.run({ rid: current["rid"], ...Object.fromEntries(changed.map((c) => [c, next[c]])) });
|
|
28009
|
+
}
|
|
28010
|
+
/** mergeNode, unless the live row already holds this content. Content is what
|
|
28011
|
+
* the columns a merge rewrites from the producer (label, description, attrs,
|
|
28012
|
+
* confidence, stability);
|
|
28013
|
+
* rank, community, landmark, surprise, hits, tier and the vector belong to
|
|
28014
|
+
* the passes that compute them, and a skipped write leaves them as they are
|
|
28015
|
+
* where a merge would have reset them to the producer's defaults. Returns
|
|
28016
|
+
* whether it wrote. */
|
|
28017
|
+
mergeNodeIfChanged(node) {
|
|
28018
|
+
const row = this.stmts.nodeContent.get(node.id);
|
|
28019
|
+
if (row && row.valid_to === null && row.label === node.label && row.description === node.description && row.extraction_confidence === node.extractionConfidence && row.stability === node.stability && !EXPOSURE_ATTRS.some((k) => node.attrs?.[k] !== void 0) && row.attrs_json === JSON.stringify(node.attrs ?? {})) return false;
|
|
28020
|
+
this.mergeNode(node);
|
|
28021
|
+
return true;
|
|
26594
28022
|
}
|
|
26595
28023
|
updateNode(id, patch) {
|
|
26596
28024
|
const hasEmbedding = patch.embedding !== void 0 && patch.embedding.length > 0;
|
|
@@ -26615,7 +28043,37 @@ var SqliteGraphStore = class {
|
|
|
26615
28043
|
logical_id: patch.logicalId ?? null,
|
|
26616
28044
|
created_at_seq: patch.createdAtSeq ?? null,
|
|
26617
28045
|
last_reinforced_at_seq: patch.lastReinforcedAtSeq ?? null,
|
|
26618
|
-
attrs_json: patch.attrs ?
|
|
28046
|
+
attrs_json: patch.attrs ? this.nodeAttrsJson(id, patch.attrs) : null
|
|
28047
|
+
});
|
|
28048
|
+
}
|
|
28049
|
+
/**
|
|
28050
|
+
* The node-row attrs of a write, after handing any exposure keys to the side
|
|
28051
|
+
* row (exposure-attrs.ts). `getNode` returns exposure merged into attrs, so a
|
|
28052
|
+
* read-modify-write carries it; kept in the row it would re-bloat the row and
|
|
28053
|
+
* drift from the side row. Unchanged exposure costs no write.
|
|
28054
|
+
*/
|
|
28055
|
+
nodeAttrsJson(id, attrs) {
|
|
28056
|
+
if (!EXPOSURE_ATTRS.some((k) => attrs[k] !== void 0)) return JSON.stringify(attrs);
|
|
28057
|
+
const current = this.stmts.exposureOf.get(id);
|
|
28058
|
+
const next = JSON.stringify({ ...current ? JSON.parse(current.attrs_json) : {}, ...pickExposure(attrs) });
|
|
28059
|
+
if (current?.attrs_json !== next) this.stmts.writeExposure.run(id, next);
|
|
28060
|
+
const rest = { ...attrs };
|
|
28061
|
+
for (const k of EXPOSURE_ATTRS) delete rest[k];
|
|
28062
|
+
return JSON.stringify(rest);
|
|
28063
|
+
}
|
|
28064
|
+
/** One-time: exposure keys written to node rows before the side table existed
|
|
28065
|
+
* move to it. Only rows carrying a counter can carry exposure, and those are
|
|
28066
|
+
* what nodes_wm_counted holds, so this reads the index, not the table. */
|
|
28067
|
+
moveExposureToSideRows() {
|
|
28068
|
+
if (this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(EXPOSURE_SIDE_KEY)) return;
|
|
28069
|
+
const rows = this.db.prepare(`SELECT id, attrs_json FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}`).all();
|
|
28070
|
+
const move = this.db.prepare("UPDATE nodes SET attrs_json = ? WHERE id = ?");
|
|
28071
|
+
this.db.transaction(() => {
|
|
28072
|
+
for (const r of rows) {
|
|
28073
|
+
const stripped = this.nodeAttrsJson(r.id, JSON.parse(r.attrs_json));
|
|
28074
|
+
if (stripped !== r.attrs_json) move.run(stripped, r.id);
|
|
28075
|
+
}
|
|
28076
|
+
this.db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, 'done')").run(EXPOSURE_SIDE_KEY);
|
|
26619
28077
|
});
|
|
26620
28078
|
}
|
|
26621
28079
|
/** Fast single-column writes for the nightly pass (avoid the 18-column COALESCE
|
|
@@ -26626,26 +28084,30 @@ var SqliteGraphStore = class {
|
|
|
26626
28084
|
setLandmark(id, isLandmark) {
|
|
26627
28085
|
this.stmts.setLandmark.run({ id, v: isLandmark ? 1 : 0 });
|
|
26628
28086
|
}
|
|
26629
|
-
setLocalCommunity(id, community,
|
|
26630
|
-
this.stmts.setLocalCommunity.run({ id, v: community, col:
|
|
28087
|
+
setLocalCommunity(id, community, column2) {
|
|
28088
|
+
this.stmts.setLocalCommunity.run({ id, v: community, col: column2 ? 1 : 0 });
|
|
26631
28089
|
}
|
|
26632
28090
|
fieldStmts = /* @__PURE__ */ new Map();
|
|
26633
28091
|
findNodeFieldsByLabel(label, attrKeys) {
|
|
26634
28092
|
for (const k of attrKeys) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) throw new Error(`findNodeFieldsByLabel: bad attr key ${k}`);
|
|
26635
28093
|
const cacheKey = attrKeys.join(",");
|
|
26636
|
-
let
|
|
26637
|
-
if (!
|
|
26638
|
-
const cols = attrKeys.map((k, i2) => `,
|
|
26639
|
-
|
|
26640
|
-
|
|
28094
|
+
let stmt2 = this.fieldStmts.get(cacheKey);
|
|
28095
|
+
if (!stmt2) {
|
|
28096
|
+
const cols = attrKeys.map((k, i2) => `, ${attrValueSql(`'$.${k}'`)} AS a${i2}, json_type(attrs_json, '$.${k}') AS t${i2}`).join("");
|
|
28097
|
+
stmt2 = this.db.prepare(
|
|
28098
|
+
// Ordered, not left to the planner: readers take "first row wins" (the
|
|
28099
|
+
// symbol lexicon's name winner and doc pick), and the order otherwise
|
|
28100
|
+
// followed whichever partial index the planner chose. nodes_live_created
|
|
28101
|
+
// serves this order without a sort.
|
|
28102
|
+
`SELECT id, description, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL ORDER BY created_at, rowid`
|
|
26641
28103
|
);
|
|
26642
|
-
this.fieldStmts.set(cacheKey,
|
|
28104
|
+
this.fieldStmts.set(cacheKey, stmt2);
|
|
26643
28105
|
}
|
|
26644
|
-
return
|
|
28106
|
+
return stmt2.all(label).map((r) => {
|
|
26645
28107
|
const attrs = {};
|
|
26646
28108
|
attrKeys.forEach((k, i2) => {
|
|
26647
|
-
const v = r[`a${i2}`];
|
|
26648
|
-
if (v !==
|
|
28109
|
+
const v = decodeAttr(r[`a${i2}`], r[`t${i2}`]);
|
|
28110
|
+
if (v !== void 0) attrs[k] = v;
|
|
26649
28111
|
});
|
|
26650
28112
|
return {
|
|
26651
28113
|
id: r["id"],
|
|
@@ -26656,6 +28118,15 @@ var SqliteGraphStore = class {
|
|
|
26656
28118
|
};
|
|
26657
28119
|
});
|
|
26658
28120
|
}
|
|
28121
|
+
selectNodes(q) {
|
|
28122
|
+
const { sql, params } = compileNodeSelect(q);
|
|
28123
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
28124
|
+
return rows.map((r) => decodeSelectedRow(r, q.attrs ?? [], q.withEmbedding ? decodeEmbedding : void 0));
|
|
28125
|
+
}
|
|
28126
|
+
countNodes(q) {
|
|
28127
|
+
const { where, params } = compileNodeWhere(q);
|
|
28128
|
+
return Number(this.db.prepare(`SELECT COUNT(*) AS n FROM ${nodeSelectFrom(q)} WHERE ${where}`).get(...params).n);
|
|
28129
|
+
}
|
|
26659
28130
|
setCommunity(id, community) {
|
|
26660
28131
|
this.stmts.setCommunity.run({ id, v: community });
|
|
26661
28132
|
}
|
|
@@ -26668,7 +28139,7 @@ var SqliteGraphStore = class {
|
|
|
26668
28139
|
return;
|
|
26669
28140
|
}
|
|
26670
28141
|
this.mutations++;
|
|
26671
|
-
|
|
28142
|
+
const values = {
|
|
26672
28143
|
id: edge.id,
|
|
26673
28144
|
from_id: edge.from,
|
|
26674
28145
|
to_id: edge.to,
|
|
@@ -26682,8 +28153,33 @@ var SqliteGraphStore = class {
|
|
|
26682
28153
|
valid_from: edge.validFrom ?? edge.createdAt,
|
|
26683
28154
|
valid_to: edge.validTo ?? null,
|
|
26684
28155
|
attrs_json: JSON.stringify(edge.attrs ?? {})
|
|
28156
|
+
};
|
|
28157
|
+
const current = this.stmts.edgeUpsertTarget.get(edge.from, edge.type, edge.to);
|
|
28158
|
+
if (!current) {
|
|
28159
|
+
this.stmts.insertEdge.run(values);
|
|
28160
|
+
return;
|
|
28161
|
+
}
|
|
28162
|
+
this.writeChangedColumns("edges", EDGE_UPSERT_COLUMNS, current, {
|
|
28163
|
+
...values,
|
|
28164
|
+
confidence: Math.max(current["confidence"], edge.confidence),
|
|
28165
|
+
valid_to: null
|
|
26685
28166
|
});
|
|
26686
28167
|
}
|
|
28168
|
+
/** mergeEdge, unless the live edge already holds this content: same attrs, and
|
|
28169
|
+
* a confidence the upsert's MAX would keep. Its navigation posteriors and
|
|
28170
|
+
* last-seen stamp stay as they are where a merge would have reset them. The
|
|
28171
|
+
* ontology gate runs first either way, so refusals are recorded as before.
|
|
28172
|
+
* Returns whether it wrote (or refused). */
|
|
28173
|
+
mergeEdgeIfChanged(edge) {
|
|
28174
|
+
if (!this.edgeRuleViolation(edge)) {
|
|
28175
|
+
const row = this.stmts.edgeContent.get(edge.from, edge.type, edge.to);
|
|
28176
|
+
if (row && row.valid_to === null && row.confidence >= edge.confidence && row.attrs_json === JSON.stringify(edge.attrs ?? {})) {
|
|
28177
|
+
return false;
|
|
28178
|
+
}
|
|
28179
|
+
}
|
|
28180
|
+
this.mergeEdge(edge);
|
|
28181
|
+
return true;
|
|
28182
|
+
}
|
|
26687
28183
|
/** EDGE_RULES check against the stored endpoints' labels, with the LOCAL
|
|
26688
28184
|
* overlay consulted first. Returns the reason string on a documented-
|
|
26689
28185
|
* forbidden combination, else null. Point lookups on the id PK — negligible
|
|
@@ -26728,11 +28224,13 @@ var SqliteGraphStore = class {
|
|
|
26728
28224
|
}
|
|
26729
28225
|
}
|
|
26730
28226
|
edgeRuleViolation(edge) {
|
|
26731
|
-
return localEdgeViolation(
|
|
26732
|
-
|
|
26733
|
-
|
|
26734
|
-
|
|
26735
|
-
|
|
28227
|
+
return localEdgeViolation(this.labelOf(edge.from), edge.type, this.labelOf(edge.to));
|
|
28228
|
+
}
|
|
28229
|
+
/** An endpoint's label, any version, as `getNode(id)?.label` reads it. Every
|
|
28230
|
+
* edge write checks both endpoints, and hydrating them (attrs + vector) to
|
|
28231
|
+
* read one leading column was most of an incremental reindex's reads. */
|
|
28232
|
+
labelOf(id) {
|
|
28233
|
+
return this.stmts.labelOf.get(id)?.label;
|
|
26736
28234
|
}
|
|
26737
28235
|
updateEdge(id, patch) {
|
|
26738
28236
|
this.stmts.updateEdge.run({
|
|
@@ -26748,18 +28246,38 @@ var SqliteGraphStore = class {
|
|
|
26748
28246
|
}
|
|
26749
28247
|
getNode(id) {
|
|
26750
28248
|
const row = this.stmts.getNode.get(id);
|
|
26751
|
-
|
|
28249
|
+
if (!row) return null;
|
|
28250
|
+
const node = rowToNode(row);
|
|
28251
|
+
if (row.exposure_json) Object.assign(node.attrs, JSON.parse(row.exposure_json));
|
|
28252
|
+
return node;
|
|
28253
|
+
}
|
|
28254
|
+
/** A node's render exposure: the side row's keys over the node row's own
|
|
28255
|
+
* (exposure-attrs.ts), plus the node's `lastUpdatedAt` (the "shown since
|
|
28256
|
+
* touch" counter is relative to it) — without reading the node's attrs. */
|
|
28257
|
+
exposureOf(id) {
|
|
28258
|
+
const row = this.stmts.lastUpdatedOf.get(id);
|
|
28259
|
+
if (!row) return null;
|
|
28260
|
+
const side = this.stmts.exposureOf.get(id);
|
|
28261
|
+
return {
|
|
28262
|
+
attrs: { ...pickExposure(JSON.parse(row.attrs_json)), ...side ? JSON.parse(side.attrs_json) : {} },
|
|
28263
|
+
lastUpdatedAt: row.t
|
|
28264
|
+
};
|
|
28265
|
+
}
|
|
28266
|
+
/** Replace a node's exposure (only EXPOSURE_ATTRS keys belong here). Touches
|
|
28267
|
+
* neither the node row nor its indexes, nor the change feed. */
|
|
28268
|
+
writeExposure(id, attrs) {
|
|
28269
|
+
this.stmts.writeExposure.run(id, JSON.stringify(attrs));
|
|
26752
28270
|
}
|
|
26753
28271
|
nodeLabels(ids) {
|
|
26754
28272
|
const width = NODE_LABELS_CHUNK;
|
|
26755
|
-
const
|
|
28273
|
+
const stmt2 = this.typedEdgeStmt(
|
|
26756
28274
|
`SELECT id, label FROM nodes WHERE id IN (${Array.from({ length: width }, () => "?").join(",")})`
|
|
26757
28275
|
);
|
|
26758
28276
|
const labels = /* @__PURE__ */ new Map();
|
|
26759
28277
|
for (let i2 = 0; i2 < ids.length; i2 += width) {
|
|
26760
28278
|
const chunk = ids.slice(i2, i2 + width);
|
|
26761
28279
|
while (chunk.length < width) chunk.push(null);
|
|
26762
|
-
for (const row of
|
|
28280
|
+
for (const row of stmt2.all(...chunk)) labels.set(row.id, row.label);
|
|
26763
28281
|
}
|
|
26764
28282
|
return labels;
|
|
26765
28283
|
}
|
|
@@ -26776,7 +28294,21 @@ var SqliteGraphStore = class {
|
|
|
26776
28294
|
* per project per request; the materializing form held the daemon's event
|
|
26777
28295
|
* loop for seconds at scale. */
|
|
26778
28296
|
countNodesByLabel(label) {
|
|
26779
|
-
return Number(this.stmts.countByLabel.get(label).n);
|
|
28297
|
+
return this.countAt(`label:${label}`, [nodeScope(label)], () => Number(this.stmts.countByLabel.get(label).n));
|
|
28298
|
+
}
|
|
28299
|
+
/** Counts asked for on every status poll (/health, `errata status`, the
|
|
28300
|
+
* dashboards): a live-node COUNT(*) reads the whole liveness index, ~3 MB per
|
|
28301
|
+
* call on a 1.1 GB store, to recount a set that moves only on a write. Each is
|
|
28302
|
+
* kept with the change-feed reading of the rows it counts — triggers bump it on
|
|
28303
|
+
* every write from any connection, so a hit is exact, not a TTL guess. */
|
|
28304
|
+
counts = /* @__PURE__ */ new Map();
|
|
28305
|
+
countAt(key, scopes, count) {
|
|
28306
|
+
const seq = readChangeSeq(this.db, scopes);
|
|
28307
|
+
const hit = this.counts.get(key);
|
|
28308
|
+
if (hit && hit.seq === seq) return hit.n;
|
|
28309
|
+
const n = count();
|
|
28310
|
+
this.counts.set(key, { seq, n });
|
|
28311
|
+
return n;
|
|
26780
28312
|
}
|
|
26781
28313
|
/** Live rows for a label with attrs and creation stamps only — for scans that
|
|
26782
28314
|
* read attrs and never the vector. `findNodesByLabel` decodes every embedding
|
|
@@ -26785,16 +28317,39 @@ var SqliteGraphStore = class {
|
|
|
26785
28317
|
findNodeStampsByLabel(label, attrKey) {
|
|
26786
28318
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attrKey)) throw new Error(`findNodeStampsByLabel: bad attr key ${attrKey}`);
|
|
26787
28319
|
const rows = this.db.prepare(
|
|
26788
|
-
`SELECT id, created_at, created_at_seq, json_type(
|
|
28320
|
+
`SELECT id, created_at, created_at_seq, json_type(${attrDocSql(attrKey)}, '$.${attrKey}') IS NOT NULL AS has,
|
|
28321
|
+
COALESCE(json_extract(attrs_json, '$.source') = 'cloud', 0) AS cloud
|
|
26789
28322
|
FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
26790
28323
|
).all(label);
|
|
26791
28324
|
return rows.map((r) => ({
|
|
26792
28325
|
id: r.id,
|
|
26793
28326
|
createdAt: r.created_at,
|
|
26794
28327
|
...r.created_at_seq != null ? { createdAtSeq: r.created_at_seq } : {},
|
|
26795
|
-
has: r.has === 1
|
|
28328
|
+
has: r.has === 1,
|
|
28329
|
+
cloud: r.cloud === 1
|
|
26796
28330
|
}));
|
|
26797
28331
|
}
|
|
28332
|
+
findNodeStampsByLabelAttrs(label, attrKeys) {
|
|
28333
|
+
for (const k of attrKeys) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) throw new Error(`findNodeStampsByLabelAttrs: bad attr key ${k}`);
|
|
28334
|
+
const keys = [...new Set(attrKeys)];
|
|
28335
|
+
const rows = this.db.prepare(
|
|
28336
|
+
`SELECT id, created_at, created_at_seq,
|
|
28337
|
+
${keys.map((k, i2) => `json_type(${attrDocSql(k)}, '$.${k}') IS NOT NULL AS h${i2}`).join(", ")}${keys.length ? "," : ""}
|
|
28338
|
+
COALESCE(json_extract(attrs_json, '$.source') = 'cloud', 0) AS cloud
|
|
28339
|
+
FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
28340
|
+
).all(label);
|
|
28341
|
+
const out2 = new Map(keys.map((k) => [k, []]));
|
|
28342
|
+
for (const r of rows) {
|
|
28343
|
+
keys.forEach((k, i2) => out2.get(k).push({
|
|
28344
|
+
id: r.id,
|
|
28345
|
+
createdAt: r.created_at,
|
|
28346
|
+
...r.created_at_seq != null ? { createdAtSeq: r.created_at_seq } : {},
|
|
28347
|
+
has: r[`h${i2}`] === 1,
|
|
28348
|
+
cloud: r.cloud === 1
|
|
28349
|
+
}));
|
|
28350
|
+
}
|
|
28351
|
+
return out2;
|
|
28352
|
+
}
|
|
26798
28353
|
findNodeAttrsByLabel(label) {
|
|
26799
28354
|
const rows = this.stmts.attrsByLabel.all(label);
|
|
26800
28355
|
return rows.map((r) => ({
|
|
@@ -26822,8 +28377,8 @@ var SqliteGraphStore = class {
|
|
|
26822
28377
|
after = rows[rows.length - 1].rid;
|
|
26823
28378
|
}
|
|
26824
28379
|
}
|
|
26825
|
-
|
|
26826
|
-
const rows = this.db.prepare("SELECT id FROM nodes WHERE label =
|
|
28380
|
+
recentProblemIds(limit) {
|
|
28381
|
+
const rows = this.db.prepare("SELECT id FROM nodes WHERE label = 'Problem' AND valid_to IS NULL ORDER BY last_updated_at DESC LIMIT ?").all(Math.max(0, limit));
|
|
26827
28382
|
return rows.map((r) => r.id);
|
|
26828
28383
|
}
|
|
26829
28384
|
recentOpenProblemCandidates(opts) {
|
|
@@ -26903,6 +28458,20 @@ var SqliteGraphStore = class {
|
|
|
26903
28458
|
}
|
|
26904
28459
|
searchTextCandidates(terms) {
|
|
26905
28460
|
if (terms.length === 0) return [];
|
|
28461
|
+
const indexed = terms.filter((t) => t.length >= TEXT_INDEX_MIN_TERM);
|
|
28462
|
+
if (indexed.length > 0 && textIndexReady(this.db)) {
|
|
28463
|
+
const short = terms.filter((t) => t.length < TEXT_INDEX_MIN_TERM).map((t) => t.toLowerCase());
|
|
28464
|
+
const rows2 = this.db.prepare(`WITH hits AS MATERIALIZED (
|
|
28465
|
+
SELECT t.rowid AS rid, COUNT(*) AS matched FROM json_each(:terms) q, nodes_text t
|
|
28466
|
+
WHERE t.description LIKE '%' || q.value || '%' GROUP BY t.rowid
|
|
28467
|
+
) SELECT ${TEXT_SEARCH_COLUMNS}, hits.matched FROM hits JOIN nodes ON nodes.rowid = hits.rid
|
|
28468
|
+
WHERE nodes.valid_to IS NULL ORDER BY page_rank DESC, last_updated_at DESC`).all({ terms: JSON.stringify(indexed) });
|
|
28469
|
+
return rows2.map((row) => {
|
|
28470
|
+
const text = row.description.toLowerCase();
|
|
28471
|
+
const extra = short.filter((t) => text.includes(t)).length;
|
|
28472
|
+
return { node: textSearchNode(row), matched: row.matched + extra };
|
|
28473
|
+
});
|
|
28474
|
+
}
|
|
26906
28475
|
const rows = this.db.prepare(`WITH matches AS MATERIALIZED (
|
|
26907
28476
|
SELECT n.id, COUNT(*) AS matched FROM nodes n INDEXED BY nodes_live_description, json_each(:terms) t
|
|
26908
28477
|
WHERE n.valid_to IS NULL AND n.description LIKE '%' || t.value || '%'
|
|
@@ -26911,6 +28480,18 @@ var SqliteGraphStore = class {
|
|
|
26911
28480
|
JOIN matches USING (id) ORDER BY page_rank DESC, last_updated_at DESC`).all({ terms: JSON.stringify(terms) });
|
|
26912
28481
|
return rows.map((row) => ({ node: textSearchNode(row), matched: row.matched }));
|
|
26913
28482
|
}
|
|
28483
|
+
commentAnchors(commentIds) {
|
|
28484
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
28485
|
+
if (commentIds.length === 0) return out2;
|
|
28486
|
+
const rows = this.db.prepare(`WITH a AS MATERIALIZED (
|
|
28487
|
+
SELECT q.value AS cid, COALESCE(
|
|
28488
|
+
(SELECT to_id FROM edges WHERE from_id = q.value AND type IN ('EXPLAINS', 'ANNOTATES') AND valid_to IS NULL LIMIT 1),
|
|
28489
|
+
(SELECT from_id FROM edges WHERE to_id = q.value AND type = 'DOCUMENTED_BY' AND valid_to IS NULL LIMIT 1)
|
|
28490
|
+
) AS aid FROM json_each(:ids) q
|
|
28491
|
+
) SELECT a.cid AS cid, ${TEXT_SEARCH_COLUMNS} FROM a JOIN nodes ON nodes.id = a.aid AND nodes.valid_to IS NULL`).all({ ids: JSON.stringify(commentIds) });
|
|
28492
|
+
for (const row of rows) out2.set(row.cid, textSearchNode(row));
|
|
28493
|
+
return out2;
|
|
28494
|
+
}
|
|
26914
28495
|
getTextSearchNode(id) {
|
|
26915
28496
|
const row = this.db.prepare(`SELECT ${TEXT_SEARCH_COLUMNS} FROM nodes WHERE id = ? AND valid_to IS NULL`).get(id);
|
|
26916
28497
|
return row ? textSearchNode(row) : null;
|
|
@@ -26930,12 +28511,12 @@ var SqliteGraphStore = class {
|
|
|
26930
28511
|
return scale;
|
|
26931
28512
|
}
|
|
26932
28513
|
typedEdgeStmt(sql) {
|
|
26933
|
-
let
|
|
26934
|
-
if (!
|
|
26935
|
-
|
|
26936
|
-
this.typedEdgeStmts.set(sql,
|
|
28514
|
+
let stmt2 = this.typedEdgeStmts.get(sql);
|
|
28515
|
+
if (!stmt2) {
|
|
28516
|
+
stmt2 = this.db.prepare(sql);
|
|
28517
|
+
this.typedEdgeStmts.set(sql, stmt2);
|
|
26937
28518
|
}
|
|
26938
|
-
return
|
|
28519
|
+
return stmt2;
|
|
26939
28520
|
}
|
|
26940
28521
|
outEdges(nodeId, types) {
|
|
26941
28522
|
if (!types || types.length === 0) {
|
|
@@ -26994,21 +28575,54 @@ var SqliteGraphStore = class {
|
|
|
26994
28575
|
return out2;
|
|
26995
28576
|
}
|
|
26996
28577
|
nodeCount() {
|
|
26997
|
-
return Number(this.stmts.nodeCount.get().n);
|
|
28578
|
+
return this.countAt("nodes", [ANY_CHANGE], () => Number(this.stmts.nodeCount.get().n));
|
|
26998
28579
|
}
|
|
26999
28580
|
mutationCount() {
|
|
27000
28581
|
return this.mutations;
|
|
27001
28582
|
}
|
|
28583
|
+
changeStamp() {
|
|
28584
|
+
const { data_version } = this.db.pragma("data_version");
|
|
28585
|
+
const { n } = this.db.prepare("SELECT total_changes() AS n").get();
|
|
28586
|
+
return `${data_version}:${n}`;
|
|
28587
|
+
}
|
|
28588
|
+
changeSeq(scopes) {
|
|
28589
|
+
return readChangeSeq(this.db, scopes);
|
|
28590
|
+
}
|
|
28591
|
+
lineageChangedFiles(sinceSeq) {
|
|
28592
|
+
return readLineageChangedFiles(this.db, sinceSeq);
|
|
28593
|
+
}
|
|
28594
|
+
changedNodeIds(sinceSeq, labels) {
|
|
28595
|
+
return readChangedNodeIds(this.db, sinceSeq, labels);
|
|
28596
|
+
}
|
|
28597
|
+
changedAmong(sinceSeq, ids) {
|
|
28598
|
+
return readChangedAmong(this.db, sinceSeq, ids);
|
|
28599
|
+
}
|
|
28600
|
+
pruneChangeLog() {
|
|
28601
|
+
return pruneChangeLog(this.db);
|
|
28602
|
+
}
|
|
28603
|
+
fileRowStates(ids) {
|
|
28604
|
+
if (ids.length === 0) return [];
|
|
28605
|
+
if (ids.length > 500) throw new RangeError("fileRowStates accepts at most 500 ids per read");
|
|
28606
|
+
return this.db.prepare(`SELECT id, last_updated_at AS lastUpdatedAt, valid_to AS validTo FROM nodes WHERE id IN (${ids.map(() => "?").join(",")})`).all(...ids);
|
|
28607
|
+
}
|
|
28608
|
+
// Read and advanced in SQL, never from a per-connection copy: pass workers and
|
|
28609
|
+
// MCP processes open their own connections and advance the same row, so a
|
|
28610
|
+
// copy loaded at open went stale, and `copy + 1` wrote a duplicate or a
|
|
28611
|
+
// backwards seq from whichever connection had fallen behind.
|
|
27002
28612
|
currentIngestSeq() {
|
|
28613
|
+
const row = this.db.prepare("SELECT value FROM store_meta WHERE key = 'ingest_seq'").get();
|
|
28614
|
+
this.ingestSeq = row ? Number(row.value) : 0;
|
|
27003
28615
|
return this.ingestSeq;
|
|
27004
28616
|
}
|
|
27005
28617
|
nextIngestSeq() {
|
|
27006
|
-
this.
|
|
27007
|
-
|
|
28618
|
+
const row = this.db.prepare(
|
|
28619
|
+
"INSERT INTO store_meta (key, value) VALUES ('ingest_seq', '1') ON CONFLICT(key) DO UPDATE SET value = CAST(value AS INTEGER) + 1 RETURNING value"
|
|
28620
|
+
).get();
|
|
28621
|
+
this.ingestSeq = Number(row.value);
|
|
27008
28622
|
return this.ingestSeq;
|
|
27009
28623
|
}
|
|
27010
28624
|
edgeCount() {
|
|
27011
|
-
return Number(this.stmts.edgeCount.get().n);
|
|
28625
|
+
return this.countAt("edges", [ANY_CHANGE], () => Number(this.stmts.edgeCount.get().n));
|
|
27012
28626
|
}
|
|
27013
28627
|
// Lightweight whole-graph scans — select only the columns the caller needs so a
|
|
27014
28628
|
// 100k-node pass skips the per-row attrs JSON.parse + embedding decode rowToNode
|
|
@@ -27136,28 +28750,63 @@ var SqliteGraphStore = class {
|
|
|
27136
28750
|
}
|
|
27137
28751
|
}
|
|
27138
28752
|
reviveReobserved(relPaths, workspaceId, since) {
|
|
27139
|
-
|
|
28753
|
+
const revived = [];
|
|
27140
28754
|
const CHUNK = 400;
|
|
27141
28755
|
for (let i2 = 0; i2 < relPaths.length; i2 += CHUNK) {
|
|
27142
28756
|
const slice = relPaths.slice(i2, i2 + CHUNK);
|
|
27143
28757
|
if (slice.length === 0) continue;
|
|
27144
|
-
const
|
|
27145
|
-
`
|
|
28758
|
+
const rows = this.db.prepare(
|
|
28759
|
+
`SELECT id, valid_to AS closedAt, COALESCE(logical_id, id) AS lid FROM nodes
|
|
27146
28760
|
WHERE valid_to IS NOT NULL
|
|
27147
28761
|
AND last_updated_at >= ?
|
|
27148
28762
|
AND json_extract(attrs_json, '$.workspaceId') = ?
|
|
27149
|
-
AND json_extract(attrs_json, '$.relPath') IN (${slice.map(() => "?").join(",")})
|
|
27150
|
-
|
|
27151
|
-
|
|
27152
|
-
|
|
27153
|
-
|
|
27154
|
-
return
|
|
28763
|
+
AND json_extract(attrs_json, '$.relPath') IN (${slice.map(() => "?").join(",")})
|
|
28764
|
+
AND json_type(attrs_json, '$.historical') IS NOT 'true'`
|
|
28765
|
+
).all(since, workspaceId, ...slice);
|
|
28766
|
+
revived.push(...rows);
|
|
28767
|
+
}
|
|
28768
|
+
if (revived.length === 0) return 0;
|
|
28769
|
+
const reopen = this.db.prepare("UPDATE nodes SET valid_to = NULL WHERE id = ? AND valid_to IS NOT NULL");
|
|
28770
|
+
for (const r of revived) reopen.run(r.id);
|
|
28771
|
+
for (const r of revived) reopenCascadeClosedEdges(this.db, r.id, Number(r.closedAt));
|
|
28772
|
+
collapseLiveHeads(this.db, revived.map((r) => r.lid), since);
|
|
28773
|
+
this.mutations++;
|
|
28774
|
+
return revived.length;
|
|
28775
|
+
}
|
|
28776
|
+
incomingEdgeCoverage(label, edgeTypes, cloudOnly) {
|
|
28777
|
+
if (edgeTypes.length === 0) return { count: 0, total: 0 };
|
|
28778
|
+
const r = this.db.prepare(
|
|
28779
|
+
`SELECT COUNT(*) AS total,
|
|
28780
|
+
COALESCE(SUM(CASE WHEN EXISTS (
|
|
28781
|
+
SELECT 1 FROM edges e WHERE e.to_id = n.id AND e.valid_to IS NULL
|
|
28782
|
+
AND e.type IN (${edgeTypes.map(() => "?").join(",")})
|
|
28783
|
+
) THEN 1 ELSE 0 END), 0) AS count
|
|
28784
|
+
FROM nodes n
|
|
28785
|
+
WHERE n.label = ? AND n.valid_to IS NULL
|
|
28786
|
+
AND (? = 0 OR json_extract(n.attrs_json, '$.source') = 'cloud')`
|
|
28787
|
+
).get(...edgeTypes, label, cloudOnly ? 1 : 0);
|
|
28788
|
+
return { count: Number(r.count), total: Number(r.total) };
|
|
28789
|
+
}
|
|
28790
|
+
liveEdgeTargets(edgeTypes) {
|
|
28791
|
+
if (edgeTypes.length === 0) return [];
|
|
28792
|
+
const rows = this.db.prepare(`SELECT DISTINCT to_id FROM edges INDEXED BY edges_type WHERE type IN (${edgeTypes.map(() => "?").join(",")}) AND valid_to IS NULL`).all(...edgeTypes);
|
|
28793
|
+
return rows.map((r) => r.to_id);
|
|
27155
28794
|
}
|
|
27156
28795
|
recentEdgeAttrCoverage(marker, attr, limit) {
|
|
28796
|
+
if (marker === PRIOR_TAG_MARKER) {
|
|
28797
|
+
const r2 = this.db.prepare(
|
|
28798
|
+
`SELECT COUNT(*) AS total,
|
|
28799
|
+
COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
|
|
28800
|
+
FROM (SELECT attrs_json FROM edges INDEXED BY edges_live_prior_tag
|
|
28801
|
+
WHERE valid_to IS NULL AND json_extract(attrs_json, '$.priorTag') = 1
|
|
28802
|
+
ORDER BY created_at DESC LIMIT ?)`
|
|
28803
|
+
).get(attr, limit);
|
|
28804
|
+
return { count: Number(r2.count), total: Number(r2.total) };
|
|
28805
|
+
}
|
|
27157
28806
|
const r = this.db.prepare(
|
|
27158
28807
|
`SELECT COUNT(*) AS total,
|
|
27159
28808
|
COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
|
|
27160
|
-
FROM (SELECT attrs_json FROM edges
|
|
28809
|
+
FROM (SELECT attrs_json FROM edges INDEXED BY edges_live_created
|
|
27161
28810
|
WHERE valid_to IS NULL AND attrs_json LIKE ?
|
|
27162
28811
|
ORDER BY created_at DESC LIMIT ?)`
|
|
27163
28812
|
).get(attr, `%${marker}%`, limit);
|
|
@@ -27170,7 +28819,7 @@ var SqliteGraphStore = class {
|
|
|
27170
28819
|
const slice = cloudIds.slice(i2, i2 + CHUNK);
|
|
27171
28820
|
if (slice.length === 0) continue;
|
|
27172
28821
|
const rows = this.db.prepare(
|
|
27173
|
-
`SELECT id, json_extract(attrs_json, '$.cloudNodeId') AS cloud FROM nodes INDEXED BY
|
|
28822
|
+
`SELECT id, json_extract(attrs_json, '$.cloudNodeId') AS cloud FROM nodes INDEXED BY nodes_cloud_node_id_v2 WHERE json_extract(attrs_json, '$.cloudNodeId') IN (${slice.map(() => "?").join(",")}) AND valid_to IS NULL ORDER BY id`
|
|
27174
28823
|
).all(...slice);
|
|
27175
28824
|
for (const r of rows) out2.set(r.cloud, [...out2.get(r.cloud) ?? [], r.id]);
|
|
27176
28825
|
}
|
|
@@ -27283,8 +28932,9 @@ var SqliteGraphStore = class {
|
|
|
27283
28932
|
this.stmts.advanceLive.run({ live_id: liveId, t });
|
|
27284
28933
|
return frozenId;
|
|
27285
28934
|
}
|
|
27286
|
-
closeNode(id, t) {
|
|
27287
|
-
this.stmts.closeNode.run({ id, t });
|
|
28935
|
+
closeNode(id, t, opts = {}) {
|
|
28936
|
+
const r = this.stmts.closeNode.run({ id, t });
|
|
28937
|
+
if (Number(r.changes) > 0 && !opts.retainEdges) closeIncidentEdges(this.db, id, t);
|
|
27288
28938
|
}
|
|
27289
28939
|
closeEdge(id, t) {
|
|
27290
28940
|
this.stmts.closeEdge.run({ id, t });
|
|
@@ -27304,9 +28954,11 @@ var SqliteGraphStore = class {
|
|
|
27304
28954
|
* so it must be a plain identifier. */
|
|
27305
28955
|
nodesAsOfWhereTrue(t, attr) {
|
|
27306
28956
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attr)) throw new Error(`nodesAsOfWhereTrue: bad attr ${JSON.stringify(attr)}`);
|
|
28957
|
+
const indexed = attr === "revisit" ? " INDEXED BY nodes_revisit_true" : "";
|
|
27307
28958
|
const rows = this.db.prepare(
|
|
27308
|
-
`SELECT * FROM nodes WHERE valid_from <= :t AND (valid_to IS NULL OR valid_to > :t)
|
|
27309
|
-
AND json_type(attrs_json, '$.${attr}') = 'true'
|
|
28959
|
+
`SELECT * FROM nodes${indexed} WHERE valid_from <= :t AND (valid_to IS NULL OR valid_to > :t)
|
|
28960
|
+
AND json_type(attrs_json, '$.${attr}') = 'true'
|
|
28961
|
+
ORDER BY rowid`
|
|
27310
28962
|
).all({ t });
|
|
27311
28963
|
return rows.map(rowToNode);
|
|
27312
28964
|
}
|
|
@@ -27390,19 +29042,16 @@ function conditionOf(node) {
|
|
|
27390
29042
|
}
|
|
27391
29043
|
function clearAnsweredRevisits(store2, ts) {
|
|
27392
29044
|
const report = { cleared: 0, standing: 0 };
|
|
27393
|
-
|
|
27394
|
-
|
|
27395
|
-
|
|
27396
|
-
|
|
27397
|
-
|
|
27398
|
-
|
|
27399
|
-
|
|
27400
|
-
report.standing++;
|
|
27401
|
-
continue;
|
|
27402
|
-
}
|
|
27403
|
-
clearRevisit(store2, node.id, ts);
|
|
27404
|
-
report.cleared++;
|
|
29045
|
+
const semantic = new Set(SEMANTIC_NODE_LABELS);
|
|
29046
|
+
for (const node of store2.nodesAsOfWhereTrue(ts, "revisit")) {
|
|
29047
|
+
if (!semantic.has(node.label) || node.validTo != null) continue;
|
|
29048
|
+
const condition = conditionOf(node);
|
|
29049
|
+
if (condition === void 0 || !isRevisitAnswered(store2, node, condition)) {
|
|
29050
|
+
report.standing++;
|
|
29051
|
+
continue;
|
|
27405
29052
|
}
|
|
29053
|
+
clearRevisit(store2, node.id, ts);
|
|
29054
|
+
report.cleared++;
|
|
27406
29055
|
}
|
|
27407
29056
|
return report;
|
|
27408
29057
|
}
|
|
@@ -27566,7 +29215,7 @@ var NAME_SEPARATOR = /[@/\-._]/;
|
|
|
27566
29215
|
var MIN_NAME_LEN = 3;
|
|
27567
29216
|
function buildPackageIndex(store2) {
|
|
27568
29217
|
const idx = /* @__PURE__ */ new Map();
|
|
27569
|
-
for (const n of store2.
|
|
29218
|
+
for (const n of store2.selectNodes({ labels: ["Package"], attrs: ["name", "resolved"] })) {
|
|
27570
29219
|
const name2 = String(n.attrs["name"] ?? "").trim();
|
|
27571
29220
|
if (!name2) continue;
|
|
27572
29221
|
const key = name2.toLowerCase();
|
|
@@ -27633,7 +29282,7 @@ function backfillProblemPackageLinks(store2, ts) {
|
|
|
27633
29282
|
const report = { problems: 0, edges: 0 };
|
|
27634
29283
|
const idx = buildPackageIndex(store2);
|
|
27635
29284
|
if (idx.size === 0) return report;
|
|
27636
|
-
for (const p of store2.
|
|
29285
|
+
for (const p of store2.selectNodes({ labels: ["Problem"], attrs: ["resolvedAs"] })) {
|
|
27637
29286
|
if (p.attrs["resolvedAs"] === "false_positive") continue;
|
|
27638
29287
|
const { linked } = linkProblemToPackages(store2, p.id, problemText(store2, p), ts, idx);
|
|
27639
29288
|
if (linked.length > 0) {
|
|
@@ -27673,7 +29322,7 @@ function deriveContextFromAnchors(store2, problemId, ts, pkgIndex) {
|
|
|
27673
29322
|
if (!p || p.label !== "Problem" && p.label !== "Vulnerability") return report;
|
|
27674
29323
|
const files = store2.outEdges(problemId, ["ANCHORED_AT"]).map((e) => store2.getNode(e.to)).filter((n) => !!n && n.label === "File");
|
|
27675
29324
|
if (files.length === 0) return report;
|
|
27676
|
-
|
|
29325
|
+
let idx = pkgIndex;
|
|
27677
29326
|
const haveLang = new Set(store2.outEdges(problemId, ["OCCURS_IN"]).map((e) => e.to));
|
|
27678
29327
|
const havePkg = new Set(store2.outEdges(problemId, ["DEPENDS_ON"]).map((e) => e.to));
|
|
27679
29328
|
for (const file2 of files) {
|
|
@@ -27712,7 +29361,7 @@ function deriveContextFromAnchors(store2, problemId, ts, pkgIndex) {
|
|
|
27712
29361
|
if (!mod) continue;
|
|
27713
29362
|
const name2 = specToPackageName(mod.description);
|
|
27714
29363
|
if (!name2) continue;
|
|
27715
|
-
const pkg = idx.get(name2);
|
|
29364
|
+
const pkg = (idx ??= buildPackageIndex(store2)).get(name2);
|
|
27716
29365
|
if (!pkg || havePkg.has(pkg.id)) continue;
|
|
27717
29366
|
addDependency(store2, {
|
|
27718
29367
|
fromId: problemId,
|
|
@@ -27731,7 +29380,7 @@ function deriveContextFromAnchors(store2, problemId, ts, pkgIndex) {
|
|
|
27731
29380
|
function backfillProblemContext(store2, ts) {
|
|
27732
29381
|
const report = { problems: 0, edges: 0 };
|
|
27733
29382
|
const idx = buildPackageIndex(store2);
|
|
27734
|
-
for (const p of store2.
|
|
29383
|
+
for (const p of store2.selectNodes({ labels: ["Problem"], attrs: ["resolvedAs"] })) {
|
|
27735
29384
|
if (p.attrs["resolvedAs"] === "false_positive") continue;
|
|
27736
29385
|
const r = deriveContextFromAnchors(store2, p.id, ts, idx);
|
|
27737
29386
|
const n = r.languages.length + r.packages.length;
|
|
@@ -27757,6 +29406,11 @@ var REDIRECT_EDGES = [
|
|
|
27757
29406
|
"MANIFESTED_IN",
|
|
27758
29407
|
"EVIDENCED_BY"
|
|
27759
29408
|
];
|
|
29409
|
+
var OPEN_PROBLEM_WHERE = [
|
|
29410
|
+
{ attr: "resolvedAt", nullish: true },
|
|
29411
|
+
{ attr: "mergedInto", absent: true }
|
|
29412
|
+
];
|
|
29413
|
+
var OPEN_PROBLEM_KEYS = ["kind", "corroborations"];
|
|
27760
29414
|
function corroborations(n) {
|
|
27761
29415
|
return Number(n.attrs["corroborations"] ?? 0);
|
|
27762
29416
|
}
|
|
@@ -27770,7 +29424,7 @@ function mergeDuplicateProblems(store2, opts) {
|
|
|
27770
29424
|
const minOverlap = opts.minOverlap ?? 0.85;
|
|
27771
29425
|
const minTokens = opts.minTokens ?? 4;
|
|
27772
29426
|
const report = { clusters: 0, merged: 0 };
|
|
27773
|
-
const open = store2.
|
|
29427
|
+
const open = store2.selectNodes({ labels: ["Problem"], attrs: OPEN_PROBLEM_KEYS, where: OPEN_PROBLEM_WHERE });
|
|
27774
29428
|
const tokens = /* @__PURE__ */ new Map();
|
|
27775
29429
|
for (const p of open) tokens.set(p.id, new Set(retrievalTokens(p.description)));
|
|
27776
29430
|
const consumed = /* @__PURE__ */ new Set();
|
|
@@ -27794,8 +29448,9 @@ function mergeDuplicateProblems(store2, opts) {
|
|
|
27794
29448
|
if (cluster.length < 2) continue;
|
|
27795
29449
|
report.clusters++;
|
|
27796
29450
|
cluster.sort((x, y) => corroborations(y) - corroborations(x) || x.createdAt - y.createdAt);
|
|
27797
|
-
const
|
|
27798
|
-
|
|
29451
|
+
const members = cluster.map((m) => store2.getNode(m.id)).filter((n) => n !== null);
|
|
29452
|
+
const survivor = members[0];
|
|
29453
|
+
for (const dup of members.slice(1)) {
|
|
27799
29454
|
foldDuplicateProblem(store2, dup, survivor, opts.ts);
|
|
27800
29455
|
report.merged++;
|
|
27801
29456
|
}
|
|
@@ -27808,16 +29463,19 @@ function foldDuplicateProblem(store2, dup, survivor, ts) {
|
|
|
27808
29463
|
if (!surv) return;
|
|
27809
29464
|
const sources = new Set(surv.attrs["sources"] ?? []);
|
|
27810
29465
|
for (const s of dup.attrs["sources"] ?? []) sources.add(s);
|
|
27811
|
-
const
|
|
27812
|
-
const
|
|
27813
|
-
const
|
|
29466
|
+
const samePass = dup.createdAt === surv.createdAt;
|
|
29467
|
+
const [held, restated] = dup.createdAt < surv.createdAt ? [dup, surv] : [surv, dup];
|
|
29468
|
+
const at = {
|
|
29469
|
+
seq: restated.createdAtSeq ?? restated.attrs["createdAtSeq"] ?? store2.currentIngestSeq(),
|
|
29470
|
+
ts: restated.createdAt
|
|
29471
|
+
};
|
|
29472
|
+
const exposure = samePass ? null : priorExposure(store2, held.id, at);
|
|
27814
29473
|
store2.updateNode(survivor.id, {
|
|
27815
29474
|
attrs: {
|
|
27816
29475
|
...surv.attrs,
|
|
27817
29476
|
sources: [...sources],
|
|
27818
29477
|
corroborations: corroborations(surv) + corroborations(dup) + 1,
|
|
27819
|
-
|
|
27820
|
-
lastReinforceExposure: exposure
|
|
29478
|
+
...exposure ? exposureOutcomeAttrs(surv, exposure, at) : {}
|
|
27821
29479
|
},
|
|
27822
29480
|
cumulativeHits: (surv.cumulativeHits ?? 0) + (dup.cumulativeHits ?? 0),
|
|
27823
29481
|
lastUpdatedAt: ts
|
|
@@ -27848,11 +29506,21 @@ function isConstraintProblem(node) {
|
|
|
27848
29506
|
return node.attrs["kind"] === "constraint";
|
|
27849
29507
|
}
|
|
27850
29508
|
var RECURRENCE_DEBOUNCE_MS = 60 * 60 * 1e3;
|
|
27851
|
-
function resolveFileNode(store2,
|
|
27852
|
-
const want =
|
|
27853
|
-
|
|
27854
|
-
|
|
27855
|
-
|
|
29509
|
+
function resolveFileNode(store2, path2, workspaceId) {
|
|
29510
|
+
const want = path2.trim().replace(/^\.?\//, "");
|
|
29511
|
+
const id = firstFileByPath(store2, want, (a) => !(a["workspaceId"] && a["workspaceId"] !== workspaceId));
|
|
29512
|
+
return id ? store2.getNode(id) : null;
|
|
29513
|
+
}
|
|
29514
|
+
function firstFileByPath(store2, want, accept) {
|
|
29515
|
+
const byPath = store2.selectNodes({ labels: ["File"], attrs: ["workspaceId"], where: [{ attr: "relPath", eq: want }] }).find((r) => accept(r.attrs))?.id;
|
|
29516
|
+
const described = store2.liveNodesByDescription([want], ["File"]).filter((n) => accept(n.attrs));
|
|
29517
|
+
const byDescription = described.length === 1 ? described[0].id : void 0;
|
|
29518
|
+
if (described.length <= 1 && (byPath === void 0 || byDescription === void 0 || byPath === byDescription)) {
|
|
29519
|
+
return byPath ?? byDescription ?? null;
|
|
29520
|
+
}
|
|
29521
|
+
for (const r of store2.selectNodes({ labels: ["File"], attrs: ["workspaceId", "relPath"] })) {
|
|
29522
|
+
if (!accept(r.attrs)) continue;
|
|
29523
|
+
if (r.attrs["relPath"] === want || r.description === want) return r.id;
|
|
27856
29524
|
}
|
|
27857
29525
|
return null;
|
|
27858
29526
|
}
|
|
@@ -27868,13 +29536,8 @@ function isAutoMinted(n) {
|
|
|
27868
29536
|
}
|
|
27869
29537
|
function priorsForFile(store2, relPath) {
|
|
27870
29538
|
const want = relPath.trim().replace(/^\.?\//, "");
|
|
27871
|
-
|
|
27872
|
-
|
|
27873
|
-
if (n.description === want || n.attrs["relPath"] === want) {
|
|
27874
|
-
file2 = n;
|
|
27875
|
-
break;
|
|
27876
|
-
}
|
|
27877
|
-
}
|
|
29539
|
+
const fileId = firstFileByPath(store2, want, () => true);
|
|
29540
|
+
const file2 = fileId ? store2.getNode(fileId) : null;
|
|
27878
29541
|
if (!file2) return null;
|
|
27879
29542
|
const openProblems = [];
|
|
27880
29543
|
const constraints = [];
|
|
@@ -28064,10 +29727,10 @@ function openAnchorHint(p) {
|
|
|
28064
29727
|
if (!h || typeof h.path !== "string" || h.upgradedAt) return null;
|
|
28065
29728
|
return { path: h.path, ts: typeof h.ts === "number" ? h.ts : 0 };
|
|
28066
29729
|
}
|
|
28067
|
-
function consumeAnchorHint(store2, p, ts) {
|
|
29730
|
+
function consumeAnchorHint(store2, p, ts, pkgIndex) {
|
|
28068
29731
|
const hint = p.attrs["anchorHint"];
|
|
28069
29732
|
store2.updateNode(p.id, { attrs: { ...p.attrs, anchorHint: { ...hint, upgradedAt: ts } } });
|
|
28070
|
-
deriveContextFromAnchors(store2, p.id, ts);
|
|
29733
|
+
deriveContextFromAnchors(store2, p.id, ts, pkgIndex);
|
|
28071
29734
|
}
|
|
28072
29735
|
function upgradeAnchorHints(store2, workspaceId, ts) {
|
|
28073
29736
|
let upgraded = 0;
|
|
@@ -28075,14 +29738,21 @@ function upgradeAnchorHints(store2, workspaceId, ts) {
|
|
|
28075
29738
|
let unresolvable = 0;
|
|
28076
29739
|
let blocked = 0;
|
|
28077
29740
|
const filesByPath = /* @__PURE__ */ new Map();
|
|
28078
|
-
for (const f of store2.
|
|
29741
|
+
for (const f of store2.selectNodes({ labels: ["File"], attrs: ["workspaceId", "relPath", "contentChangedAt"] })) {
|
|
28079
29742
|
if (f.attrs["workspaceId"] && f.attrs["workspaceId"] !== workspaceId) continue;
|
|
28080
29743
|
const rel = typeof f.attrs["relPath"] === "string" ? f.attrs["relPath"] : f.description;
|
|
28081
29744
|
if (rel && !filesByPath.has(rel)) filesByPath.set(rel, f);
|
|
28082
29745
|
}
|
|
28083
|
-
|
|
28084
|
-
|
|
28085
|
-
|
|
29746
|
+
let pkgIndex;
|
|
29747
|
+
const hinted = store2.selectNodes({
|
|
29748
|
+
labels: ["Problem"],
|
|
29749
|
+
attrs: ["resolvedAt", "anchorHint"],
|
|
29750
|
+
where: [{ attr: "anchorHint", notNullish: true }],
|
|
29751
|
+
indexedBy: "nodes_live_anchor_hint"
|
|
29752
|
+
});
|
|
29753
|
+
for (const row of hinted) {
|
|
29754
|
+
if (row.attrs["resolvedAt"]) continue;
|
|
29755
|
+
const hint = openAnchorHint(row);
|
|
28086
29756
|
if (!hint) continue;
|
|
28087
29757
|
const file2 = filesByPath.get(hint.path.trim().replace(/^\.?\//, ""));
|
|
28088
29758
|
if (!file2 || !isAnchorableCodePath(hint.path)) {
|
|
@@ -28092,8 +29762,10 @@ function upgradeAnchorHints(store2, workspaceId, ts) {
|
|
|
28092
29762
|
standing++;
|
|
28093
29763
|
const changedAt = Number(file2.attrs["contentChangedAt"] ?? 0);
|
|
28094
29764
|
if (changedAt <= hint.ts) continue;
|
|
29765
|
+
const p = store2.getNode(row.id);
|
|
29766
|
+
if (!p) continue;
|
|
28095
29767
|
if (anchorProblemToWorkingFile(store2, p.id, hint.path, workspaceId, ts, "hint-confirmed")) {
|
|
28096
|
-
consumeAnchorHint(store2, p, ts);
|
|
29768
|
+
consumeAnchorHint(store2, p, ts, pkgIndex ??= buildPackageIndex(store2));
|
|
28097
29769
|
upgraded++;
|
|
28098
29770
|
standing--;
|
|
28099
29771
|
} else {
|
|
@@ -28104,7 +29776,13 @@ function upgradeAnchorHints(store2, workspaceId, ts) {
|
|
|
28104
29776
|
}
|
|
28105
29777
|
function reopenAutoClosedProblems(store2, t) {
|
|
28106
29778
|
const report = { reopened: 0, alreadySuspect: 0, agentDescribed: 0 };
|
|
28107
|
-
|
|
29779
|
+
const closed = store2.selectNodes({
|
|
29780
|
+
labels: ["Problem"],
|
|
29781
|
+
attrs: ["resolvedAt", "resolvedAs", "mergedInto", "resolutionWitnessed", "retiredEditPlaceholder", "resolutionSuspect"],
|
|
29782
|
+
where: [{ attr: "resolvedAt", notNullish: true }, { attr: "resolvedAs", nullish: true }, { attr: "mergedInto", absent: true }],
|
|
29783
|
+
indexedBy: "nodes_live_resolved"
|
|
29784
|
+
});
|
|
29785
|
+
for (const p of closed) {
|
|
28108
29786
|
if (p.attrs["resolvedAt"] == null) continue;
|
|
28109
29787
|
if (p.attrs["resolvedAs"] != null) continue;
|
|
28110
29788
|
if (p.attrs["mergedInto"] !== void 0) continue;
|
|
@@ -28119,7 +29797,9 @@ function reopenAutoClosedProblems(store2, t) {
|
|
|
28119
29797
|
continue;
|
|
28120
29798
|
}
|
|
28121
29799
|
if (p.attrs["resolutionSuspect"] === true) report.alreadySuspect++;
|
|
28122
|
-
const
|
|
29800
|
+
const node = store2.getNode(p.id);
|
|
29801
|
+
if (!node) continue;
|
|
29802
|
+
const attrs = { ...node.attrs };
|
|
28123
29803
|
delete attrs["resolvedAt"];
|
|
28124
29804
|
attrs["reopenedFrom"] = "auto-close";
|
|
28125
29805
|
attrs["reopenedAt"] = t;
|
|
@@ -28130,7 +29810,12 @@ function reopenAutoClosedProblems(store2, t) {
|
|
|
28130
29810
|
}
|
|
28131
29811
|
function markFixCandidates(store2, t) {
|
|
28132
29812
|
let resolved = 0;
|
|
28133
|
-
|
|
29813
|
+
const candidates = store2.selectNodes({
|
|
29814
|
+
labels: ["Problem"],
|
|
29815
|
+
attrs: ["resolvedAt", "kind", "fixCandidateClearedAt", "fixCandidateAt"],
|
|
29816
|
+
where: [{ idPrefix: "dprob_" }]
|
|
29817
|
+
});
|
|
29818
|
+
for (const p of candidates) {
|
|
28134
29819
|
if (!p.id.startsWith("dprob_")) continue;
|
|
28135
29820
|
if (p.attrs["resolvedAt"]) continue;
|
|
28136
29821
|
if (isConstraintProblem(p)) continue;
|
|
@@ -28157,9 +29842,11 @@ function markFixCandidates(store2, t) {
|
|
|
28157
29842
|
return s !== null && !String(s.description ?? "").startsWith(AUTO_MINT_PREFIX);
|
|
28158
29843
|
}))
|
|
28159
29844
|
continue;
|
|
29845
|
+
const node = store2.getNode(p.id);
|
|
29846
|
+
if (!node) continue;
|
|
28160
29847
|
store2.updateNode(p.id, {
|
|
28161
29848
|
attrs: {
|
|
28162
|
-
...
|
|
29849
|
+
...node.attrs,
|
|
28163
29850
|
// The cue, kept as structured data so the render can name the file it is
|
|
28164
29851
|
// asking about. Deliberately NOT `resolvedAt` — this is a question.
|
|
28165
29852
|
fixCandidateAt: t,
|
|
@@ -28169,7 +29856,7 @@ function markFixCandidates(store2, t) {
|
|
|
28169
29856
|
// measurable. `shownCount` is the node's LIFETIME count across every
|
|
28170
29857
|
// band, so comparing it raw would retire a fresh ask on a
|
|
28171
29858
|
// frequently-surfaced problem before anyone had seen the question once.
|
|
28172
|
-
fixCandidateShownBase: Number(
|
|
29859
|
+
fixCandidateShownBase: Number(node.attrs["shownCount"] ?? 0)
|
|
28173
29860
|
}
|
|
28174
29861
|
// No `lastUpdatedAt`: raising a question is bookkeeping, not a new
|
|
28175
29862
|
// observation of the problem, and the passive block's recent-problems band
|
|
@@ -28182,7 +29869,10 @@ function markFixCandidates(store2, t) {
|
|
|
28182
29869
|
var FIX_CANDIDATE_ASK_LIMIT = 5;
|
|
28183
29870
|
function clearSettledFixCandidates(store2, t) {
|
|
28184
29871
|
const report = { answered: 0, ignored: 0, unsubstantiated: 0, standing: 0 };
|
|
28185
|
-
|
|
29872
|
+
const asked = store2.selectNodes({ labels: ["Problem"], where: [{ attr: "fixCandidateAt", present: true }], indexedBy: "nodes_live_fix_candidate" });
|
|
29873
|
+
for (const row of asked) {
|
|
29874
|
+
const p = store2.getNode(row.id);
|
|
29875
|
+
if (!p) continue;
|
|
28186
29876
|
if (p.attrs["fixCandidateAt"] === void 0) continue;
|
|
28187
29877
|
const fileAnchors = store2.outEdges(p.id, ["ANCHORED_AT"]).filter((e) => e.attrs?.["mention"] !== true && e.attrs?.["anchorProvenance"] !== "legacy").map((e) => store2.getNode(e.to)).filter((n) => n !== null && n.label === "File");
|
|
28188
29878
|
const unsubstantiated = fileAnchors.length > 0 && fileAnchors.every((f) => f.attrs["contentChangedAt"] === void 0);
|
|
@@ -28211,16 +29901,77 @@ function clearSettledFixCandidates(store2, t) {
|
|
|
28211
29901
|
}
|
|
28212
29902
|
return report;
|
|
28213
29903
|
}
|
|
28214
|
-
function
|
|
29904
|
+
function exposureLog(attrs) {
|
|
29905
|
+
const raw = attrs["exposureLog"];
|
|
29906
|
+
if (!Array.isArray(raw)) return [];
|
|
29907
|
+
return raw.filter(
|
|
29908
|
+
(e) => Array.isArray(e) && typeof e[0] === "number" && (e[1] === "r" || e[1] === "c" || e[1] === "e")
|
|
29909
|
+
);
|
|
29910
|
+
}
|
|
29911
|
+
function priorExposure(store2, nodeId, at) {
|
|
28215
29912
|
const n = store2.getNode(nodeId);
|
|
28216
29913
|
if (!n || !n.attrs) return "unshown";
|
|
28217
|
-
const
|
|
28218
|
-
|
|
28219
|
-
const
|
|
28220
|
-
|
|
28221
|
-
|
|
29914
|
+
const a = n.attrs;
|
|
29915
|
+
const num2 = (k) => typeof a[k] === "number" ? a[k] : void 0;
|
|
29916
|
+
const since = num2("lastOutcomeAt");
|
|
29917
|
+
const outOfOrder = since !== void 0 && at.ts <= since;
|
|
29918
|
+
const lo = outOfOrder || since === void 0 ? -Infinity : since;
|
|
29919
|
+
const kinds = ["r", "c", "e"];
|
|
29920
|
+
const seen = { r: false, c: false, e: false };
|
|
29921
|
+
const maybe = { r: outOfOrder, c: outOfOrder, e: outOfOrder };
|
|
29922
|
+
for (const [t, kind] of exposureLog(a)) if (t > lo && t <= at.ts) seen[kind] = true;
|
|
29923
|
+
const logSince = num2("exposureLogSince");
|
|
29924
|
+
const trimmed = a["exposureTrimmed"] ?? {};
|
|
29925
|
+
if (logSince !== void 0 && lo < logSince) {
|
|
29926
|
+
for (const k of kinds) {
|
|
29927
|
+
const t = trimmed[k];
|
|
29928
|
+
if (typeof t !== "number" || t <= lo) continue;
|
|
29929
|
+
if (t <= at.ts) seen[k] = true;
|
|
29930
|
+
else maybe[k] = true;
|
|
29931
|
+
}
|
|
29932
|
+
}
|
|
29933
|
+
const firstShownAt = num2("firstShownAt");
|
|
29934
|
+
const firstEvictedAt = num2("firstEvictedAt");
|
|
29935
|
+
const legacyShowSeq = num2("firstShownAtSeq") ?? num2("lastShownAtSeq");
|
|
29936
|
+
const legacyShown = firstShownAt === 0 || firstShownAt === void 0 && legacyShowSeq !== void 0;
|
|
29937
|
+
const legacyEvicted = firstEvictedAt === 0 || firstEvictedAt === void 0 && (num2("lastEvictedAtSeq") !== void 0 || (num2("evictedCount") ?? 0) > 0);
|
|
29938
|
+
const logStart = num2("exposureLogStart");
|
|
29939
|
+
const beforeLog = logStart === void 0 || lo < logStart;
|
|
29940
|
+
if (beforeLog && legacyShown) maybe.r = true;
|
|
29941
|
+
if (beforeLog && legacyEvicted) maybe.e = true;
|
|
29942
|
+
const first = a["exposureFirst"] ?? {};
|
|
29943
|
+
const legacyBy = (seq) => logStart !== void 0 && at.ts >= logStart ? true : seq !== void 0 && seq <= at.seq;
|
|
29944
|
+
const happenedBy = {
|
|
29945
|
+
r: typeof first["r"] === "number" && first["r"] <= at.ts || legacyShown && legacyBy(legacyShowSeq),
|
|
29946
|
+
c: typeof first["c"] === "number" && first["c"] <= at.ts,
|
|
29947
|
+
e: typeof first["e"] === "number" && first["e"] <= at.ts || legacyEvicted && (legacyBy(num2("lastEvictedAtSeq")) || a["lastEvictedAtSeq"] === void 0 && (num2("evictedCount") ?? 0) > 0)
|
|
29948
|
+
};
|
|
29949
|
+
const present = (k) => seen[k] || maybe[k] && happenedBy[k];
|
|
29950
|
+
if (present("r")) return "shown";
|
|
29951
|
+
if (present("c")) return "recent";
|
|
29952
|
+
if (present("e")) return "evicted";
|
|
28222
29953
|
return "unshown";
|
|
28223
29954
|
}
|
|
29955
|
+
function passivelyShowable(n) {
|
|
29956
|
+
const quality = n.attrs["historyQuality"];
|
|
29957
|
+
const evaluatedAt = quality && typeof quality === "object" ? quality["evaluatedAtCommit"] : void 0;
|
|
29958
|
+
return historyReadPolicy(n, {
|
|
29959
|
+
mode: "passive",
|
|
29960
|
+
atCommit: typeof evaluatedAt === "string" ? evaluatedAt : void 0
|
|
29961
|
+
}).allow;
|
|
29962
|
+
}
|
|
29963
|
+
function exposureOutcomeAttrs(node, exposure, at) {
|
|
29964
|
+
const attrs = node.attrs;
|
|
29965
|
+
const field = `reinforce${exposure[0].toUpperCase()}${exposure.slice(1)}Count`;
|
|
29966
|
+
const prev = attrs["lastOutcomeAt"];
|
|
29967
|
+
const queryOnly = exposure === "unshown" && !passivelyShowable(node);
|
|
29968
|
+
return {
|
|
29969
|
+
[field]: (attrs[field] ?? 0) + 1,
|
|
29970
|
+
...queryOnly ? { reinforceUnshownQueryOnlyCount: (attrs["reinforceUnshownQueryOnlyCount"] ?? 0) + 1 } : {},
|
|
29971
|
+
lastReinforceExposure: exposure,
|
|
29972
|
+
lastOutcomeAt: typeof prev === "number" ? Math.max(prev, at.ts) : at.ts
|
|
29973
|
+
};
|
|
29974
|
+
}
|
|
28224
29975
|
|
|
28225
29976
|
// ../../packages/local-graph/src/intent.ts
|
|
28226
29977
|
init_src();
|
|
@@ -28237,9 +29988,9 @@ var STATEMENT_PATH_RE = new RegExp(
|
|
|
28237
29988
|
);
|
|
28238
29989
|
function filePathResolver(store2) {
|
|
28239
29990
|
const byPath = /* @__PURE__ */ new Map();
|
|
28240
|
-
for (const f of store2.
|
|
28241
|
-
const
|
|
28242
|
-
if (
|
|
29991
|
+
for (const f of store2.selectNodes({ labels: ["File"], attrs: ["relPath"] })) {
|
|
29992
|
+
const path2 = String(f.attrs["relPath"] ?? f.description ?? "");
|
|
29993
|
+
if (path2) byPath.set(path2, { id: f.id, path: path2 });
|
|
28243
29994
|
}
|
|
28244
29995
|
return (named) => {
|
|
28245
29996
|
const exact = byPath.get(named);
|
|
@@ -28283,7 +30034,7 @@ function repairLegacyAnchorsFromStatement(store2, ts) {
|
|
|
28283
30034
|
alreadyCorrect: 0
|
|
28284
30035
|
};
|
|
28285
30036
|
const resolvePath = filePathResolver(store2);
|
|
28286
|
-
for (const p of store2.
|
|
30037
|
+
for (const p of store2.selectNodes({ labels: ["Problem"] })) {
|
|
28287
30038
|
try {
|
|
28288
30039
|
const anchors = store2.outEdges(p.id, ["ANCHORED_AT"]);
|
|
28289
30040
|
const legacy = anchors.filter((e) => e.attrs?.["anchorProvenance"] === "legacy");
|
|
@@ -28347,10 +30098,12 @@ function backfillLegacyAnchors(store2, ts) {
|
|
|
28347
30098
|
suspects: []
|
|
28348
30099
|
};
|
|
28349
30100
|
let resolvePath;
|
|
28350
|
-
for (const
|
|
28351
|
-
const resolved =
|
|
28352
|
-
const legacy = store2.outEdges(
|
|
30101
|
+
for (const row of store2.selectNodes({ labels: ["Problem"], attrs: ["resolvedAt"] })) {
|
|
30102
|
+
const resolved = row.attrs["resolvedAt"] !== void 0;
|
|
30103
|
+
const legacy = store2.outEdges(row.id, ["ANCHORED_AT"]).filter((e) => isLegacyAnchor(e.attrs) || !resolved && e.attrs?.["anchorProvenance"] === "legacy");
|
|
28353
30104
|
if (legacy.length === 0) continue;
|
|
30105
|
+
const p = store2.getNode(row.id);
|
|
30106
|
+
if (!p) continue;
|
|
28354
30107
|
try {
|
|
28355
30108
|
if (!resolved) {
|
|
28356
30109
|
resolvePath ??= filePathResolver(store2);
|
|
@@ -28365,8 +30118,8 @@ function backfillLegacyAnchors(store2, ts) {
|
|
|
28365
30118
|
const retired = legacy.filter((e) => !(target && e.to === target.id));
|
|
28366
30119
|
for (const e of retired) {
|
|
28367
30120
|
const file2 = store2.getNode(e.to);
|
|
28368
|
-
const
|
|
28369
|
-
if (
|
|
30121
|
+
const path2 = String(file2?.attrs["relPath"] ?? file2?.description ?? "");
|
|
30122
|
+
if (path2) recordAnchorHint(store2, p.id, path2, "legacy", ts);
|
|
28370
30123
|
store2.closeEdge(e.id, ts);
|
|
28371
30124
|
report.demotedEdges++;
|
|
28372
30125
|
}
|
|
@@ -28491,7 +30244,7 @@ function detectLocalCommunities(store2, opts = {}) {
|
|
|
28491
30244
|
for (const n of unowned) {
|
|
28492
30245
|
const pulled = n.attrs["source"] === "cloud";
|
|
28493
30246
|
const clearColumn = !!n.community && !pulled;
|
|
28494
|
-
if (n.attrs["localCommunity"]
|
|
30247
|
+
if (n.attrs["localCommunity"] == null && !clearColumn) continue;
|
|
28495
30248
|
store2.setLocalCommunity(n.id, null, clearColumn);
|
|
28496
30249
|
if (clearColumn) report.cleared++;
|
|
28497
30250
|
}
|
|
@@ -28559,13 +30312,13 @@ function detectLocalCommunities(store2, opts = {}) {
|
|
|
28559
30312
|
// ../../packages/local-graph/src/tools.ts
|
|
28560
30313
|
init_src();
|
|
28561
30314
|
function rankToolsForHandles(store2, limit = 5) {
|
|
28562
|
-
const scored = store2.
|
|
28563
|
-
|
|
28564
|
-
concerns: store2.inEdges(
|
|
28565
|
-
hits:
|
|
30315
|
+
const scored = store2.selectNodes({ labels: ["Tool"] }).map((row) => ({
|
|
30316
|
+
id: row.id,
|
|
30317
|
+
concerns: store2.inEdges(row.id, ["CONCERNS"]).length,
|
|
30318
|
+
hits: row.cumulativeHits ?? 0
|
|
28566
30319
|
}));
|
|
28567
30320
|
scored.sort((a, b) => b.concerns - a.concerns || b.hits - a.hits);
|
|
28568
|
-
return scored.slice(0, Math.max(0, limit)).map((s) => s.
|
|
30321
|
+
return scored.slice(0, Math.max(0, limit)).map((s) => store2.getNode(s.id)).filter((n) => n !== null);
|
|
28569
30322
|
}
|
|
28570
30323
|
|
|
28571
30324
|
// ../../packages/local-graph/src/principle-sync.ts
|
|
@@ -28621,6 +30374,7 @@ var MECHANISMS = [
|
|
|
28621
30374
|
},
|
|
28622
30375
|
{
|
|
28623
30376
|
id: "fix-candidate-ask",
|
|
30377
|
+
requiresNodes: ["Problem"],
|
|
28624
30378
|
what: "asks an agent whether an edit to a problem's anchor fixed it",
|
|
28625
30379
|
pass: "semantic-maintenance",
|
|
28626
30380
|
// markFixCandidates fails CLOSED on a File anchor with no contentChangedAt —
|
|
@@ -28672,7 +30426,12 @@ var MECHANISMS = [
|
|
|
28672
30426
|
// the producer was verifiably stamping every new node.
|
|
28673
30427
|
minFraction: 0.9,
|
|
28674
30428
|
recent: 200,
|
|
28675
|
-
sinceFirstStamp: true
|
|
30429
|
+
sinceFirstStamp: true,
|
|
30430
|
+
// The anchor-feed pull lands cloud mirrors by the hundreds every ten
|
|
30431
|
+
// minutes (createdAt = pull time, no local seq — stamping them would age
|
|
30432
|
+
// every local node by Δseq). On 2026-10-09 1,146 mirrors in a few hours
|
|
30433
|
+
// dragged this to 62% and toasted "ingest-sequence DEAD" with effect 41.
|
|
30434
|
+
localOnly: true
|
|
28676
30435
|
},
|
|
28677
30436
|
effectCounter: "seqAdvanced",
|
|
28678
30437
|
note: "column, not attr; measured over nodes born since the first stamp in the recent window"
|
|
@@ -28710,6 +30469,28 @@ var MECHANISMS = [
|
|
|
28710
30469
|
// climbs while `accepted` stays flat is the shape of a silently-closed door.
|
|
28711
30470
|
backlogCounter: "candidates"
|
|
28712
30471
|
},
|
|
30472
|
+
{
|
|
30473
|
+
id: "edge-park-replay",
|
|
30474
|
+
what: "parks causal/anchor edges the door refused as unresolvable, replays them when due, drops them with a disposition",
|
|
30475
|
+
pass: "ship",
|
|
30476
|
+
// FED = a source whose whole binding-edge family LANDED: the digest is
|
|
30477
|
+
// stamped only when every edge in it came back created/reinforced, so it is
|
|
30478
|
+
// the store-side proof that edges (not just nodes) cross. Live dev
|
|
30479
|
+
// 2026-10-12 sat with the same 241 refusals re-sent every drain and this
|
|
30480
|
+
// stamp frozen — nodes crossed, their causal wiring did not.
|
|
30481
|
+
fed: {
|
|
30482
|
+
labels: ["Problem", "RootCause"],
|
|
30483
|
+
attr: "semanticEdgesContributedDigest",
|
|
30484
|
+
minFraction: 0.01
|
|
30485
|
+
},
|
|
30486
|
+
// A parked edge that later LANDED — the replay doing its job.
|
|
30487
|
+
effectCounter: "edgesReplayLanded",
|
|
30488
|
+
// Outstanding parked edges (a level). Flat-and-nonzero while replays keep
|
|
30489
|
+
// re-parking is an endpoint that never resolves; it drains to tombstones
|
|
30490
|
+
// (`edgesDroppedExhausted`) instead of being resent forever.
|
|
30491
|
+
backlogCounter: "edgeParkSize",
|
|
30492
|
+
note: "edge-park.ts; quiet (no-effect) is healthy when nothing is refused \u2014 read edgesParked/edgesDropped* on the ship row"
|
|
30493
|
+
},
|
|
28713
30494
|
{
|
|
28714
30495
|
id: "orphan-reap",
|
|
28715
30496
|
what: "closes File nodes whose path the indexer no longer covers",
|
|
@@ -28764,12 +30545,31 @@ var MECHANISMS = [
|
|
|
28764
30545
|
// the bar is "the field is written at all", the revisit-sweep precedent.
|
|
28765
30546
|
fed: { labels: ["Problem"], attr: "foldCandidate", minFraction: 0 },
|
|
28766
30547
|
effectCounter: "folded",
|
|
28767
|
-
//
|
|
28768
|
-
//
|
|
28769
|
-
|
|
30548
|
+
// The open-nomination POOL is the backlog. It used to be `standing`, which
|
|
30549
|
+
// only counts what the pass examined — capped at the per-run limit — so it
|
|
30550
|
+
// read a flat 8 for weeks while 824 nominations sat open (2026-10-12): the
|
|
30551
|
+
// gauge could not see past the slice it was meant to be measuring.
|
|
30552
|
+
backlogCounter: "pending",
|
|
28770
30553
|
configCounter: "judgeConfigured",
|
|
28771
30554
|
note: "the ambiguous band needs the opt-in judge: set ERRATA_FOLD_JUDGE=1 + a configured chat provider (azure env), or leave it off and the nominations stand"
|
|
28772
30555
|
},
|
|
30556
|
+
{
|
|
30557
|
+
id: "fold-candidate-coverage",
|
|
30558
|
+
what: "rotates the fold-judge pass through every open nomination (never-examined first, standing ones re-examined on new evidence)",
|
|
30559
|
+
// Judge-INDEPENDENT on purpose — no configCounter. The embedding-certain
|
|
30560
|
+
// bands fold/reject with no judge at all, and the defect this watches is
|
|
30561
|
+
// the pass re-reading the same oldest 8 forever (101 runs, 8 distinct
|
|
30562
|
+
// nodes, 816 of 824 nominations never looked at). That starves those
|
|
30563
|
+
// bands on a judge-less daemon exactly as much as on a judged one, so an
|
|
30564
|
+
// opt-in being off must not excuse it.
|
|
30565
|
+
pass: "fold-judge",
|
|
30566
|
+
fed: { labels: ["Problem"], attr: "foldCandidate", minFraction: 0 },
|
|
30567
|
+
effectCounter: "firstExamined",
|
|
30568
|
+
// A LEVEL: open nominations never examined. Flat-and-positive with no
|
|
30569
|
+
// first examinations across the stall window = the rotation is stuck.
|
|
30570
|
+
backlogCounter: "neverExamined",
|
|
30571
|
+
note: "neverExamined must drain while the pass runs; flat means the selection is re-reading the same slice"
|
|
30572
|
+
},
|
|
28773
30573
|
{
|
|
28774
30574
|
id: "cite-anchor-accrual",
|
|
28775
30575
|
what: "accrues cite-context anchor evidence on anchorless cited priors; two distinct sessions promote a cite-confirmed ANCHORED_AT",
|
|
@@ -28817,6 +30617,7 @@ var MECHANISMS = [
|
|
|
28817
30617
|
},
|
|
28818
30618
|
{
|
|
28819
30619
|
id: "liveness-watch",
|
|
30620
|
+
inputOptional: true,
|
|
28820
30621
|
what: "the daemon-side schedule of this very verdict \u2014 alarms when a mechanism stops working",
|
|
28821
30622
|
// The watcher is itself a row: a probe that only runs when a human remembers
|
|
28822
30623
|
// is how the original six defects shipped, and a scheduled watch that
|
|
@@ -28831,12 +30632,46 @@ var MECHANISMS = [
|
|
|
28831
30632
|
// reading is IDLE — correct, and it declares no backlog on purpose.
|
|
28832
30633
|
effectCounter: "alarms",
|
|
28833
30634
|
note: "steady-state IDLE is healthy; the row exists for its invocation age"
|
|
30635
|
+
},
|
|
30636
|
+
{
|
|
30637
|
+
id: "project-causal-sync",
|
|
30638
|
+
what: "lands the cloud project stratum's SOLVED_BY/FIXED_BY edges onto the knowledge the anchor feed mirrors",
|
|
30639
|
+
// PRODUCER: the anchor feed's causal leg — `fetchProjectAnchors` returns the
|
|
30640
|
+
// edges, `syncProjectAnchors` (apps/daemon/src/project-reanchor.ts) lands or
|
|
30641
|
+
// parks them, on the `anchor-pull` beat. CONSUMERS: every local walk that
|
|
30642
|
+
// crosses Problem→Solution — errata_trace/why/burst over the store, and the
|
|
30643
|
+
// passive priming block that renders a problem with its fix. Before
|
|
30644
|
+
// PJ-sync-causal the feed mirrored 5,054 cloud Solutions on the dev checkout
|
|
30645
|
+
// with not one incoming SOLVED_BY: this row would have read 0.0% for months.
|
|
30646
|
+
pass: "anchor-pull",
|
|
30647
|
+
fedIncoming: { label: "Solution", edgeTypes: ["SOLVED_BY", "FIXED_BY"], cloudOnly: true, minFraction: 0.5 },
|
|
30648
|
+
effectCounter: "edgesLanded",
|
|
30649
|
+
// Parked edges waiting on an endpoint — a flat, non-zero backlog is a stratum
|
|
30650
|
+
// whose other ends never arrive here (a cap, or a gate that refuses them).
|
|
30651
|
+
backlogCounter: "edgesPending",
|
|
30652
|
+
requiresNodes: ["Problem"],
|
|
30653
|
+
note: "an orphaned cloud Solution reads as a fix with no problem; the one-time causal re-walk lifts this after the server ships the leg"
|
|
28834
30654
|
}
|
|
28835
30655
|
];
|
|
28836
30656
|
var COLUMN_INPUTS = {
|
|
28837
30657
|
__createdAtSeq: (n) => n.createdAtSeq !== void 0 && n.createdAtSeq !== null
|
|
28838
30658
|
};
|
|
28839
|
-
function evaluateFed(store2, d) {
|
|
30659
|
+
function evaluateFed(store2, d, stamps) {
|
|
30660
|
+
const incomingRows = d.fedIncoming && store2.liveEdgeTargets ? stamps?.(d.fedIncoming.label, "source") : void 0;
|
|
30661
|
+
if (d.fedIncoming && incomingRows) {
|
|
30662
|
+
const targets = new Set(store2.liveEdgeTargets(d.fedIncoming.edgeTypes));
|
|
30663
|
+
const rows = d.fedIncoming.cloudOnly ? incomingRows.filter((r) => r.cloud) : incomingRows;
|
|
30664
|
+
const count2 = rows.filter((r) => targets.has(r.id)).length;
|
|
30665
|
+
return { count: count2, total: rows.length, fraction: rows.length === 0 ? 0 : count2 / rows.length };
|
|
30666
|
+
}
|
|
30667
|
+
if (d.fedIncoming) {
|
|
30668
|
+
const { count: count2, total } = store2.incomingEdgeCoverage?.(
|
|
30669
|
+
d.fedIncoming.label,
|
|
30670
|
+
d.fedIncoming.edgeTypes,
|
|
30671
|
+
d.fedIncoming.cloudOnly
|
|
30672
|
+
) ?? { count: 0, total: 0 };
|
|
30673
|
+
return { count: count2, total, fraction: total === 0 ? 0 : count2 / total };
|
|
30674
|
+
}
|
|
28840
30675
|
if (d.fedEdges) {
|
|
28841
30676
|
const { count: count2, total } = store2.recentEdgeAttrCoverage(
|
|
28842
30677
|
d.fedEdges.marker,
|
|
@@ -28846,10 +30681,12 @@ function evaluateFed(store2, d) {
|
|
|
28846
30681
|
return { count: count2, total, fraction: total === 0 ? 0 : count2 / total };
|
|
28847
30682
|
}
|
|
28848
30683
|
if (!d.fed) return { count: 0, total: 0, fraction: 0 };
|
|
28849
|
-
const
|
|
30684
|
+
const column2 = COLUMN_INPUTS[d.fed.attr];
|
|
28850
30685
|
const fed = d.fed;
|
|
28851
|
-
const
|
|
28852
|
-
const
|
|
30686
|
+
const all = fed.labels.flatMap((label) => stamps?.(label, fed.attr) ?? (store2.findNodeStampsByLabel ? store2.findNodeStampsByLabel(label, fed.attr) : store2.findNodeAttrsByLabel(label)));
|
|
30687
|
+
const isCloudMirror = (n) => n.cloud ?? n.attrs?.["source"] === "cloud";
|
|
30688
|
+
const nodes = fed.localOnly ? all.filter((n) => !isCloudMirror(n)) : all;
|
|
30689
|
+
const has = (n) => column2 ? column2(n) : n.has ?? n.attrs?.[fed.attr] !== void 0;
|
|
28853
30690
|
let window2 = nodes;
|
|
28854
30691
|
if (fed.recent !== void 0) {
|
|
28855
30692
|
window2 = [...nodes].sort((a, b) => b.createdAt - a.createdAt).slice(0, fed.recent);
|
|
@@ -28864,8 +30701,21 @@ function evaluateFed(store2, d) {
|
|
|
28864
30701
|
return { count, total: window2.length, fraction: window2.length === 0 ? 0 : count / window2.length };
|
|
28865
30702
|
}
|
|
28866
30703
|
function evaluateMechanisms(store2, invocations = /* @__PURE__ */ new Map(), mechanisms = MECHANISMS) {
|
|
30704
|
+
const counts = /* @__PURE__ */ new Map();
|
|
30705
|
+
const count = (label) => {
|
|
30706
|
+
if (!counts.has(label)) counts.set(label, store2.countNodesByLabel(label));
|
|
30707
|
+
return counts.get(label);
|
|
30708
|
+
};
|
|
30709
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
30710
|
+
for (const d of mechanisms) for (const l of d.fed?.labels ?? []) wanted.set(l, (wanted.get(l) ?? /* @__PURE__ */ new Set()).add(d.fed.attr));
|
|
30711
|
+
for (const d of mechanisms) if (d.fedIncoming) wanted.set(d.fedIncoming.label, (wanted.get(d.fedIncoming.label) ?? /* @__PURE__ */ new Set()).add("source"));
|
|
30712
|
+
const byLabel = /* @__PURE__ */ new Map();
|
|
30713
|
+
const stamps = store2.findNodeStampsByLabelAttrs ? (label, attr) => {
|
|
30714
|
+
if (!byLabel.has(label)) byLabel.set(label, store2.findNodeStampsByLabelAttrs(label, [...wanted.get(label) ?? [attr]]));
|
|
30715
|
+
return byLabel.get(label).get(attr);
|
|
30716
|
+
} : void 0;
|
|
28867
30717
|
return mechanisms.map((d) => {
|
|
28868
|
-
const fed = evaluateFed(store2, d);
|
|
30718
|
+
const fed = evaluateFed(store2, d, stamps);
|
|
28869
30719
|
const inv = invocations.get(d.pass);
|
|
28870
30720
|
const effectCount = d.effectIsLevel ? Math.max(
|
|
28871
30721
|
0,
|
|
@@ -28876,9 +30726,12 @@ function evaluateMechanisms(store2, invocations = /* @__PURE__ */ new Map(), mec
|
|
|
28876
30726
|
const span = inv?.firstTs === void 0 ? 0 : inv.lastTs - inv.firstTs;
|
|
28877
30727
|
const stalled = backlog !== void 0 && backlog > 0 && effectCount === 0 && (inv?.runs ?? 0) >= STALL_MIN_RUNS && span >= STALL_MIN_SPAN_MS && (backlogWas === void 0 || backlog >= backlogWas);
|
|
28878
30728
|
let verdict;
|
|
28879
|
-
|
|
28880
|
-
|
|
28881
|
-
|
|
30729
|
+
const hasInput = fed.total > 0 || Boolean(d.fed?.sinceFirstStamp && d.fed.labels.some((label) => count(label) > 0));
|
|
30730
|
+
const missingRequiredNodes = d.requiresNodes?.some((label) => count(label) === 0) ?? false;
|
|
30731
|
+
if (d.configCounter && inv?.lastTotals?.[d.configCounter] === 0) verdict = "unconfigured";
|
|
30732
|
+
else if ((backlog ?? 0) === 0 && (!hasInput && !d.inputOptional || missingRequiredNodes)) verdict = "no-input";
|
|
30733
|
+
else if (!inv || inv.runs === 0) verdict = "never-invoked";
|
|
30734
|
+
else if (fed.fraction < minFractionOf(d)) verdict = "starved";
|
|
28882
30735
|
else if (stalled) verdict = "stalled";
|
|
28883
30736
|
else if (effectCount === 0) verdict = "no-effect";
|
|
28884
30737
|
else verdict = "ok";
|
|
@@ -28889,7 +30742,7 @@ function evaluateMechanisms(store2, invocations = /* @__PURE__ */ new Map(), mec
|
|
|
28889
30742
|
fedCount: fed.count,
|
|
28890
30743
|
fedTotal: fed.total,
|
|
28891
30744
|
fedFraction: fed.fraction,
|
|
28892
|
-
minFraction: d
|
|
30745
|
+
minFraction: minFractionOf(d),
|
|
28893
30746
|
...inv ? { lastInvokedMs: inv.lastTs } : {},
|
|
28894
30747
|
runs: inv?.runs ?? 0,
|
|
28895
30748
|
effectCount,
|
|
@@ -28901,57 +30754,8 @@ function evaluateMechanisms(store2, invocations = /* @__PURE__ */ new Map(), mec
|
|
|
28901
30754
|
};
|
|
28902
30755
|
});
|
|
28903
30756
|
}
|
|
28904
|
-
|
|
28905
|
-
|
|
28906
|
-
var WM_CALIBRATION_LABELS = [
|
|
28907
|
-
"Problem",
|
|
28908
|
-
"Solution",
|
|
28909
|
-
"RootCause",
|
|
28910
|
-
"Pattern",
|
|
28911
|
-
"Technique",
|
|
28912
|
-
"AntiPattern"
|
|
28913
|
-
];
|
|
28914
|
-
var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
28915
|
-
var WM_CALIBRATION_KEYS = [
|
|
28916
|
-
"shownCount",
|
|
28917
|
-
"evictedCount",
|
|
28918
|
-
"reinforceShownCount",
|
|
28919
|
-
"reinforceEvictedCount",
|
|
28920
|
-
"reinforceUnshownCount"
|
|
28921
|
-
];
|
|
28922
|
-
function wmCalibrationCells(store2) {
|
|
28923
|
-
const cells = {
|
|
28924
|
-
shownNodes: 0,
|
|
28925
|
-
shownImpressions: 0,
|
|
28926
|
-
evictedNodes: 0,
|
|
28927
|
-
evictedImpressions: 0,
|
|
28928
|
-
reinforceShown: 0,
|
|
28929
|
-
reinforceEvicted: 0,
|
|
28930
|
-
reinforceUnshown: 0,
|
|
28931
|
-
reinforcedNodes: 0
|
|
28932
|
-
};
|
|
28933
|
-
for (const label of WM_CALIBRATION_LABELS) {
|
|
28934
|
-
for (const n of store2.findNodeFieldsByLabel(label, WM_CALIBRATION_KEYS)) {
|
|
28935
|
-
const shown = num(n.attrs["shownCount"]);
|
|
28936
|
-
const evicted = num(n.attrs["evictedCount"]);
|
|
28937
|
-
if (shown > 0) {
|
|
28938
|
-
cells.shownNodes++;
|
|
28939
|
-
cells.shownImpressions += shown;
|
|
28940
|
-
}
|
|
28941
|
-
if (evicted > 0) {
|
|
28942
|
-
cells.evictedNodes++;
|
|
28943
|
-
cells.evictedImpressions += evicted;
|
|
28944
|
-
}
|
|
28945
|
-
const rs = num(n.attrs["reinforceShownCount"]);
|
|
28946
|
-
const re = num(n.attrs["reinforceEvictedCount"]);
|
|
28947
|
-
const ru = num(n.attrs["reinforceUnshownCount"]);
|
|
28948
|
-
cells.reinforceShown += rs;
|
|
28949
|
-
cells.reinforceEvicted += re;
|
|
28950
|
-
cells.reinforceUnshown += ru;
|
|
28951
|
-
if (rs + re + ru > 0) cells.reinforcedNodes++;
|
|
28952
|
-
}
|
|
28953
|
-
}
|
|
28954
|
-
return cells;
|
|
30757
|
+
function minFractionOf(d) {
|
|
30758
|
+
return d.fedIncoming?.minFraction ?? d.fedEdges?.minFraction ?? d.fed?.minFraction ?? 0;
|
|
28955
30759
|
}
|
|
28956
30760
|
|
|
28957
30761
|
// ../../packages/generalizer/src/generalizer.ts
|
|
@@ -29036,14 +30840,12 @@ function promoteMotifs(store2, t) {
|
|
|
29036
30840
|
}
|
|
29037
30841
|
report.communities = byCommunity.size;
|
|
29038
30842
|
const existing = /* @__PURE__ */ new Map();
|
|
29039
|
-
for (const
|
|
29040
|
-
|
|
29041
|
-
|
|
29042
|
-
|
|
29043
|
-
|
|
29044
|
-
|
|
29045
|
-
existing.set(node.id, { node, edges, members });
|
|
29046
|
-
}
|
|
30843
|
+
for (const row of store2.selectNodes({ labels: MOTIF_LABELS, where: [{ idPrefix: "motif_" }] })) {
|
|
30844
|
+
const node = store2.getNode(row.id);
|
|
30845
|
+
const edges = store2.inEdges(node.id, ["INSTANCE_OF"]).filter(isDerived);
|
|
30846
|
+
const remembered = node.attrs["memberIds"];
|
|
30847
|
+
const members = new Set(Array.isArray(remembered) ? remembered : edges.map((e) => e.from));
|
|
30848
|
+
existing.set(node.id, { node, edges, members });
|
|
29047
30849
|
}
|
|
29048
30850
|
const pairs = [];
|
|
29049
30851
|
for (const [cid, members] of byCommunity) {
|
|
@@ -29484,24 +31286,129 @@ var AGENTS_POINTER_BODY = [
|
|
|
29484
31286
|
// ../../packages/context-writer/src/render.ts
|
|
29485
31287
|
init_src2();
|
|
29486
31288
|
init_src();
|
|
31289
|
+
|
|
31290
|
+
// ../../packages/context-writer/src/snapshot-inputs.ts
|
|
31291
|
+
var SNAPSHOT_NODE_LABELS = [
|
|
31292
|
+
"Problem",
|
|
31293
|
+
"Vulnerability",
|
|
31294
|
+
"Solution",
|
|
31295
|
+
"Pattern",
|
|
31296
|
+
"Technique",
|
|
31297
|
+
"AntiPattern",
|
|
31298
|
+
"Language",
|
|
31299
|
+
"Package",
|
|
31300
|
+
"Tool"
|
|
31301
|
+
];
|
|
31302
|
+
var SNAPSHOT_EDGE_TYPES = [
|
|
31303
|
+
"ANCHORED_AT",
|
|
31304
|
+
"CAUSED_BY",
|
|
31305
|
+
"CONCERNS",
|
|
31306
|
+
"DEPENDS_ON",
|
|
31307
|
+
"EVIDENCED_BY",
|
|
31308
|
+
"MANIFESTED_IN",
|
|
31309
|
+
"OCCURS_IN",
|
|
31310
|
+
"PERTAIN_TO",
|
|
31311
|
+
"REGRESSION_OF",
|
|
31312
|
+
"SOLVED_BY"
|
|
31313
|
+
];
|
|
31314
|
+
var SNAPSHOT_READ_GUARD = typeof process !== "undefined" && Boolean(process.env["VITEST"]);
|
|
31315
|
+
var LABELS = new Set(SNAPSHOT_NODE_LABELS);
|
|
31316
|
+
var EDGES = new Set(SNAPSHOT_EDGE_TYPES);
|
|
31317
|
+
function guardSnapshotReads(store2) {
|
|
31318
|
+
const fail = (what) => {
|
|
31319
|
+
throw new Error(`buildSnapshot read ${what}, outside SNAPSHOT_NODE_LABELS/SNAPSHOT_EDGE_TYPES (snapshot-inputs.ts)`);
|
|
31320
|
+
};
|
|
31321
|
+
const label = (l, via) => {
|
|
31322
|
+
if (typeof l === "string" && !LABELS.has(l)) fail(`label ${l} via ${via}`);
|
|
31323
|
+
};
|
|
31324
|
+
const types = (t, via) => {
|
|
31325
|
+
if (!Array.isArray(t) || t.length === 0) fail(`every edge type via ${via}`);
|
|
31326
|
+
for (const x of t) if (!EDGES.has(String(x))) fail(`edge type ${String(x)} via ${via}`);
|
|
31327
|
+
};
|
|
31328
|
+
const nodes = (r, via) => {
|
|
31329
|
+
for (const n of Array.isArray(r) ? r : r ? [r] : []) label(n.label, via);
|
|
31330
|
+
return r;
|
|
31331
|
+
};
|
|
31332
|
+
const allowed = {
|
|
31333
|
+
getNode: (id) => nodes(store2.getNode(id), "getNode"),
|
|
31334
|
+
selectNodes: (q) => {
|
|
31335
|
+
for (const l of q.labels) label(l, "selectNodes");
|
|
31336
|
+
return store2.selectNodes(q);
|
|
31337
|
+
},
|
|
31338
|
+
countNodes: (q) => {
|
|
31339
|
+
for (const l of q.labels) label(l, "countNodes");
|
|
31340
|
+
return store2.countNodes(q);
|
|
31341
|
+
},
|
|
31342
|
+
findLiveNodesByAttr: (l, ...rest) => {
|
|
31343
|
+
label(l, "findLiveNodesByAttr");
|
|
31344
|
+
return store2.findLiveNodesByAttr(l, ...rest);
|
|
31345
|
+
},
|
|
31346
|
+
outEdges: (id, t) => {
|
|
31347
|
+
types(t, "outEdges");
|
|
31348
|
+
return store2.outEdges(id, t);
|
|
31349
|
+
},
|
|
31350
|
+
inEdges: (id, t) => {
|
|
31351
|
+
types(t, "inEdges");
|
|
31352
|
+
return store2.inEdges(id, t);
|
|
31353
|
+
},
|
|
31354
|
+
inDegreeByType: (t) => {
|
|
31355
|
+
types(t, "inDegreeByType");
|
|
31356
|
+
return store2.inDegreeByType(t);
|
|
31357
|
+
},
|
|
31358
|
+
nodesAsOfWhereTrue: (asOf, attr) => {
|
|
31359
|
+
if (attr !== "revisit") fail(`nodesAsOfWhereTrue(${String(attr)})`);
|
|
31360
|
+
return store2.nodesAsOfWhereTrue(asOf, attr);
|
|
31361
|
+
}
|
|
31362
|
+
};
|
|
31363
|
+
return new Proxy(store2, {
|
|
31364
|
+
get(target, prop) {
|
|
31365
|
+
if (typeof prop === "string" && prop in allowed) return allowed[prop];
|
|
31366
|
+
const v = Reflect.get(target, prop, target);
|
|
31367
|
+
if (typeof v === "function") return () => fail(`store.${String(prop)}()`);
|
|
31368
|
+
return v;
|
|
31369
|
+
}
|
|
31370
|
+
});
|
|
31371
|
+
}
|
|
31372
|
+
|
|
31373
|
+
// ../../packages/context-writer/src/render.ts
|
|
29487
31374
|
var MOTIF_BAND = 10;
|
|
29488
31375
|
var MOTIF_POOL = 1e3;
|
|
29489
31376
|
function rankMotifs(pool, signal, k = MOTIF_BAND) {
|
|
29490
31377
|
if (!signal) return pool.slice(0, k);
|
|
29491
31378
|
return pool.filter((m) => (signal.get(m.id) ?? 0) > 0).sort((a, b) => signal.get(b.id) - signal.get(a.id) || b.pageRank - a.pageRank).slice(0, k);
|
|
29492
31379
|
}
|
|
31380
|
+
var HISTORY_POLICY_ATTRS = ["source", "extractionSource", "historyQuality", "validationSource", "attrsJson"];
|
|
31381
|
+
function passiveAllowed(row, atCommit) {
|
|
31382
|
+
return historyReadPolicy({ extractionSource: row.extractionSource, attrs: row.attrs }, { mode: "passive", atCommit }).allow;
|
|
31383
|
+
}
|
|
31384
|
+
var PROBLEM_BAND_ATTRS = [
|
|
31385
|
+
"mergedInto",
|
|
31386
|
+
"resolvedAt",
|
|
31387
|
+
"resolvedAs",
|
|
31388
|
+
"kind",
|
|
31389
|
+
"shownSinceTouch",
|
|
31390
|
+
"shownSinceTouchAt",
|
|
31391
|
+
...HISTORY_POLICY_ATTRS
|
|
31392
|
+
];
|
|
29493
31393
|
function buildSnapshot(opts) {
|
|
29494
|
-
const
|
|
31394
|
+
const codeStore = opts.store;
|
|
31395
|
+
if (SNAPSHOT_READ_GUARD) opts = { ...opts, store: guardSnapshotReads(opts.store) };
|
|
31396
|
+
const hydrate = (id) => opts.store.getNode(id);
|
|
31397
|
+
const problems = opts.store.selectNodes({ labels: ["Problem", "Vulnerability"], attrs: PROBLEM_BAND_ATTRS }).filter((p) => p.attrs["mergedInto"] === void 0 && passiveAllowed(p, opts.atCommit));
|
|
29495
31398
|
const stillOpen = problems.filter((p) => p.attrs["resolvedAt"] == null);
|
|
29496
31399
|
const allProblems = rankRecentBand(stillOpen.filter((p) => !isConstraintProblem(p)));
|
|
29497
|
-
const openPool = allProblems.slice(0, RECENT_OPEN * SCOPE_POOL_FACTOR).map((p) => ({
|
|
29498
|
-
node: p,
|
|
31400
|
+
const openPool = allProblems.slice(0, Math.max(RECENT_OPEN * SCOPE_POOL_FACTOR, RECENT_WINDOW_BANDS * RECENT_OPEN)).map((p) => ({
|
|
31401
|
+
node: hydrate(p.id),
|
|
29499
31402
|
anchors: opts.store.outEdges(p.id, ["MANIFESTED_IN", "EVIDENCED_BY"])
|
|
29500
31403
|
}));
|
|
29501
31404
|
const recent = openPool.slice(0, RECENT_OPEN);
|
|
29502
|
-
const constraintPool = rankRecentBand(stillOpen.filter((p) => isConstraintProblem(p)), { window: RECENT_WINDOW_BANDS * RECENT_CONSTRAINTS }).slice(0, RECENT_CONSTRAINTS * SCOPE_POOL_FACTOR);
|
|
31405
|
+
const constraintPool = rankRecentBand(stillOpen.filter((p) => isConstraintProblem(p)), { window: RECENT_WINDOW_BANDS * RECENT_CONSTRAINTS }).slice(0, Math.max(RECENT_CONSTRAINTS * SCOPE_POOL_FACTOR, RECENT_WINDOW_BANDS * RECENT_CONSTRAINTS)).map((p) => hydrate(p.id));
|
|
29503
31406
|
const recentConstraints = constraintPool.slice(0, RECENT_CONSTRAINTS);
|
|
29504
|
-
const
|
|
31407
|
+
const regressionSources = /* @__PURE__ */ new Set();
|
|
31408
|
+
for (const target of opts.store.inDegreeByType(["REGRESSION_OF"]).keys()) {
|
|
31409
|
+
for (const e of opts.store.inEdges(target, ["REGRESSION_OF"])) regressionSources.add(e.from);
|
|
31410
|
+
}
|
|
31411
|
+
const regressions = problems.filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0 && regressionSources.has(p.id)).map((p) => hydrate(p.id)).flatMap(
|
|
29505
31412
|
(open) => opts.store.outEdges(open.id, ["REGRESSION_OF"]).flatMap((e) => {
|
|
29506
31413
|
const closed = opts.store.getNode(e.to);
|
|
29507
31414
|
if (!closed || closed.attrs["resolutionSuspect"] !== true || closed.attrs["resolvedAt"] == null) {
|
|
@@ -29516,18 +31423,14 @@ function buildSnapshot(opts) {
|
|
|
29516
31423
|
const resolvedPool = problems.filter((p) => p.attrs["resolvedAt"] != null && p.attrs["resolvedAs"] == null).filter((p) => !regressionClosedIds.has(p.id)).sort((a, b) => Number(b.attrs["resolvedAt"] ?? 0) - Number(a.attrs["resolvedAt"] ?? 0)).slice(0, RECENT_RESOLVED * SCOPE_POOL_FACTOR).map((p) => {
|
|
29517
31424
|
const solEdge = opts.store.outEdges(p.id, ["SOLVED_BY"])[0];
|
|
29518
31425
|
const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
|
|
29519
|
-
return { node: p, ...solution ? { solution } : {} };
|
|
31426
|
+
return { node: hydrate(p.id), ...solution ? { solution } : {} };
|
|
29520
31427
|
});
|
|
29521
31428
|
const recentResolved = resolvedPool.slice(0, RECENT_RESOLVED);
|
|
29522
|
-
const motifPool = [
|
|
29523
|
-
...opts.store.findNodesByLabel("Pattern"),
|
|
29524
|
-
...opts.store.findNodesByLabel("Technique"),
|
|
29525
|
-
...opts.store.findNodesByLabel("AntiPattern")
|
|
29526
|
-
].filter((n) => historyReadPolicy(n, { mode: "passive", atCommit: opts.atCommit }).allow).filter((n) => !(Number(n.attrs["missedRuns"] ?? 0) > 0)).sort((a, b) => b.pageRank - a.pageRank).slice(0, MOTIF_POOL).map((n) => n.embedding.length > 0 ? { ...n, embedding: [] } : n);
|
|
31429
|
+
const motifPool = opts.store.selectNodes({ labels: ["Pattern", "Technique", "AntiPattern"], attrs: ["missedRuns", ...HISTORY_POLICY_ATTRS] }).filter((n) => passiveAllowed(n, opts.atCommit)).filter((n) => !(Number(n.attrs["missedRuns"] ?? 0) > 0)).sort((a, b) => b.pageRank - a.pageRank).slice(0, MOTIF_POOL).map((r) => hydrate(r.id)).map((n) => n.embedding.length > 0 ? { ...n, embedding: [] } : n);
|
|
29527
31430
|
const motifs = rankMotifs(motifPool, void 0);
|
|
29528
|
-
const workingFile = opts.workingFile ? sliceForFile(
|
|
31431
|
+
const workingFile = opts.workingFile ? sliceForFile(codeStore, opts.workingFile, opts.atCommit) : void 0;
|
|
29529
31432
|
const byEpisode = /* @__PURE__ */ new Map();
|
|
29530
|
-
for (const sol of opts.store.
|
|
31433
|
+
for (const sol of opts.store.findLiveNodesByAttr("Solution", "enrichmentPending", 1)) {
|
|
29531
31434
|
if (!historyReadPolicy(sol, { mode: "passive", atCommit: opts.atCommit }).allow) continue;
|
|
29532
31435
|
if (sol.attrs["enrichmentPending"] !== true) continue;
|
|
29533
31436
|
const ep = sol.attrs["episodeId"] ?? sol.id;
|
|
@@ -29558,13 +31461,14 @@ function buildSnapshot(opts) {
|
|
|
29558
31461
|
const matchNode = (cands, key) => {
|
|
29559
31462
|
const k = norm(key);
|
|
29560
31463
|
if (k.length < 2) return null;
|
|
29561
|
-
|
|
31464
|
+
const hit = cands.find((n) => norm(n.description ?? "") === k) ?? cands.find((n) => {
|
|
29562
31465
|
const d = norm(n.description ?? "");
|
|
29563
31466
|
return d.length >= 2 && (d.includes(k) || k.includes(d));
|
|
29564
|
-
})
|
|
31467
|
+
});
|
|
31468
|
+
return hit ? hydrate(hit.id) : null;
|
|
29565
31469
|
};
|
|
29566
|
-
const langNodes = opts.store.
|
|
29567
|
-
const pkgNodes = opts.store.
|
|
31470
|
+
const langNodes = opts.store.selectNodes({ labels: ["Language"] });
|
|
31471
|
+
const pkgNodes = opts.store.selectNodes({ labels: ["Package"] });
|
|
29568
31472
|
const toolNodes = rankToolsForHandles(opts.store, 5);
|
|
29569
31473
|
const profileContext = {
|
|
29570
31474
|
languages: opts.profile.languages.map((name2) => ({ name: name2, node: matchNode(langNodes, name2) })),
|
|
@@ -29615,8 +31519,8 @@ var RECENT_RESOLVED = 4;
|
|
|
29615
31519
|
var RECENT_CONSTRAINTS = 3;
|
|
29616
31520
|
var SCOPE_POOL_FACTOR = 3;
|
|
29617
31521
|
function sliceForFile(store2, relPath, atCommit) {
|
|
29618
|
-
const
|
|
29619
|
-
const file2 =
|
|
31522
|
+
const hit = store2.selectNodes({ labels: ["File"], where: [{ descriptionIn: [relPath] }], limit: 1 })[0];
|
|
31523
|
+
const file2 = hit ? store2.getNode(hit.id) : null;
|
|
29620
31524
|
if (!file2) return void 0;
|
|
29621
31525
|
const symbols = [];
|
|
29622
31526
|
for (const e of store2.outEdges(file2.id, ["DEFINES"])) {
|
|
@@ -29794,11 +31698,20 @@ function listSourceFiles(root) {
|
|
|
29794
31698
|
return gitSourceFiles(root) ?? walkSource(root);
|
|
29795
31699
|
}
|
|
29796
31700
|
var SCAN_YIELD_EVERY = 100;
|
|
29797
|
-
async function findStaleFiles(store2, rootPath, workspaceId) {
|
|
31701
|
+
async function findStaleFiles(store2, rootPath, workspaceId, lineageVerifiedAt) {
|
|
29798
31702
|
const stale = [];
|
|
31703
|
+
const changed = lineageVerifiedAt !== void 0 && store2.fileRowStates ? store2.lineageChangedFiles?.(lineageVerifiedAt) : null;
|
|
31704
|
+
const relineage = changed ? new Set(changed) : null;
|
|
29799
31705
|
let pending = [];
|
|
29800
31706
|
const check2 = () => {
|
|
29801
|
-
const
|
|
31707
|
+
const full = relineage ? pending.filter((f) => relineage.has(f.id)) : pending;
|
|
31708
|
+
const states = new Map(
|
|
31709
|
+
store2.fileIndexStates(full.map((f) => f.id)).map((s) => [s.id, s])
|
|
31710
|
+
);
|
|
31711
|
+
if (relineage) {
|
|
31712
|
+
const rowOnly = pending.filter((f) => !relineage.has(f.id)).map((f) => f.id);
|
|
31713
|
+
for (const s of store2.fileRowStates(rowOnly)) states.set(s.id, { ...s, complete: true });
|
|
31714
|
+
}
|
|
29802
31715
|
for (const f of pending) {
|
|
29803
31716
|
const state = states.get(f.id);
|
|
29804
31717
|
if (!state || state.validTo !== null || state.lastUpdatedAt < f.mtimeMs || !state.complete) stale.push(f.abs);
|
|
@@ -29820,9 +31733,34 @@ async function findStaleFiles(store2, rootPath, workspaceId) {
|
|
|
29820
31733
|
check2();
|
|
29821
31734
|
return stale;
|
|
29822
31735
|
}
|
|
31736
|
+
var FILE_DECLARATION_LABELS = [
|
|
31737
|
+
"Function",
|
|
31738
|
+
"Class",
|
|
31739
|
+
"Method",
|
|
31740
|
+
"Module",
|
|
31741
|
+
"Interface",
|
|
31742
|
+
"Const",
|
|
31743
|
+
"Static",
|
|
31744
|
+
"Property",
|
|
31745
|
+
"TypeAlias",
|
|
31746
|
+
"Namespace",
|
|
31747
|
+
"Enum",
|
|
31748
|
+
"Annotation",
|
|
31749
|
+
"Comment"
|
|
31750
|
+
];
|
|
31751
|
+
var DECLARATION_LABEL_SET = new Set(FILE_DECLARATION_LABELS);
|
|
31752
|
+
var LINEAGE_VERIFIED_KEY = "pass-inputs:reconcile-lineage";
|
|
29823
31753
|
async function reconcileStaleFiles(store2, rootPath, workspaceId) {
|
|
29824
|
-
const
|
|
29825
|
-
|
|
31754
|
+
const startedAt = store2.changeSeq?.();
|
|
31755
|
+
const verified = Number(store2.getMeta?.(LINEAGE_VERIFIED_KEY) ?? NaN);
|
|
31756
|
+
const stale = await findStaleFiles(store2, rootPath, workspaceId, Number.isFinite(verified) ? verified : void 0);
|
|
31757
|
+
const verify = () => {
|
|
31758
|
+
if (startedAt !== void 0) store2.setMeta?.(LINEAGE_VERIFIED_KEY, String(startedAt));
|
|
31759
|
+
};
|
|
31760
|
+
if (stale.length === 0) {
|
|
31761
|
+
verify();
|
|
31762
|
+
return 0;
|
|
31763
|
+
}
|
|
29826
31764
|
let total = 0;
|
|
29827
31765
|
let failedBatches = 0;
|
|
29828
31766
|
const BATCH = 20;
|
|
@@ -29840,11 +31778,15 @@ async function reconcileStaleFiles(store2, rootPath, workspaceId) {
|
|
|
29840
31778
|
}
|
|
29841
31779
|
if (failedBatches > 0) {
|
|
29842
31780
|
console.warn(`[errata] reconcile: ${failedBatches} batch(es) failed; ${total} file(s) reindexed`);
|
|
31781
|
+
} else {
|
|
31782
|
+
verify();
|
|
29843
31783
|
}
|
|
29844
31784
|
return total;
|
|
29845
31785
|
}
|
|
29846
31786
|
|
|
29847
31787
|
// src/workspace-maintenance.ts
|
|
31788
|
+
var WM_CALIBRATION_AT_KEY = "wm-calibration-at";
|
|
31789
|
+
var WM_CALIBRATION_INTERVAL_MS = 30 * 6e4;
|
|
29848
31790
|
function runWorkspaceMaintenance(store2, workspaceId) {
|
|
29849
31791
|
const seqBefore = store2.currentIngestSeq();
|
|
29850
31792
|
const report = runSemanticMaintenance(store2);
|
|
@@ -29855,9 +31797,13 @@ function runWorkspaceMaintenance(store2, workspaceId) {
|
|
|
29855
31797
|
}
|
|
29856
31798
|
let calibration = null;
|
|
29857
31799
|
const calibrationStarted = Date.now();
|
|
29858
|
-
|
|
29859
|
-
|
|
29860
|
-
|
|
31800
|
+
const lastCalibration = Number(store2.getMeta?.(WM_CALIBRATION_AT_KEY) ?? 0);
|
|
31801
|
+
if (!(calibrationStarted - lastCalibration < WM_CALIBRATION_INTERVAL_MS)) {
|
|
31802
|
+
try {
|
|
31803
|
+
calibration = wmCalibrationCells(store2);
|
|
31804
|
+
store2.setMeta?.(WM_CALIBRATION_AT_KEY, String(calibrationStarted));
|
|
31805
|
+
} catch {
|
|
31806
|
+
}
|
|
29861
31807
|
}
|
|
29862
31808
|
return {
|
|
29863
31809
|
report,
|
|
@@ -30114,9 +32060,9 @@ function readDirectDependencies(root) {
|
|
|
30114
32060
|
const hit = directCache.get(root);
|
|
30115
32061
|
if (hit && hit.key === key) return hit.direct;
|
|
30116
32062
|
const direct = [];
|
|
30117
|
-
for (const { c, path } of found) {
|
|
32063
|
+
for (const { c, path: path2 } of found) {
|
|
30118
32064
|
try {
|
|
30119
|
-
direct.push(...c.parse(readFileSync3(
|
|
32065
|
+
direct.push(...c.parse(readFileSync3(path2, "utf8")).direct ?? []);
|
|
30120
32066
|
} catch {
|
|
30121
32067
|
}
|
|
30122
32068
|
}
|
|
@@ -30199,7 +32145,7 @@ function scanManifests(root) {
|
|
|
30199
32145
|
try {
|
|
30200
32146
|
const spine = buildSpine("workspace", [...texts.keys()], (p) => texts.get(p) ?? "");
|
|
30201
32147
|
scan2.deps = Object.values(spine.manifests).flat();
|
|
30202
|
-
for (const [
|
|
32148
|
+
for (const [path2, text] of texts) scan2.self.push(...parseManifestSelf(path2, text));
|
|
30203
32149
|
} catch {
|
|
30204
32150
|
scan2.deps = [];
|
|
30205
32151
|
scan2.self = [];
|