@inerrata-corporation/errata 2.0.21-dev.3745 → 2.0.21-dev.3763
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/consolidate-worker.mjs +271 -177
- package/errata.mjs +4517 -4407
- package/generalize-worker.mjs +94 -0
- package/package.json +1 -1
- package/pass-worker.mjs +1934 -1838
- package/resources/context-plugin/.codex-plugin/plugin.json +1 -1
- package/resources/context-plugin/SHA256SUMS +1 -1
package/pass-worker.mjs
CHANGED
|
@@ -16146,10 +16146,12 @@ function historyReadPolicy(node, options = {}) {
|
|
|
16146
16146
|
notice: `History-derived${at}; source ${String(source)}; applicability ${current ? String(applicability) : "unknown at this checkout"}; ${environmentKnown ? "dependency context checked" : "dependency applicability unverified"}; ${independentlyValidated ? "separate validation recorded" : "not independently validated"}.`
|
|
16147
16147
|
};
|
|
16148
16148
|
}
|
|
16149
|
+
var CLOUD_ATTR_DUPLICATE_KEYS;
|
|
16149
16150
|
var init_history_read = __esm({
|
|
16150
16151
|
"../../packages/shared/src/history-read.ts"() {
|
|
16151
16152
|
"use strict";
|
|
16152
16153
|
init_history_dependencies();
|
|
16154
|
+
CLOUD_ATTR_DUPLICATE_KEYS = ["attrsJson", "embedding"];
|
|
16153
16155
|
}
|
|
16154
16156
|
});
|
|
16155
16157
|
|
|
@@ -25425,7 +25427,474 @@ function installEditPlaceholderRetirement(db) {
|
|
|
25425
25427
|
});
|
|
25426
25428
|
}
|
|
25427
25429
|
|
|
25430
|
+
// ../../packages/local-graph/src/cloud-attr-compaction.ts
|
|
25431
|
+
init_src2();
|
|
25432
|
+
var CLOUD_ATTR_COMPACTION_KEY = "cloud-attr-compaction-1";
|
|
25433
|
+
var DUPES = CLOUD_ATTR_DUPLICATE_KEYS.map((k) => `'${k}'`).join(",");
|
|
25434
|
+
function compacted(row) {
|
|
25435
|
+
const packed = `json_extract(${row}.attrs_json, '$.attrsJson')`;
|
|
25436
|
+
const nested = `CASE WHEN json_type(${row}.attrs_json, '$.attrsJson') = 'text' AND json_valid(${packed})
|
|
25437
|
+
AND json_type(${packed}) = 'object' THEN ${packed} ELSE '{}' END`;
|
|
25438
|
+
const value = `CASE WHEN type IN ('array','object') THEN json(value) WHEN type IN ('true','false') THEN json(type) ELSE value END`;
|
|
25439
|
+
return `(SELECT json_group_object(key, ${value}) FROM (
|
|
25440
|
+
SELECT key, value, type, min(pri) FROM (
|
|
25441
|
+
SELECT key, value, type, 0 AS pri FROM json_each(${row}.attrs_json)
|
|
25442
|
+
UNION ALL
|
|
25443
|
+
SELECT key, value, type, 1 AS pri FROM json_each(${nested})
|
|
25444
|
+
) WHERE key NOT IN (${DUPES}) GROUP BY key))`;
|
|
25445
|
+
}
|
|
25446
|
+
function dirty(row) {
|
|
25447
|
+
const has = CLOUD_ATTR_DUPLICATE_KEYS.map((k) => `json_type(${row}.attrs_json, '$.${k}') IS NOT NULL`).join(" OR ");
|
|
25448
|
+
return `json_extract(${row}.attrs_json, '$.source') = 'cloud' AND (${has})`;
|
|
25449
|
+
}
|
|
25450
|
+
function installCloudAttrCompaction(db) {
|
|
25451
|
+
if (db.prepare("SELECT value FROM store_meta WHERE key = ?").get(CLOUD_ATTR_COMPACTION_KEY)) return;
|
|
25452
|
+
db.transaction(() => {
|
|
25453
|
+
for (const operation of ["INSERT", "UPDATE"]) {
|
|
25454
|
+
db.exec(`CREATE TRIGGER IF NOT EXISTS cloud_attr_compact_${operation.toLowerCase()}
|
|
25455
|
+
AFTER ${operation} ON nodes WHEN ${dirty("NEW")}
|
|
25456
|
+
BEGIN UPDATE nodes SET attrs_json = ${compacted("nodes")} WHERE id = NEW.id; END;`);
|
|
25457
|
+
}
|
|
25458
|
+
db.exec(`UPDATE nodes SET attrs_json = ${compacted("nodes")} WHERE ${dirty("nodes")}`);
|
|
25459
|
+
db.prepare("INSERT INTO store_meta(key,value) VALUES (?,?)").run(CLOUD_ATTR_COMPACTION_KEY, "installed");
|
|
25460
|
+
});
|
|
25461
|
+
}
|
|
25462
|
+
|
|
25463
|
+
// ../../packages/math/src/pagerank.ts
|
|
25464
|
+
function pagerank(input) {
|
|
25465
|
+
const damping = input.damping ?? 0.85;
|
|
25466
|
+
const tol = input.tolerance ?? 1e-6;
|
|
25467
|
+
const maxIter = input.maxIterations ?? 100;
|
|
25468
|
+
const ids = input.nodeIds;
|
|
25469
|
+
const n = ids.length;
|
|
25470
|
+
if (n === 0) {
|
|
25471
|
+
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true, danglingRank: 0 };
|
|
25472
|
+
}
|
|
25473
|
+
const index = /* @__PURE__ */ new Map();
|
|
25474
|
+
for (let i2 = 0; i2 < n; i2++) index.set(ids[i2], i2);
|
|
25475
|
+
const outSum = new Float64Array(n);
|
|
25476
|
+
const dangling = new Uint8Array(n).fill(1);
|
|
25477
|
+
const rowLen = new Int32Array(n);
|
|
25478
|
+
let edgeCount = 0;
|
|
25479
|
+
for (const [from, edges] of input.out) {
|
|
25480
|
+
const fi = index.get(from);
|
|
25481
|
+
if (fi === void 0) continue;
|
|
25482
|
+
let sum = 0;
|
|
25483
|
+
let inSet = 0;
|
|
25484
|
+
for (const e of edges) {
|
|
25485
|
+
sum += e.weight;
|
|
25486
|
+
if (index.has(e.to)) inSet++;
|
|
25487
|
+
}
|
|
25488
|
+
if (sum > 0) {
|
|
25489
|
+
outSum[fi] = sum;
|
|
25490
|
+
dangling[fi] = 0;
|
|
25491
|
+
}
|
|
25492
|
+
rowLen[fi] = inSet;
|
|
25493
|
+
edgeCount += inSet;
|
|
25494
|
+
}
|
|
25495
|
+
const rowStart = new Int32Array(n + 1);
|
|
25496
|
+
for (let i2 = 0; i2 < n; i2++) rowStart[i2 + 1] = rowStart[i2] + rowLen[i2];
|
|
25497
|
+
const colIdx = new Int32Array(edgeCount);
|
|
25498
|
+
const colW = new Float64Array(edgeCount);
|
|
25499
|
+
const cursor = rowStart.slice(0, n);
|
|
25500
|
+
for (const [from, edges] of input.out) {
|
|
25501
|
+
const fi = index.get(from);
|
|
25502
|
+
if (fi === void 0) continue;
|
|
25503
|
+
for (const e of edges) {
|
|
25504
|
+
const ti = index.get(e.to);
|
|
25505
|
+
if (ti === void 0) continue;
|
|
25506
|
+
const c = cursor[fi];
|
|
25507
|
+
cursor[fi] = c + 1;
|
|
25508
|
+
colIdx[c] = ti;
|
|
25509
|
+
colW[c] = e.weight;
|
|
25510
|
+
}
|
|
25511
|
+
}
|
|
25512
|
+
let score2 = new Float64Array(n).fill(1 / n);
|
|
25513
|
+
let next = new Float64Array(n);
|
|
25514
|
+
const base = (1 - damping) / n;
|
|
25515
|
+
let iter = 0;
|
|
25516
|
+
let converged = false;
|
|
25517
|
+
for (; iter < maxIter; iter++) {
|
|
25518
|
+
next.fill(base);
|
|
25519
|
+
let danglingMass = 0;
|
|
25520
|
+
for (let i2 = 0; i2 < n; i2++) if (dangling[i2]) danglingMass += score2[i2];
|
|
25521
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
25522
|
+
if (dangling[i2]) continue;
|
|
25523
|
+
const f = damping * score2[i2] / outSum[i2];
|
|
25524
|
+
const end = rowStart[i2 + 1];
|
|
25525
|
+
for (let c = rowStart[i2]; c < end; c++) next[colIdx[c]] += f * colW[c];
|
|
25526
|
+
}
|
|
25527
|
+
if (danglingMass > 0) {
|
|
25528
|
+
const share = damping * danglingMass / n;
|
|
25529
|
+
for (let i2 = 0; i2 < n; i2++) next[i2] += share;
|
|
25530
|
+
}
|
|
25531
|
+
let diff = 0;
|
|
25532
|
+
for (let i2 = 0; i2 < n; i2++) diff += Math.abs(next[i2] - score2[i2]);
|
|
25533
|
+
const tmp = score2;
|
|
25534
|
+
score2 = next;
|
|
25535
|
+
next = tmp;
|
|
25536
|
+
if (diff < tol) {
|
|
25537
|
+
iter++;
|
|
25538
|
+
converged = true;
|
|
25539
|
+
break;
|
|
25540
|
+
}
|
|
25541
|
+
}
|
|
25542
|
+
const scores = /* @__PURE__ */ new Map();
|
|
25543
|
+
let danglingRank = 0;
|
|
25544
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
25545
|
+
scores.set(ids[i2], score2[i2]);
|
|
25546
|
+
if (dangling[i2]) danglingRank += score2[i2];
|
|
25547
|
+
}
|
|
25548
|
+
return { scores, iterations: iter, converged, danglingRank };
|
|
25549
|
+
}
|
|
25550
|
+
function markLandmarks(scores, percentile = 0.1, filter) {
|
|
25551
|
+
const entries = [...scores.entries()];
|
|
25552
|
+
if (filter) {
|
|
25553
|
+
for (let i2 = entries.length - 1; i2 >= 0; i2--) {
|
|
25554
|
+
if (!filter(entries[i2][0])) entries.splice(i2, 1);
|
|
25555
|
+
}
|
|
25556
|
+
}
|
|
25557
|
+
if (entries.length === 0) return /* @__PURE__ */ new Set();
|
|
25558
|
+
entries.sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
|
|
25559
|
+
const cutoff = Math.max(1, Math.floor(entries.length * percentile));
|
|
25560
|
+
const out2 = /* @__PURE__ */ new Set();
|
|
25561
|
+
for (let i2 = 0; i2 < cutoff; i2++) {
|
|
25562
|
+
out2.add(entries[i2][0]);
|
|
25563
|
+
}
|
|
25564
|
+
return out2;
|
|
25565
|
+
}
|
|
25566
|
+
|
|
25567
|
+
// ../../packages/math/src/relevance.ts
|
|
25568
|
+
function pageRankScale(ranks) {
|
|
25569
|
+
const positive = [...ranks].filter((r) => Number.isFinite(r) && r > 0).sort((a, b) => a - b);
|
|
25570
|
+
return positive.length ? Math.max(1e-9, positive[Math.floor((positive.length - 1) * 0.95)]) : 1;
|
|
25571
|
+
}
|
|
25572
|
+
|
|
25573
|
+
// ../../packages/math/src/leiden.ts
|
|
25574
|
+
function buildGraph(nodeIds, adj) {
|
|
25575
|
+
const nbr = /* @__PURE__ */ new Map();
|
|
25576
|
+
const deg = /* @__PURE__ */ new Map();
|
|
25577
|
+
const present = new Set(nodeIds);
|
|
25578
|
+
for (const id of nodeIds) {
|
|
25579
|
+
nbr.set(id, /* @__PURE__ */ new Map());
|
|
25580
|
+
deg.set(id, 0);
|
|
25581
|
+
}
|
|
25582
|
+
let m2 = 0;
|
|
25583
|
+
for (const id of nodeIds) {
|
|
25584
|
+
for (const { to, weight } of adj.get(id) ?? []) {
|
|
25585
|
+
if (!present.has(to)) continue;
|
|
25586
|
+
const row = nbr.get(id);
|
|
25587
|
+
row.set(to, (row.get(to) ?? 0) + weight);
|
|
25588
|
+
deg.set(id, (deg.get(id) ?? 0) + weight);
|
|
25589
|
+
m2 += weight;
|
|
25590
|
+
}
|
|
25591
|
+
}
|
|
25592
|
+
return { nbr, deg, m2 };
|
|
25593
|
+
}
|
|
25594
|
+
function localMoving(nodeIds, g) {
|
|
25595
|
+
const community = new Map(nodeIds.map((n) => [n, n]));
|
|
25596
|
+
const comTot = new Map(nodeIds.map((n) => [n, g.deg.get(n) ?? 0]));
|
|
25597
|
+
const order = [...nodeIds].sort();
|
|
25598
|
+
const m2 = g.m2 > 0 ? g.m2 : 1;
|
|
25599
|
+
let improved = true;
|
|
25600
|
+
let iter = 0;
|
|
25601
|
+
while (improved && iter < 100) {
|
|
25602
|
+
improved = false;
|
|
25603
|
+
iter++;
|
|
25604
|
+
for (const node of order) {
|
|
25605
|
+
const ci = community.get(node);
|
|
25606
|
+
const ki = g.deg.get(node) ?? 0;
|
|
25607
|
+
comTot.set(ci, (comTot.get(ci) ?? 0) - ki);
|
|
25608
|
+
const wToComm = /* @__PURE__ */ new Map();
|
|
25609
|
+
for (const [nb, w] of g.nbr.get(node) ?? []) {
|
|
25610
|
+
if (nb === node) continue;
|
|
25611
|
+
const c = community.get(nb);
|
|
25612
|
+
wToComm.set(c, (wToComm.get(c) ?? 0) + w);
|
|
25613
|
+
}
|
|
25614
|
+
let bestC = ci;
|
|
25615
|
+
let bestGain = (wToComm.get(ci) ?? 0) - (comTot.get(ci) ?? 0) * ki / m2;
|
|
25616
|
+
for (const [c, kiIn] of [...wToComm.entries()].sort(
|
|
25617
|
+
(a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0
|
|
25618
|
+
)) {
|
|
25619
|
+
const gain = kiIn - (comTot.get(c) ?? 0) * ki / m2;
|
|
25620
|
+
if (gain > bestGain) {
|
|
25621
|
+
bestGain = gain;
|
|
25622
|
+
bestC = c;
|
|
25623
|
+
}
|
|
25624
|
+
}
|
|
25625
|
+
community.set(node, bestC);
|
|
25626
|
+
comTot.set(bestC, (comTot.get(bestC) ?? 0) + ki);
|
|
25627
|
+
if (bestC !== ci) improved = true;
|
|
25628
|
+
}
|
|
25629
|
+
}
|
|
25630
|
+
return community;
|
|
25631
|
+
}
|
|
25632
|
+
function refineConnectivity(community, g) {
|
|
25633
|
+
const members = /* @__PURE__ */ new Map();
|
|
25634
|
+
for (const [id, c] of community) {
|
|
25635
|
+
const l = members.get(c) ?? [];
|
|
25636
|
+
l.push(id);
|
|
25637
|
+
members.set(c, l);
|
|
25638
|
+
}
|
|
25639
|
+
const refined = /* @__PURE__ */ new Map();
|
|
25640
|
+
for (const [, group] of members) {
|
|
25641
|
+
const inGroup = new Set(group);
|
|
25642
|
+
const seen = /* @__PURE__ */ new Set();
|
|
25643
|
+
for (const start2 of [...group].sort()) {
|
|
25644
|
+
if (seen.has(start2)) continue;
|
|
25645
|
+
const comp = [];
|
|
25646
|
+
const stack = [start2];
|
|
25647
|
+
seen.add(start2);
|
|
25648
|
+
while (stack.length) {
|
|
25649
|
+
const cur = stack.pop();
|
|
25650
|
+
comp.push(cur);
|
|
25651
|
+
for (const [nb] of g.nbr.get(cur) ?? []) {
|
|
25652
|
+
if (inGroup.has(nb) && !seen.has(nb)) {
|
|
25653
|
+
seen.add(nb);
|
|
25654
|
+
stack.push(nb);
|
|
25655
|
+
}
|
|
25656
|
+
}
|
|
25657
|
+
}
|
|
25658
|
+
const anchor = [...comp].sort()[0];
|
|
25659
|
+
for (const id of comp) refined.set(id, anchor);
|
|
25660
|
+
}
|
|
25661
|
+
}
|
|
25662
|
+
return refined;
|
|
25663
|
+
}
|
|
25664
|
+
function normalize(assignment) {
|
|
25665
|
+
const groups = /* @__PURE__ */ new Map();
|
|
25666
|
+
for (const [id, c] of assignment) {
|
|
25667
|
+
const l = groups.get(c) ?? [];
|
|
25668
|
+
l.push(id);
|
|
25669
|
+
groups.set(c, l);
|
|
25670
|
+
}
|
|
25671
|
+
const community = /* @__PURE__ */ new Map();
|
|
25672
|
+
for (const grp of groups.values()) {
|
|
25673
|
+
const anchor = [...grp].sort()[0];
|
|
25674
|
+
const cid = `community_${anchor}`;
|
|
25675
|
+
for (const m of grp) community.set(m, cid);
|
|
25676
|
+
}
|
|
25677
|
+
return { community, count: groups.size, iterations: 1 };
|
|
25678
|
+
}
|
|
25679
|
+
function causalComponents(nodeIds, pairs) {
|
|
25680
|
+
const parent = new Map(nodeIds.map((n) => [n, n]));
|
|
25681
|
+
const find = (x) => {
|
|
25682
|
+
let r = x;
|
|
25683
|
+
while (parent.get(r) !== r) r = parent.get(r);
|
|
25684
|
+
let c = x;
|
|
25685
|
+
while (parent.get(c) !== r) {
|
|
25686
|
+
const next = parent.get(c);
|
|
25687
|
+
parent.set(c, r);
|
|
25688
|
+
c = next;
|
|
25689
|
+
}
|
|
25690
|
+
return r;
|
|
25691
|
+
};
|
|
25692
|
+
for (const [a, b] of pairs) {
|
|
25693
|
+
const ra = find(a);
|
|
25694
|
+
const rb = find(b);
|
|
25695
|
+
if (ra !== rb) parent.set(ra < rb ? rb : ra, ra < rb ? ra : rb);
|
|
25696
|
+
}
|
|
25697
|
+
return find;
|
|
25698
|
+
}
|
|
25699
|
+
function detectCommunitiesLeiden(input, opts = {}) {
|
|
25700
|
+
const idSet = new Set(input.nodeIds);
|
|
25701
|
+
const protect = [...opts.protect ?? []].filter(
|
|
25702
|
+
([a, b]) => a !== b && idSet.has(a) && idSet.has(b)
|
|
25703
|
+
);
|
|
25704
|
+
if (protect.length === 0) {
|
|
25705
|
+
const g = buildGraph(input.nodeIds, input.adj);
|
|
25706
|
+
return normalize(refineConnectivity(localMoving(input.nodeIds, g), g));
|
|
25707
|
+
}
|
|
25708
|
+
const rep = causalComponents(input.nodeIds, protect);
|
|
25709
|
+
const superIds = [...new Set(input.nodeIds.map(rep))];
|
|
25710
|
+
const superAdj = /* @__PURE__ */ new Map();
|
|
25711
|
+
const acc = /* @__PURE__ */ new Map();
|
|
25712
|
+
for (const id of input.nodeIds) {
|
|
25713
|
+
const ra = rep(id);
|
|
25714
|
+
for (const { to, weight } of input.adj.get(id) ?? []) {
|
|
25715
|
+
if (!idSet.has(to)) continue;
|
|
25716
|
+
const rb = rep(to);
|
|
25717
|
+
if (ra === rb) continue;
|
|
25718
|
+
const row = acc.get(ra) ?? /* @__PURE__ */ new Map();
|
|
25719
|
+
row.set(rb, (row.get(rb) ?? 0) + weight);
|
|
25720
|
+
acc.set(ra, row);
|
|
25721
|
+
}
|
|
25722
|
+
}
|
|
25723
|
+
for (const [a, row] of acc) superAdj.set(a, [...row].map(([to, weight]) => ({ to, weight })));
|
|
25724
|
+
const sg = buildGraph(superIds, superAdj);
|
|
25725
|
+
const superComm = refineConnectivity(localMoving(superIds, sg), sg);
|
|
25726
|
+
const assignment = /* @__PURE__ */ new Map();
|
|
25727
|
+
for (const id of input.nodeIds) assignment.set(id, superComm.get(rep(id)) ?? rep(id));
|
|
25728
|
+
return normalize(assignment);
|
|
25729
|
+
}
|
|
25730
|
+
|
|
25731
|
+
// ../../packages/math/src/bko/a1-stable-motif.ts
|
|
25732
|
+
var MIN_INSTANCES = 5;
|
|
25733
|
+
var MIN_SURVIVED_RUNS = 2;
|
|
25734
|
+
function a1StableMotif(input) {
|
|
25735
|
+
if (input.instanceCount >= MIN_INSTANCES && input.survivedRuns >= MIN_SURVIVED_RUNS) {
|
|
25736
|
+
return {
|
|
25737
|
+
shouldMarkStable: true,
|
|
25738
|
+
confidence: Math.min(1, input.instanceCount / 20),
|
|
25739
|
+
rule: "A1"
|
|
25740
|
+
};
|
|
25741
|
+
}
|
|
25742
|
+
return { shouldMarkStable: false, confidence: 0, rule: "A1" };
|
|
25743
|
+
}
|
|
25744
|
+
|
|
25745
|
+
// ../../packages/math/src/bko/a2-motif-quality.ts
|
|
25746
|
+
var MIN_INSTANCES2 = 3;
|
|
25747
|
+
var MIN_QUALITY = 0.65;
|
|
25748
|
+
var MAX_FAILURE_RATE = 0.15;
|
|
25749
|
+
var QUALITY_BOOST = 1.2;
|
|
25750
|
+
function a2MotifQuality(input) {
|
|
25751
|
+
if (input.instances.length === 0) {
|
|
25752
|
+
return {
|
|
25753
|
+
qualityScore: 0,
|
|
25754
|
+
qualityFailureRate: 0,
|
|
25755
|
+
qualified: false,
|
|
25756
|
+
qualityBoost: 1,
|
|
25757
|
+
rule: "A2"
|
|
25758
|
+
};
|
|
25759
|
+
}
|
|
25760
|
+
const mean = input.instances.reduce((s, i2) => s + i2.confidence, 0) / input.instances.length;
|
|
25761
|
+
const failures = input.instances.filter((i2) => i2.failed).length;
|
|
25762
|
+
const failureRate = failures / input.instances.length;
|
|
25763
|
+
const qualified = input.instances.length >= MIN_INSTANCES2 && mean >= MIN_QUALITY && failureRate <= MAX_FAILURE_RATE;
|
|
25764
|
+
return {
|
|
25765
|
+
qualityScore: mean,
|
|
25766
|
+
qualityFailureRate: failureRate,
|
|
25767
|
+
qualified,
|
|
25768
|
+
qualityBoost: qualified ? QUALITY_BOOST : 1,
|
|
25769
|
+
rule: "A2"
|
|
25770
|
+
};
|
|
25771
|
+
}
|
|
25772
|
+
|
|
25773
|
+
// ../../packages/math/src/motif-gate.ts
|
|
25774
|
+
var MOTIF_MIN_MEMBERS = 5;
|
|
25775
|
+
var MOTIF_MIN_SURVIVED = 2;
|
|
25776
|
+
function motifDecision(input) {
|
|
25777
|
+
if (input.memberCount < MOTIF_MIN_MEMBERS) {
|
|
25778
|
+
return {
|
|
25779
|
+
isCandidate: false,
|
|
25780
|
+
survivedRuns: input.priorSurvivedRuns,
|
|
25781
|
+
stable: false,
|
|
25782
|
+
qualified: false,
|
|
25783
|
+
promoted: false,
|
|
25784
|
+
motifConfidence: 0
|
|
25785
|
+
};
|
|
25786
|
+
}
|
|
25787
|
+
const survivedRuns = input.priorSurvivedRuns + 1;
|
|
25788
|
+
const a2 = a2MotifQuality({ instances: input.instances });
|
|
25789
|
+
const a1 = a1StableMotif({ instanceCount: input.instanceCount, survivedRuns });
|
|
25790
|
+
const stable = a1.shouldMarkStable;
|
|
25791
|
+
const promoted = survivedRuns >= MOTIF_MIN_SURVIVED && stable && a2.qualified;
|
|
25792
|
+
return {
|
|
25793
|
+
isCandidate: true,
|
|
25794
|
+
survivedRuns,
|
|
25795
|
+
stable,
|
|
25796
|
+
qualified: a2.qualified,
|
|
25797
|
+
promoted,
|
|
25798
|
+
motifConfidence: Math.round(a2.qualityScore * 100) / 100
|
|
25799
|
+
};
|
|
25800
|
+
}
|
|
25801
|
+
|
|
25802
|
+
// ../../packages/math/src/pagerank-local.ts
|
|
25803
|
+
function pagerankLocal(input) {
|
|
25804
|
+
const damping = input.damping ?? 0.85;
|
|
25805
|
+
const tol = input.tolerance ?? 1e-6;
|
|
25806
|
+
const maxIter = input.maxIterations ?? 100;
|
|
25807
|
+
const ids = input.regionIds;
|
|
25808
|
+
const n = ids.length;
|
|
25809
|
+
if (n === 0 || input.globalN === 0) {
|
|
25810
|
+
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true };
|
|
25811
|
+
}
|
|
25812
|
+
const index = /* @__PURE__ */ new Map();
|
|
25813
|
+
for (let i2 = 0; i2 < n; i2++) index.set(ids[i2], i2);
|
|
25814
|
+
const base = (1 - damping) / input.globalN;
|
|
25815
|
+
const outSum = new Float64Array(n);
|
|
25816
|
+
const inflow = new Float64Array(n);
|
|
25817
|
+
let score2 = new Float64Array(n);
|
|
25818
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
25819
|
+
const id = ids[i2];
|
|
25820
|
+
outSum[i2] = input.outSum.get(id) ?? 0;
|
|
25821
|
+
inflow[i2] = input.boundaryInflow.get(id) ?? 0;
|
|
25822
|
+
score2[i2] = input.rank0.get(id) ?? base;
|
|
25823
|
+
}
|
|
25824
|
+
const rowLen = new Int32Array(n);
|
|
25825
|
+
let edgeCount = 0;
|
|
25826
|
+
for (const [from, edges] of input.out) {
|
|
25827
|
+
const fi = index.get(from);
|
|
25828
|
+
if (fi === void 0) continue;
|
|
25829
|
+
let inSet = 0;
|
|
25830
|
+
for (const e of edges) if (index.has(e.to)) inSet++;
|
|
25831
|
+
rowLen[fi] = inSet;
|
|
25832
|
+
edgeCount += inSet;
|
|
25833
|
+
}
|
|
25834
|
+
const rowStart = new Int32Array(n + 1);
|
|
25835
|
+
for (let i2 = 0; i2 < n; i2++) rowStart[i2 + 1] = rowStart[i2] + rowLen[i2];
|
|
25836
|
+
const colIdx = new Int32Array(edgeCount);
|
|
25837
|
+
const colW = new Float64Array(edgeCount);
|
|
25838
|
+
const cursor = rowStart.slice(0, n);
|
|
25839
|
+
for (const [from, edges] of input.out) {
|
|
25840
|
+
const fi = index.get(from);
|
|
25841
|
+
if (fi === void 0) continue;
|
|
25842
|
+
for (const e of edges) {
|
|
25843
|
+
const ti = index.get(e.to);
|
|
25844
|
+
if (ti === void 0) continue;
|
|
25845
|
+
const c = cursor[fi];
|
|
25846
|
+
cursor[fi] = c + 1;
|
|
25847
|
+
colIdx[c] = ti;
|
|
25848
|
+
colW[c] = e.weight;
|
|
25849
|
+
}
|
|
25850
|
+
}
|
|
25851
|
+
const externalDangling = input.externalDanglingRank ?? 0;
|
|
25852
|
+
let next = new Float64Array(n);
|
|
25853
|
+
let iter = 0;
|
|
25854
|
+
let converged = false;
|
|
25855
|
+
for (; iter < maxIter; iter++) {
|
|
25856
|
+
let internalDangling = 0;
|
|
25857
|
+
for (let i2 = 0; i2 < n; i2++) if (outSum[i2] <= 0) internalDangling += score2[i2];
|
|
25858
|
+
const danglingShare = damping * (internalDangling + externalDangling) / input.globalN;
|
|
25859
|
+
for (let i2 = 0; i2 < n; i2++) next[i2] = base + danglingShare + damping * inflow[i2];
|
|
25860
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
25861
|
+
const os2 = outSum[i2];
|
|
25862
|
+
if (os2 <= 0) continue;
|
|
25863
|
+
const f = damping * score2[i2] / os2;
|
|
25864
|
+
const end = rowStart[i2 + 1];
|
|
25865
|
+
for (let c = rowStart[i2]; c < end; c++) next[colIdx[c]] += f * colW[c];
|
|
25866
|
+
}
|
|
25867
|
+
let diff = 0;
|
|
25868
|
+
for (let i2 = 0; i2 < n; i2++) diff += Math.abs(next[i2] - score2[i2]);
|
|
25869
|
+
const tmp = score2;
|
|
25870
|
+
score2 = next;
|
|
25871
|
+
next = tmp;
|
|
25872
|
+
if (diff < tol) {
|
|
25873
|
+
iter++;
|
|
25874
|
+
converged = true;
|
|
25875
|
+
break;
|
|
25876
|
+
}
|
|
25877
|
+
}
|
|
25878
|
+
const scores = /* @__PURE__ */ new Map();
|
|
25879
|
+
for (let i2 = 0; i2 < n; i2++) scores.set(ids[i2], score2[i2]);
|
|
25880
|
+
return { scores, iterations: iter, converged };
|
|
25881
|
+
}
|
|
25882
|
+
|
|
25428
25883
|
// ../../packages/local-graph/src/store.ts
|
|
25884
|
+
var PAGE_RANK_SCALE_KEY = "pageRankScale.p95.v1";
|
|
25885
|
+
var TEXT_SEARCH_COLUMNS = `id, label, description, page_rank,
|
|
25886
|
+
json_object('test', json(CASE WHEN json_type(attrs_json, '$.test') = 'true' THEN 'true' ELSE 'false' END),
|
|
25887
|
+
'relPath', json_extract(attrs_json, '$.relPath'),
|
|
25888
|
+
'resolutionSuspect', json(CASE WHEN json_type(attrs_json, '$.resolutionSuspect') = 'true' THEN 'true' ELSE 'false' END),
|
|
25889
|
+
'resolvedAt', json_extract(attrs_json, '$.resolvedAt'),
|
|
25890
|
+
'refutedAt', json_extract(attrs_json, '$.refutedAt'),
|
|
25891
|
+
'refuteReasons', json_extract(attrs_json, '$.refuteReasons'),
|
|
25892
|
+
'reopenedFrom', json_extract(attrs_json, '$.reopenedFrom'),
|
|
25893
|
+
'resolvedAs', json_extract(attrs_json, '$.resolvedAs')) AS search_attrs`;
|
|
25894
|
+
function textSearchNode(row) {
|
|
25895
|
+
const attrs = JSON.parse(row.search_attrs);
|
|
25896
|
+
return { id: row.id, label: row.label, description: row.description, pageRank: row.page_rank, attrs };
|
|
25897
|
+
}
|
|
25429
25898
|
var LOCAL_RULE_OVERRIDES = {
|
|
25430
25899
|
ANCHORED_AT: {
|
|
25431
25900
|
from: EDGE_RULES["ANCHORED_AT"]?.from,
|
|
@@ -25569,6 +26038,9 @@ CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_E
|
|
|
25569
26038
|
DROP INDEX IF EXISTS nodes_live_fingerprint;
|
|
25570
26039
|
DROP INDEX IF EXISTS nodes_instance_fingerprint;
|
|
25571
26040
|
CREATE INDEX IF NOT EXISTS nodes_instance_fingerprint_v2 ON nodes(label, last_updated_at, last_reinforced_at_seq, created_at_seq) WHERE valid_to IS NULL AND label IN ('Problem','Vulnerability','Solution','RootCause');
|
|
26041
|
+
-- Calibrate rank from a covering scalar index; the global page-rank index
|
|
26042
|
+
-- requires a table lookup per node to exclude frozen versions.
|
|
26043
|
+
CREATE INDEX IF NOT EXISTS nodes_live_page_rank ON nodes(valid_to, page_rank);
|
|
25572
26044
|
CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
|
|
25573
26045
|
CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
|
|
25574
26046
|
-- Expression index on the per-node relPath (stored in attrs_json) so the
|
|
@@ -25581,6 +26053,12 @@ CREATE INDEX IF NOT EXISTS nodes_live_description ON nodes(description, label) W
|
|
|
25581
26053
|
-- Problems by the file their anchor hint names: the harvest upgrades hints for
|
|
25582
26054
|
-- every edited file of every turn, and a scan parses every Problem's attrs.
|
|
25583
26055
|
CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_json, '$.anchorHint.path')) WHERE valid_to IS NULL;
|
|
26056
|
+
-- Problems by the command / checker / file that opened them. The generalizer
|
|
26057
|
+
-- looked these up per shell or tool EVENT by hydrating every Problem: ~12s of
|
|
26058
|
+
-- every minute of daemon main-thread time on a 3.4k-Problem store (2026-10-05).
|
|
26059
|
+
CREATE INDEX IF NOT EXISTS nodes_command_signature ON nodes(json_extract(attrs_json, '$.commandSignature')) WHERE valid_to IS NULL;
|
|
26060
|
+
CREATE INDEX IF NOT EXISTS nodes_diag_command ON nodes(json_extract(attrs_json, '$.diagCommand')) WHERE valid_to IS NULL;
|
|
26061
|
+
CREATE INDEX IF NOT EXISTS nodes_lsp_rel_path ON nodes(json_extract(attrs_json, '$.lspRelPath')) WHERE valid_to IS NULL;
|
|
25584
26062
|
-- Nodes still waiting on a root-cause ask. The MCP server checks this on EVERY
|
|
25585
26063
|
-- tool response (the enrichment piggyback), so it must not parse every
|
|
25586
26064
|
-- Solution's and Problem's attrs per call. Not partial: the Problem pull reads
|
|
@@ -25667,6 +26145,7 @@ var SqliteGraphStore = class {
|
|
|
25667
26145
|
* ALL upserts incl. code reindex) — this counter is the knowledge-arrival axis
|
|
25668
26146
|
* Δseq is measured over, so code reindex churn must NOT advance it. */
|
|
25669
26147
|
ingestSeq = 0;
|
|
26148
|
+
initialPageRankScale;
|
|
25670
26149
|
constructor(opts) {
|
|
25671
26150
|
this.db = openDatabase(opts.path);
|
|
25672
26151
|
this.db.exec(SCHEMA_SQL);
|
|
@@ -25683,6 +26162,7 @@ var SqliteGraphStore = class {
|
|
|
25683
26162
|
this.db.exec(TEMPORAL_INDEX_SQL);
|
|
25684
26163
|
installHistoryCacheFloor(this.db);
|
|
25685
26164
|
installEditPlaceholderRetirement(this.db);
|
|
26165
|
+
installCloudAttrCompaction(this.db);
|
|
25686
26166
|
if (!this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(SESSION_REPAIR_KEY)) {
|
|
25687
26167
|
this.db.transaction(() => {
|
|
25688
26168
|
this.repairSessionAliases();
|
|
@@ -26180,1999 +26660,1613 @@ var SqliteGraphStore = class {
|
|
|
26180
26660
|
* read attrs and never the vector. `findNodesByLabel` decodes every embedding
|
|
26181
26661
|
* blob; the liveness watch's coverage pass spent 3.4s of a 4.3s main-thread
|
|
26182
26662
|
* hold doing exactly that on a 227k-node store (2026-09-24). */
|
|
26183
|
-
findNodeAttrsByLabel(label) {
|
|
26184
|
-
const rows = this.stmts.attrsByLabel.all(label);
|
|
26185
|
-
return rows.map((r) => ({
|
|
26186
|
-
id: r.id,
|
|
26187
|
-
createdAt: r.created_at,
|
|
26188
|
-
...r.created_at_seq != null ? { createdAtSeq: r.created_at_seq } : {},
|
|
26189
|
-
attrs: JSON.parse(r.attrs_json)
|
|
26190
|
-
}));
|
|
26191
|
-
}
|
|
26192
|
-
*iterateNodeIdentitiesByLabel(label, pageSize = 2e3) {
|
|
26193
|
-
const page = this.db.prepare(
|
|
26194
|
-
"SELECT rowid AS rid, id, json_extract(attrs_json, '$.relPath') AS relPath, json_extract(attrs_json, '$.qname') AS qname FROM nodes WHERE rowid > ? AND label = ? AND valid_to IS NULL ORDER BY rowid LIMIT ?"
|
|
26195
|
-
);
|
|
26196
|
-
let after = 0;
|
|
26197
|
-
for (; ; ) {
|
|
26198
|
-
const rows = page.all(after, label, pageSize);
|
|
26199
|
-
for (const r of rows) {
|
|
26200
|
-
yield {
|
|
26201
|
-
id: r.id,
|
|
26202
|
-
relPath: typeof r.relPath === "string" ? r.relPath : null,
|
|
26203
|
-
qname: typeof r.qname === "string" ? r.qname : null
|
|
26204
|
-
};
|
|
26205
|
-
}
|
|
26206
|
-
if (rows.length < pageSize) return;
|
|
26207
|
-
after = rows[rows.length - 1].rid;
|
|
26208
|
-
}
|
|
26209
|
-
}
|
|
26210
|
-
recentNodeIds(label, limit) {
|
|
26211
|
-
const rows = this.db.prepare("SELECT id FROM nodes WHERE label = ? AND valid_to IS NULL ORDER BY last_updated_at DESC LIMIT ?").all(label, Math.max(0, limit));
|
|
26212
|
-
return rows.map((r) => r.id);
|
|
26213
|
-
}
|
|
26214
|
-
recentOpenProblemCandidates(opts) {
|
|
26215
|
-
if (!Number.isSafeInteger(opts.limit) || opts.limit < 0) {
|
|
26216
|
-
throw new RangeError("candidate limit must be a non-negative safe integer");
|
|
26217
|
-
}
|
|
26218
|
-
return this.db.prepare(`SELECT id, description FROM nodes
|
|
26219
|
-
WHERE label = 'Problem' AND valid_to IS NULL
|
|
26220
|
-
AND json_extract(attrs_json, '$.resolvedAt') IS NULL
|
|
26221
|
-
AND json_type(attrs_json, '$.mergedInto') IS NULL
|
|
26222
|
-
AND (? IS NULL OR id != ?)
|
|
26223
|
-
AND (json_type(attrs_json, '$.workspaceId') IS NULL OR json_extract(attrs_json, '$.workspaceId') = ?)
|
|
26224
|
-
AND COALESCE(json_extract(attrs_json, '$.kind') = 'constraint', 0) = ?
|
|
26225
|
-
ORDER BY last_updated_at DESC, rowid ASC LIMIT ?`).all(
|
|
26226
|
-
opts.excludeId ?? null,
|
|
26227
|
-
opts.excludeId ?? null,
|
|
26228
|
-
opts.workspaceId,
|
|
26229
|
-
opts.constraint ? 1 : 0,
|
|
26230
|
-
opts.limit
|
|
26231
|
-
);
|
|
26232
|
-
}
|
|
26233
|
-
hasLiveNodes() {
|
|
26234
|
-
const r = this.db.prepare("SELECT EXISTS(SELECT 1 FROM nodes WHERE valid_to IS NULL) AS e").get();
|
|
26235
|
-
return r.e === 1;
|
|
26236
|
-
}
|
|
26237
|
-
findNodeIdentitiesByLabel(label) {
|
|
26238
|
-
const rows = this.stmts.identitiesByLabel.all(label);
|
|
26239
|
-
return rows.map((r) => ({
|
|
26240
|
-
id: r.id,
|
|
26241
|
-
relPath: typeof r.relPath === "string" ? r.relPath : null,
|
|
26242
|
-
qname: typeof r.qname === "string" ? r.qname : null
|
|
26243
|
-
}));
|
|
26244
|
-
}
|
|
26245
|
-
findAllVersionsByLabel(label, attribute) {
|
|
26246
|
-
if (attribute && !/^[A-Za-z0-9_]+(\.[A-Za-z0-9_]+)*$/.test(attribute.path)) {
|
|
26247
|
-
throw new Error(`invalid attr path: ${attribute.path}`);
|
|
26248
|
-
}
|
|
26249
|
-
const rows = attribute ? this.db.prepare(`SELECT * FROM nodes WHERE json_extract(attrs_json, '$.${attribute.path}') = ? AND label = ?`).all(attribute.value, label) : this.stmts.findByLabelAll.all(label);
|
|
26250
|
-
return rows.map(rowToNode);
|
|
26251
|
-
}
|
|
26252
|
-
searchNodes(opts) {
|
|
26253
|
-
const where = [];
|
|
26254
|
-
const params = {};
|
|
26255
|
-
if (opts.labels && opts.labels.length > 0) {
|
|
26256
|
-
const placeholders = opts.labels.map((_, i2) => `:label${i2}`).join(", ");
|
|
26257
|
-
where.push(`label IN (${placeholders})`);
|
|
26258
|
-
opts.labels.forEach((l, i2) => {
|
|
26259
|
-
params[`label${i2}`] = l;
|
|
26260
|
-
});
|
|
26261
|
-
}
|
|
26262
|
-
if (opts.descriptionLike) {
|
|
26263
|
-
where.push("description LIKE :descPat");
|
|
26264
|
-
params["descPat"] = `%${opts.descriptionLike}%`;
|
|
26265
|
-
}
|
|
26266
|
-
where.push("valid_to IS NULL");
|
|
26267
|
-
const limit = Math.max(1, opts.limit ?? 50);
|
|
26268
|
-
params["limit"] = limit;
|
|
26269
|
-
const whereSql = where.length > 0 ? "WHERE " + where.join(" AND ") : "";
|
|
26270
|
-
const sql = `SELECT * FROM nodes ${whereSql} ORDER BY page_rank DESC, last_updated_at DESC LIMIT :limit`;
|
|
26271
|
-
const rows = this.db.prepare(sql).all(params);
|
|
26272
|
-
return rows.map(rowToNode);
|
|
26273
|
-
}
|
|
26274
|
-
searchDescriptionTerms(terms, limit) {
|
|
26275
|
-
if (terms.length === 0) return [];
|
|
26276
|
-
const score2 = terms.map(() => "(description LIKE ?)").join(" + ");
|
|
26277
|
-
const rows = this.db.prepare(
|
|
26278
|
-
`SELECT n.*, h.m AS matched
|
|
26279
|
-
FROM (SELECT rowid AS rid, ${score2} AS m
|
|
26280
|
-
FROM nodes INDEXED BY nodes_live_description
|
|
26281
|
-
WHERE valid_to IS NULL) h
|
|
26282
|
-
JOIN nodes n ON n.rowid = h.rid
|
|
26283
|
-
WHERE h.m > 0
|
|
26284
|
-
ORDER BY h.m DESC, n.page_rank DESC, n.last_updated_at DESC
|
|
26285
|
-
LIMIT ?`
|
|
26286
|
-
).all(...terms.map((t) => `%${t}%`), Math.max(1, limit));
|
|
26287
|
-
return rows.map((r) => ({ node: rowToNode(r), matched: Number(r.matched) }));
|
|
26288
|
-
}
|
|
26289
|
-
typedEdgeStmt(sql) {
|
|
26290
|
-
let stmt = this.typedEdgeStmts.get(sql);
|
|
26291
|
-
if (!stmt) {
|
|
26292
|
-
stmt = this.db.prepare(sql);
|
|
26293
|
-
this.typedEdgeStmts.set(sql, stmt);
|
|
26294
|
-
}
|
|
26295
|
-
return stmt;
|
|
26296
|
-
}
|
|
26297
|
-
outEdges(nodeId, types) {
|
|
26298
|
-
if (!types || types.length === 0) {
|
|
26299
|
-
const rows2 = this.stmts.outEdges.all(nodeId);
|
|
26300
|
-
return rows2.map(rowToEdge);
|
|
26301
|
-
}
|
|
26302
|
-
const placeholders = types.map((_, i2) => `:t${i2}`).join(",");
|
|
26303
|
-
const params = { from_id: nodeId };
|
|
26304
|
-
types.forEach((t, i2) => {
|
|
26305
|
-
params[`t${i2}`] = t;
|
|
26306
|
-
});
|
|
26307
|
-
const rows = this.typedEdgeStmt(
|
|
26308
|
-
`SELECT * FROM edges WHERE from_id = :from_id AND type IN (${placeholders}) AND valid_to IS NULL`
|
|
26309
|
-
).all(params);
|
|
26310
|
-
return rows.map(rowToEdge);
|
|
26311
|
-
}
|
|
26312
|
-
inEdges(nodeId, types) {
|
|
26313
|
-
if (!types || types.length === 0) {
|
|
26314
|
-
const rows2 = this.stmts.inEdges.all(nodeId);
|
|
26315
|
-
return rows2.map(rowToEdge);
|
|
26316
|
-
}
|
|
26317
|
-
const placeholders = types.map((_, i2) => `:t${i2}`).join(",");
|
|
26318
|
-
const params = { to_id: nodeId };
|
|
26319
|
-
types.forEach((t, i2) => {
|
|
26320
|
-
params[`t${i2}`] = t;
|
|
26321
|
-
});
|
|
26322
|
-
const rows = this.typedEdgeStmt(
|
|
26323
|
-
`SELECT * FROM edges WHERE to_id = :to_id AND type IN (${placeholders}) AND valid_to IS NULL`
|
|
26324
|
-
).all(params);
|
|
26325
|
-
return rows.map(rowToEdge);
|
|
26326
|
-
}
|
|
26327
|
-
/** Live in-degree per target over `types`, in one grouped query — the counts
|
|
26328
|
-
* `inEdges(id, types).length` gives, for every node at once. A per-node
|
|
26329
|
-
* inEdges loop over ~48k code nodes held a PreToolUse hook 12.6s when the
|
|
26330
|
-
* hotspots vfile materialized (2026-09-24). */
|
|
26331
|
-
inDegreeByType(types) {
|
|
26332
|
-
if (types.length === 0) return /* @__PURE__ */ new Map();
|
|
26333
|
-
const placeholders = types.map(() => "?").join(",");
|
|
26334
|
-
const rows = this.db.prepare(
|
|
26335
|
-
`SELECT to_id, COUNT(*) AS n FROM edges WHERE type IN (${placeholders}) AND valid_to IS NULL GROUP BY to_id`
|
|
26336
|
-
).all(...types);
|
|
26337
|
-
return new Map(rows.map((r) => [r.to_id, Number(r.n)]));
|
|
26338
|
-
}
|
|
26339
|
-
neighbors(nodeId, types) {
|
|
26340
|
-
const out2 = [];
|
|
26341
|
-
const edges = this.outEdges(nodeId, types);
|
|
26342
|
-
for (const e of edges) {
|
|
26343
|
-
const n = this.getNode(e.to);
|
|
26344
|
-
if (n) out2.push({ edgeType: e.type, confidence: e.confidence, node: n });
|
|
26345
|
-
}
|
|
26346
|
-
const inE = this.inEdges(nodeId, types);
|
|
26347
|
-
for (const e of inE) {
|
|
26348
|
-
const n = this.getNode(e.from);
|
|
26349
|
-
if (n) out2.push({ edgeType: e.type, confidence: e.confidence, node: n });
|
|
26350
|
-
}
|
|
26351
|
-
return out2;
|
|
26352
|
-
}
|
|
26353
|
-
nodeCount() {
|
|
26354
|
-
return Number(this.stmts.nodeCount.get().n);
|
|
26355
|
-
}
|
|
26356
|
-
mutationCount() {
|
|
26357
|
-
return this.mutations;
|
|
26358
|
-
}
|
|
26359
|
-
currentIngestSeq() {
|
|
26360
|
-
return this.ingestSeq;
|
|
26361
|
-
}
|
|
26362
|
-
nextIngestSeq() {
|
|
26363
|
-
this.ingestSeq += 1;
|
|
26364
|
-
this.stmts.setIngestSeq.run({ v: String(this.ingestSeq) });
|
|
26365
|
-
return this.ingestSeq;
|
|
26366
|
-
}
|
|
26367
|
-
edgeCount() {
|
|
26368
|
-
return Number(this.stmts.edgeCount.get().n);
|
|
26369
|
-
}
|
|
26370
|
-
// Lightweight whole-graph scans — select only the columns the caller needs so a
|
|
26371
|
-
// 100k-node pass skips the per-row attrs JSON.parse + embedding decode rowToNode
|
|
26372
|
-
// does. Live rows only (valid_to IS NULL), matching findNodesByLabel/outEdges.
|
|
26373
|
-
scanLiveNodes() {
|
|
26374
|
-
const rows = this.db.prepare("SELECT id, label, memory_tier, page_rank, is_landmark FROM nodes WHERE valid_to IS NULL").all();
|
|
26663
|
+
findNodeAttrsByLabel(label) {
|
|
26664
|
+
const rows = this.stmts.attrsByLabel.all(label);
|
|
26375
26665
|
return rows.map((r) => ({
|
|
26376
26666
|
id: r.id,
|
|
26377
|
-
|
|
26378
|
-
|
|
26379
|
-
|
|
26380
|
-
isLandmark: r.is_landmark === 1
|
|
26667
|
+
createdAt: r.created_at,
|
|
26668
|
+
...r.created_at_seq != null ? { createdAtSeq: r.created_at_seq } : {},
|
|
26669
|
+
attrs: JSON.parse(r.attrs_json)
|
|
26381
26670
|
}));
|
|
26382
26671
|
}
|
|
26383
|
-
|
|
26384
|
-
const
|
|
26385
|
-
|
|
26386
|
-
|
|
26387
|
-
|
|
26388
|
-
|
|
26389
|
-
|
|
26390
|
-
|
|
26391
|
-
|
|
26392
|
-
|
|
26393
|
-
|
|
26394
|
-
|
|
26672
|
+
*iterateNodeIdentitiesByLabel(label, pageSize = 2e3) {
|
|
26673
|
+
const page = this.db.prepare(
|
|
26674
|
+
"SELECT rowid AS rid, id, json_extract(attrs_json, '$.relPath') AS relPath, json_extract(attrs_json, '$.qname') AS qname FROM nodes WHERE rowid > ? AND label = ? AND valid_to IS NULL ORDER BY rowid LIMIT ?"
|
|
26675
|
+
);
|
|
26676
|
+
let after = 0;
|
|
26677
|
+
for (; ; ) {
|
|
26678
|
+
const rows = page.all(after, label, pageSize);
|
|
26679
|
+
for (const r of rows) {
|
|
26680
|
+
yield {
|
|
26681
|
+
id: r.id,
|
|
26682
|
+
relPath: typeof r.relPath === "string" ? r.relPath : null,
|
|
26683
|
+
qname: typeof r.qname === "string" ? r.qname : null
|
|
26684
|
+
};
|
|
26685
|
+
}
|
|
26686
|
+
if (rows.length < pageSize) return;
|
|
26687
|
+
after = rows[rows.length - 1].rid;
|
|
26688
|
+
}
|
|
26395
26689
|
}
|
|
26396
|
-
|
|
26397
|
-
const rows = this.db.prepare("SELECT id FROM nodes WHERE valid_to IS NULL
|
|
26690
|
+
recentNodeIds(label, limit) {
|
|
26691
|
+
const rows = this.db.prepare("SELECT id FROM nodes WHERE label = ? AND valid_to IS NULL ORDER BY last_updated_at DESC LIMIT ?").all(label, Math.max(0, limit));
|
|
26398
26692
|
return rows.map((r) => r.id);
|
|
26399
26693
|
}
|
|
26400
|
-
|
|
26401
|
-
if (
|
|
26402
|
-
|
|
26403
|
-
const r2 = this.db.prepare(
|
|
26404
|
-
`SELECT COUNT(*) AS n, MAX(last_updated_at) AS t,
|
|
26405
|
-
MAX(COALESCE(last_reinforced_at_seq, created_at_seq, 0)) AS s
|
|
26406
|
-
FROM nodes INDEXED BY nodes_instance_fingerprint_v2
|
|
26407
|
-
WHERE valid_to IS NULL AND label IN ('Problem','Vulnerability','Solution','RootCause')`
|
|
26408
|
-
).get();
|
|
26409
|
-
return `${r2.n}:${r2.t ?? 0}:${r2.s ?? 0}`;
|
|
26694
|
+
recentOpenProblemCandidates(opts) {
|
|
26695
|
+
if (!Number.isSafeInteger(opts.limit) || opts.limit < 0) {
|
|
26696
|
+
throw new RangeError("candidate limit must be a non-negative safe integer");
|
|
26410
26697
|
}
|
|
26411
|
-
|
|
26412
|
-
|
|
26413
|
-
|
|
26414
|
-
|
|
26415
|
-
|
|
26416
|
-
|
|
26417
|
-
|
|
26418
|
-
|
|
26419
|
-
|
|
26420
|
-
|
|
26421
|
-
|
|
26422
|
-
|
|
26423
|
-
|
|
26424
|
-
|
|
26425
|
-
WHERE (valid_to IS NULL AND (created_at > :ts OR last_seen_at > :ts))
|
|
26426
|
-
OR (valid_to IS NOT NULL AND valid_to > :ts)`
|
|
26427
|
-
).all({ ts });
|
|
26428
|
-
return rows.map((r) => ({ from: r.from_id, to: r.to_id }));
|
|
26429
|
-
}
|
|
26430
|
-
/** Lightweight out-edges for a SET of sources, chunked IN-lists — the region
|
|
26431
|
-
* assembly path for incremental PageRank (per-node outEdges() at region
|
|
26432
|
-
* scale re-creates the 106k-prepared-calls problem the batch scan solved). */
|
|
26433
|
-
outEdgesForMany(ids) {
|
|
26434
|
-
return this.edgesForMany(ids, "from_id");
|
|
26435
|
-
}
|
|
26436
|
-
inEdgesForMany(ids) {
|
|
26437
|
-
return this.edgesForMany(ids, "to_id");
|
|
26698
|
+
return this.db.prepare(`SELECT id, description FROM nodes
|
|
26699
|
+
WHERE label = 'Problem' AND valid_to IS NULL
|
|
26700
|
+
AND json_extract(attrs_json, '$.resolvedAt') IS NULL
|
|
26701
|
+
AND json_type(attrs_json, '$.mergedInto') IS NULL
|
|
26702
|
+
AND (? IS NULL OR id != ?)
|
|
26703
|
+
AND (json_type(attrs_json, '$.workspaceId') IS NULL OR json_extract(attrs_json, '$.workspaceId') = ?)
|
|
26704
|
+
AND COALESCE(json_extract(attrs_json, '$.kind') = 'constraint', 0) = ?
|
|
26705
|
+
ORDER BY last_updated_at DESC, rowid ASC LIMIT ?`).all(
|
|
26706
|
+
opts.excludeId ?? null,
|
|
26707
|
+
opts.excludeId ?? null,
|
|
26708
|
+
opts.workspaceId,
|
|
26709
|
+
opts.constraint ? 1 : 0,
|
|
26710
|
+
opts.limit
|
|
26711
|
+
);
|
|
26438
26712
|
}
|
|
26439
|
-
|
|
26440
|
-
|
|
26441
|
-
|
|
26442
|
-
const out2 = /* @__PURE__ */ new Map();
|
|
26443
|
-
const CHUNK = 400;
|
|
26444
|
-
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
26445
|
-
const chunk = ids.slice(i2, i2 + CHUNK);
|
|
26446
|
-
const placeholders = chunk.map(() => "?").join(", ");
|
|
26447
|
-
const rows = this.db.prepare(
|
|
26448
|
-
`SELECT id, page_rank FROM nodes WHERE id IN (${placeholders}) AND valid_to IS NULL`
|
|
26449
|
-
).all(...chunk);
|
|
26450
|
-
for (const r of rows) out2.set(r.id, r.page_rank);
|
|
26451
|
-
}
|
|
26452
|
-
return out2;
|
|
26713
|
+
hasLiveNodes() {
|
|
26714
|
+
const r = this.db.prepare("SELECT EXISTS(SELECT 1 FROM nodes WHERE valid_to IS NULL) AS e").get();
|
|
26715
|
+
return r.e === 1;
|
|
26453
26716
|
}
|
|
26454
|
-
|
|
26455
|
-
|
|
26456
|
-
* persistent-tier node (force-landmarks). A few thousand rows, so the
|
|
26457
|
-
* incremental path can refresh the GLOBAL landmark set without the 179k-row
|
|
26458
|
-
* scanLiveNodes materialization. */
|
|
26459
|
-
landmarkSweepRows() {
|
|
26460
|
-
const rows = this.db.prepare(
|
|
26461
|
-
`SELECT id, label, page_rank, is_landmark, memory_tier, extraction_source FROM nodes
|
|
26462
|
-
WHERE valid_to IS NULL
|
|
26463
|
-
AND (memory_tier = 'persistent' OR is_landmark = 1 OR label IN ('Pattern', 'RootCause'))`
|
|
26464
|
-
).all();
|
|
26717
|
+
findNodeIdentitiesByLabel(label) {
|
|
26718
|
+
const rows = this.stmts.identitiesByLabel.all(label);
|
|
26465
26719
|
return rows.map((r) => ({
|
|
26466
26720
|
id: r.id,
|
|
26467
|
-
|
|
26468
|
-
|
|
26469
|
-
isLandmark: r.is_landmark === 1,
|
|
26470
|
-
memoryTier: r.memory_tier,
|
|
26471
|
-
extractionSource: r.extraction_source
|
|
26721
|
+
relPath: typeof r.relPath === "string" ? r.relPath : null,
|
|
26722
|
+
qname: typeof r.qname === "string" ? r.qname : null
|
|
26472
26723
|
}));
|
|
26473
26724
|
}
|
|
26474
|
-
|
|
26475
|
-
|
|
26476
|
-
|
|
26477
|
-
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
26478
|
-
const chunk = ids.slice(i2, i2 + CHUNK);
|
|
26479
|
-
const placeholders = chunk.map(() => "?").join(", ");
|
|
26480
|
-
const rows = this.db.prepare(
|
|
26481
|
-
`SELECT from_id, to_id, type FROM edges WHERE ${col} IN (${placeholders}) AND valid_to IS NULL`
|
|
26482
|
-
).all(...chunk);
|
|
26483
|
-
for (const r of rows) out2.push({ from: r.from_id, to: r.to_id, type: r.type });
|
|
26725
|
+
findAllVersionsByLabel(label, attribute) {
|
|
26726
|
+
if (attribute && !/^[A-Za-z0-9_]+(\.[A-Za-z0-9_]+)*$/.test(attribute.path)) {
|
|
26727
|
+
throw new Error(`invalid attr path: ${attribute.path}`);
|
|
26484
26728
|
}
|
|
26485
|
-
|
|
26729
|
+
const rows = attribute ? this.db.prepare(`SELECT * FROM nodes WHERE json_extract(attrs_json, '$.${attribute.path}') = ? AND label = ?`).all(attribute.value, label) : this.stmts.findByLabelAll.all(label);
|
|
26730
|
+
return rows.map(rowToNode);
|
|
26486
26731
|
}
|
|
26487
|
-
|
|
26488
|
-
|
|
26489
|
-
|
|
26490
|
-
|
|
26491
|
-
|
|
26492
|
-
|
|
26732
|
+
searchNodes(opts) {
|
|
26733
|
+
const where = [];
|
|
26734
|
+
const params = {};
|
|
26735
|
+
if (opts.labels && opts.labels.length > 0) {
|
|
26736
|
+
const placeholders = opts.labels.map((_, i2) => `:label${i2}`).join(", ");
|
|
26737
|
+
where.push(`label IN (${placeholders})`);
|
|
26738
|
+
opts.labels.forEach((l, i2) => {
|
|
26739
|
+
params[`label${i2}`] = l;
|
|
26740
|
+
});
|
|
26493
26741
|
}
|
|
26494
|
-
|
|
26495
|
-
|
|
26496
|
-
|
|
26497
|
-
const CHUNK = 400;
|
|
26498
|
-
for (let i2 = 0; i2 < relPaths.length; i2 += CHUNK) {
|
|
26499
|
-
const slice = relPaths.slice(i2, i2 + CHUNK);
|
|
26500
|
-
if (slice.length === 0) continue;
|
|
26501
|
-
const r = this.db.prepare(
|
|
26502
|
-
`UPDATE nodes SET valid_to = NULL
|
|
26503
|
-
WHERE valid_to IS NOT NULL
|
|
26504
|
-
AND last_updated_at >= ?
|
|
26505
|
-
AND json_extract(attrs_json, '$.workspaceId') = ?
|
|
26506
|
-
AND json_extract(attrs_json, '$.relPath') IN (${slice.map(() => "?").join(",")})`
|
|
26507
|
-
).run(since, workspaceId, ...slice);
|
|
26508
|
-
revived += Number(r.changes);
|
|
26742
|
+
if (opts.descriptionLike) {
|
|
26743
|
+
where.push("description LIKE :descPat");
|
|
26744
|
+
params["descPat"] = `%${opts.descriptionLike}%`;
|
|
26509
26745
|
}
|
|
26510
|
-
|
|
26511
|
-
|
|
26746
|
+
where.push("valid_to IS NULL");
|
|
26747
|
+
const limit = Math.max(1, opts.limit ?? 50);
|
|
26748
|
+
params["limit"] = limit;
|
|
26749
|
+
const whereSql = where.length > 0 ? "WHERE " + where.join(" AND ") : "";
|
|
26750
|
+
const sql = `SELECT * FROM nodes ${whereSql} ORDER BY page_rank DESC, last_updated_at DESC LIMIT :limit`;
|
|
26751
|
+
const rows = this.db.prepare(sql).all(params);
|
|
26752
|
+
return rows.map(rowToNode);
|
|
26512
26753
|
}
|
|
26513
|
-
|
|
26514
|
-
|
|
26515
|
-
|
|
26516
|
-
|
|
26517
|
-
|
|
26518
|
-
|
|
26519
|
-
|
|
26520
|
-
|
|
26521
|
-
|
|
26754
|
+
searchDescriptionTerms(terms, limit) {
|
|
26755
|
+
if (terms.length === 0) return [];
|
|
26756
|
+
const score2 = terms.map(() => "(description LIKE ?)").join(" + ");
|
|
26757
|
+
const rows = this.db.prepare(
|
|
26758
|
+
`SELECT n.*, h.m AS matched
|
|
26759
|
+
FROM (SELECT rowid AS rid, ${score2} AS m
|
|
26760
|
+
FROM nodes INDEXED BY nodes_live_description
|
|
26761
|
+
WHERE valid_to IS NULL) h
|
|
26762
|
+
JOIN nodes n ON n.rowid = h.rid
|
|
26763
|
+
WHERE h.m > 0
|
|
26764
|
+
ORDER BY h.m DESC, n.page_rank DESC, n.last_updated_at DESC
|
|
26765
|
+
LIMIT ?`
|
|
26766
|
+
).all(...terms.map((t) => `%${t}%`), Math.max(1, limit));
|
|
26767
|
+
return rows.map((r) => ({ node: rowToNode(r), matched: Number(r.matched) }));
|
|
26522
26768
|
}
|
|
26523
|
-
|
|
26524
|
-
|
|
26525
|
-
const
|
|
26526
|
-
|
|
26527
|
-
|
|
26528
|
-
|
|
26529
|
-
|
|
26530
|
-
|
|
26531
|
-
|
|
26532
|
-
|
|
26533
|
-
|
|
26534
|
-
|
|
26769
|
+
searchTextCandidates(terms) {
|
|
26770
|
+
if (terms.length === 0) return [];
|
|
26771
|
+
const rows = this.db.prepare(`WITH matches AS MATERIALIZED (
|
|
26772
|
+
SELECT n.id, COUNT(*) AS matched FROM nodes n INDEXED BY nodes_live_description, json_each(:terms) t
|
|
26773
|
+
WHERE n.valid_to IS NULL AND n.description LIKE '%' || t.value || '%'
|
|
26774
|
+
GROUP BY n.id
|
|
26775
|
+
) SELECT ${TEXT_SEARCH_COLUMNS}, matches.matched FROM nodes
|
|
26776
|
+
JOIN matches USING (id) ORDER BY page_rank DESC, last_updated_at DESC`).all({ terms: JSON.stringify(terms) });
|
|
26777
|
+
return rows.map((row) => ({ node: textSearchNode(row), matched: row.matched }));
|
|
26778
|
+
}
|
|
26779
|
+
getTextSearchNode(id) {
|
|
26780
|
+
const row = this.db.prepare(`SELECT ${TEXT_SEARCH_COLUMNS} FROM nodes WHERE id = ? AND valid_to IS NULL`).get(id);
|
|
26781
|
+
return row ? textSearchNode(row) : null;
|
|
26782
|
+
}
|
|
26783
|
+
readPageRankScale() {
|
|
26784
|
+
const rows = this.db.prepare("SELECT page_rank FROM nodes WHERE valid_to IS NULL AND page_rank > 0").all();
|
|
26785
|
+
return pageRankScale(rows.map((row) => row.page_rank));
|
|
26786
|
+
}
|
|
26787
|
+
pageRankScale() {
|
|
26788
|
+
const value = Number(this.getMeta(PAGE_RANK_SCALE_KEY));
|
|
26789
|
+
if (Number.isFinite(value) && value > 0) return value;
|
|
26790
|
+
return this.initialPageRankScale ??= this.readPageRankScale();
|
|
26791
|
+
}
|
|
26792
|
+
refreshPageRankScale() {
|
|
26793
|
+
const scale = this.readPageRankScale();
|
|
26794
|
+
this.setMeta(PAGE_RANK_SCALE_KEY, String(scale));
|
|
26795
|
+
return scale;
|
|
26535
26796
|
}
|
|
26536
|
-
|
|
26537
|
-
|
|
26538
|
-
|
|
26539
|
-
|
|
26540
|
-
|
|
26541
|
-
|
|
26542
|
-
|
|
26543
|
-
* query over an index-covered scan. Labels only; nothing here touches embeddings.
|
|
26544
|
-
*/
|
|
26545
|
-
scanLiveEdgeRows() {
|
|
26546
|
-
const rows = this.db.prepare(
|
|
26547
|
-
`SELECT e.id, e.from_id, e.to_id, e.type, a.label AS from_label, b.label AS to_label
|
|
26548
|
-
FROM edges e
|
|
26549
|
-
LEFT JOIN nodes a ON a.id = e.from_id AND a.valid_to IS NULL
|
|
26550
|
-
LEFT JOIN nodes b ON b.id = e.to_id AND b.valid_to IS NULL
|
|
26551
|
-
WHERE e.valid_to IS NULL`
|
|
26552
|
-
).all();
|
|
26553
|
-
return rows.map((r) => ({
|
|
26554
|
-
id: r.id,
|
|
26555
|
-
from: r.from_id,
|
|
26556
|
-
to: r.to_id,
|
|
26557
|
-
type: r.type,
|
|
26558
|
-
fromLabel: r.from_label ?? void 0,
|
|
26559
|
-
toLabel: r.to_label ?? void 0
|
|
26560
|
-
}));
|
|
26797
|
+
typedEdgeStmt(sql) {
|
|
26798
|
+
let stmt = this.typedEdgeStmts.get(sql);
|
|
26799
|
+
if (!stmt) {
|
|
26800
|
+
stmt = this.db.prepare(sql);
|
|
26801
|
+
this.typedEdgeStmts.set(sql, stmt);
|
|
26802
|
+
}
|
|
26803
|
+
return stmt;
|
|
26561
26804
|
}
|
|
26562
|
-
|
|
26563
|
-
|
|
26564
|
-
|
|
26565
|
-
|
|
26566
|
-
* the nodes_live_description index. Chunked under the bound-parameter cap. */
|
|
26567
|
-
liveNodesByDescription(names, labels) {
|
|
26568
|
-
if (names.length === 0 || labels.length === 0) return [];
|
|
26569
|
-
const out2 = [];
|
|
26570
|
-
const labelPh = labels.map(() => "?").join(",");
|
|
26571
|
-
for (let i2 = 0; i2 < names.length; i2 += 400) {
|
|
26572
|
-
const chunk = names.slice(i2, i2 + 400);
|
|
26573
|
-
const rows = this.db.prepare(
|
|
26574
|
-
`SELECT * FROM nodes WHERE valid_to IS NULL AND description IN (${chunk.map(() => "?").join(",")}) AND label IN (${labelPh})`
|
|
26575
|
-
).all(...chunk, ...labels);
|
|
26576
|
-
for (const r of rows) out2.push(rowToNode(r));
|
|
26805
|
+
outEdges(nodeId, types) {
|
|
26806
|
+
if (!types || types.length === 0) {
|
|
26807
|
+
const rows2 = this.stmts.outEdges.all(nodeId);
|
|
26808
|
+
return rows2.map(rowToEdge);
|
|
26577
26809
|
}
|
|
26578
|
-
|
|
26810
|
+
const placeholders = types.map((_, i2) => `:t${i2}`).join(",");
|
|
26811
|
+
const params = { from_id: nodeId };
|
|
26812
|
+
types.forEach((t, i2) => {
|
|
26813
|
+
params[`t${i2}`] = t;
|
|
26814
|
+
});
|
|
26815
|
+
const rows = this.typedEdgeStmt(
|
|
26816
|
+
`SELECT * FROM edges WHERE from_id = :from_id AND type IN (${placeholders}) AND valid_to IS NULL`
|
|
26817
|
+
).all(params);
|
|
26818
|
+
return rows.map(rowToEdge);
|
|
26579
26819
|
}
|
|
26580
|
-
|
|
26581
|
-
if (
|
|
26582
|
-
|
|
26583
|
-
|
|
26584
|
-
|
|
26585
|
-
).
|
|
26586
|
-
|
|
26820
|
+
inEdges(nodeId, types) {
|
|
26821
|
+
if (!types || types.length === 0) {
|
|
26822
|
+
const rows2 = this.stmts.inEdges.all(nodeId);
|
|
26823
|
+
return rows2.map(rowToEdge);
|
|
26824
|
+
}
|
|
26825
|
+
const placeholders = types.map((_, i2) => `:t${i2}`).join(",");
|
|
26826
|
+
const params = { to_id: nodeId };
|
|
26827
|
+
types.forEach((t, i2) => {
|
|
26828
|
+
params[`t${i2}`] = t;
|
|
26829
|
+
});
|
|
26830
|
+
const rows = this.typedEdgeStmt(
|
|
26831
|
+
`SELECT * FROM edges WHERE to_id = :to_id AND type IN (${placeholders}) AND valid_to IS NULL`
|
|
26832
|
+
).all(params);
|
|
26833
|
+
return rows.map(rowToEdge);
|
|
26587
26834
|
}
|
|
26588
|
-
|
|
26589
|
-
|
|
26835
|
+
/** Live in-degree per target over `types`, in one grouped query — the counts
|
|
26836
|
+
* `inEdges(id, types).length` gives, for every node at once. A per-node
|
|
26837
|
+
* inEdges loop over ~48k code nodes held a PreToolUse hook 12.6s when the
|
|
26838
|
+
* hotspots vfile materialized (2026-09-24). */
|
|
26839
|
+
inDegreeByType(types) {
|
|
26840
|
+
if (types.length === 0) return /* @__PURE__ */ new Map();
|
|
26841
|
+
const placeholders = types.map(() => "?").join(",");
|
|
26590
26842
|
const rows = this.db.prepare(
|
|
26591
|
-
`SELECT * FROM
|
|
26592
|
-
).all(
|
|
26593
|
-
return rows.map(
|
|
26843
|
+
`SELECT to_id, COUNT(*) AS n FROM edges WHERE type IN (${placeholders}) AND valid_to IS NULL GROUP BY to_id`
|
|
26844
|
+
).all(...types);
|
|
26845
|
+
return new Map(rows.map((r) => [r.to_id, Number(r.n)]));
|
|
26594
26846
|
}
|
|
26595
|
-
|
|
26596
|
-
|
|
26597
|
-
|
|
26598
|
-
|
|
26599
|
-
|
|
26600
|
-
|
|
26601
|
-
if (!live) return null;
|
|
26602
|
-
const version3 = live.version ?? 1;
|
|
26603
|
-
const frozenId = `${liveId}@v${version3}`;
|
|
26604
|
-
if (this.getNode(frozenId)) {
|
|
26605
|
-
this.stmts.advanceLive.run({ live_id: liveId, t });
|
|
26606
|
-
return frozenId;
|
|
26847
|
+
neighbors(nodeId, types) {
|
|
26848
|
+
const out2 = [];
|
|
26849
|
+
const edges = this.outEdges(nodeId, types);
|
|
26850
|
+
for (const e of edges) {
|
|
26851
|
+
const n = this.getNode(e.to);
|
|
26852
|
+
if (n) out2.push({ edgeType: e.type, confidence: e.confidence, node: n });
|
|
26607
26853
|
}
|
|
26608
|
-
this.
|
|
26609
|
-
|
|
26610
|
-
|
|
26611
|
-
|
|
26612
|
-
|
|
26613
|
-
|
|
26614
|
-
confidence: 1,
|
|
26615
|
-
extractionSource: "daemon-extracted",
|
|
26616
|
-
createdAt: t,
|
|
26617
|
-
lastSeenAt: t,
|
|
26618
|
-
navSuccesses: 0,
|
|
26619
|
-
navFailures: 0,
|
|
26620
|
-
validFrom: t,
|
|
26621
|
-
validTo: null,
|
|
26622
|
-
attrs: {}
|
|
26623
|
-
});
|
|
26624
|
-
this.stmts.advanceLive.run({ live_id: liveId, t });
|
|
26625
|
-
return frozenId;
|
|
26854
|
+
const inE = this.inEdges(nodeId, types);
|
|
26855
|
+
for (const e of inE) {
|
|
26856
|
+
const n = this.getNode(e.from);
|
|
26857
|
+
if (n) out2.push({ edgeType: e.type, confidence: e.confidence, node: n });
|
|
26858
|
+
}
|
|
26859
|
+
return out2;
|
|
26626
26860
|
}
|
|
26627
|
-
|
|
26628
|
-
this.stmts.
|
|
26861
|
+
nodeCount() {
|
|
26862
|
+
return Number(this.stmts.nodeCount.get().n);
|
|
26629
26863
|
}
|
|
26630
|
-
|
|
26631
|
-
this.
|
|
26864
|
+
mutationCount() {
|
|
26865
|
+
return this.mutations;
|
|
26632
26866
|
}
|
|
26633
|
-
|
|
26634
|
-
|
|
26635
|
-
return rows.map(rowToNode);
|
|
26867
|
+
currentIngestSeq() {
|
|
26868
|
+
return this.ingestSeq;
|
|
26636
26869
|
}
|
|
26637
|
-
|
|
26638
|
-
|
|
26639
|
-
|
|
26870
|
+
nextIngestSeq() {
|
|
26871
|
+
this.ingestSeq += 1;
|
|
26872
|
+
this.stmts.setIngestSeq.run({ v: String(this.ingestSeq) });
|
|
26873
|
+
return this.ingestSeq;
|
|
26640
26874
|
}
|
|
26641
|
-
|
|
26642
|
-
|
|
26643
|
-
* the whole graph for a handful of revisit flags: 6.7s per context render on
|
|
26644
|
-
* a 227k-node store (2026-09-24). `attr` is interpolated into a JSON path,
|
|
26645
|
-
* so it must be a plain identifier. */
|
|
26646
|
-
nodesAsOfWhereTrue(t, attr) {
|
|
26647
|
-
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attr)) throw new Error(`nodesAsOfWhereTrue: bad attr ${JSON.stringify(attr)}`);
|
|
26648
|
-
const rows = this.db.prepare(
|
|
26649
|
-
`SELECT * FROM nodes WHERE valid_from <= :t AND (valid_to IS NULL OR valid_to > :t)
|
|
26650
|
-
AND json_type(attrs_json, '$.${attr}') = 'true'`
|
|
26651
|
-
).all({ t });
|
|
26652
|
-
return rows.map(rowToNode);
|
|
26875
|
+
edgeCount() {
|
|
26876
|
+
return Number(this.stmts.edgeCount.get().n);
|
|
26653
26877
|
}
|
|
26654
|
-
|
|
26655
|
-
|
|
26656
|
-
|
|
26878
|
+
// Lightweight whole-graph scans — select only the columns the caller needs so a
|
|
26879
|
+
// 100k-node pass skips the per-row attrs JSON.parse + embedding decode rowToNode
|
|
26880
|
+
// does. Live rows only (valid_to IS NULL), matching findNodesByLabel/outEdges.
|
|
26881
|
+
scanLiveNodes() {
|
|
26882
|
+
const rows = this.db.prepare("SELECT id, label, memory_tier, page_rank, is_landmark FROM nodes WHERE valid_to IS NULL").all();
|
|
26883
|
+
return rows.map((r) => ({
|
|
26884
|
+
id: r.id,
|
|
26885
|
+
label: r.label,
|
|
26886
|
+
memoryTier: r.memory_tier,
|
|
26887
|
+
pageRank: r.page_rank,
|
|
26888
|
+
isLandmark: r.is_landmark === 1
|
|
26889
|
+
}));
|
|
26657
26890
|
}
|
|
26658
|
-
|
|
26659
|
-
|
|
26891
|
+
scanLiveEdges() {
|
|
26892
|
+
const rows = this.db.prepare("SELECT from_id, to_id, type FROM edges WHERE valid_to IS NULL").all();
|
|
26893
|
+
return rows.map((r) => ({ from: r.from_id, to: r.to_id, type: r.type }));
|
|
26660
26894
|
}
|
|
26661
|
-
|
|
26662
|
-
this.db.
|
|
26895
|
+
getMeta(key) {
|
|
26896
|
+
const r = this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(key);
|
|
26897
|
+
return r?.value ?? null;
|
|
26663
26898
|
}
|
|
26664
|
-
|
|
26665
|
-
|
|
26666
|
-
|
|
26667
|
-
}
|
|
26668
|
-
|
|
26669
|
-
// ../../packages/local-graph/src/traversal.ts
|
|
26670
|
-
init_src2();
|
|
26671
|
-
var MAX_EDGE_WEIGHT = Math.max(...Object.values(EDGE_WEIGHT));
|
|
26672
|
-
var CAUSAL_FAMILY = [
|
|
26673
|
-
...CAUSAL_EDGES,
|
|
26674
|
-
...RESOLUTION_EDGES
|
|
26675
|
-
];
|
|
26676
|
-
|
|
26677
|
-
// ../../packages/local-graph/src/justification.ts
|
|
26678
|
-
init_src();
|
|
26679
|
-
init_src2();
|
|
26680
|
-
function addDependency(store2, opts) {
|
|
26681
|
-
const id = digest({ from: opts.fromId, type: "DEPENDS_ON", to: opts.toId });
|
|
26682
|
-
const attrs = { relation: opts.relation };
|
|
26683
|
-
if (opts.introduced !== void 0) attrs.introduced = opts.introduced;
|
|
26684
|
-
if (opts.fixed !== void 0) attrs.fixed = opts.fixed;
|
|
26685
|
-
if (opts.evidence !== void 0) attrs.evidence = opts.evidence;
|
|
26686
|
-
const edge = {
|
|
26687
|
-
id,
|
|
26688
|
-
from: opts.fromId,
|
|
26689
|
-
to: opts.toId,
|
|
26690
|
-
type: "DEPENDS_ON",
|
|
26691
|
-
confidence: 1,
|
|
26692
|
-
extractionSource: opts.extractionSource ?? "agent-observed",
|
|
26693
|
-
createdAt: opts.ts,
|
|
26694
|
-
lastSeenAt: opts.ts,
|
|
26695
|
-
navSuccesses: 0,
|
|
26696
|
-
navFailures: 0,
|
|
26697
|
-
attrs
|
|
26698
|
-
};
|
|
26699
|
-
store2.mergeEdge(edge);
|
|
26700
|
-
return edge;
|
|
26701
|
-
}
|
|
26702
|
-
function markRevisit(store2, node, reason, ts, answeredWhen) {
|
|
26703
|
-
const fresh = store2.getNode(node.id) ?? node;
|
|
26704
|
-
if (fresh.validTo != null || fresh.attrs["retiredReason"]) return;
|
|
26705
|
-
store2.updateNode(node.id, {
|
|
26706
|
-
attrs: {
|
|
26707
|
-
...fresh.attrs,
|
|
26708
|
-
revisit: true,
|
|
26709
|
-
revisitReason: reason,
|
|
26710
|
-
revisitSinceTs: ts,
|
|
26711
|
-
revisitAnsweredWhen: answeredWhen
|
|
26712
|
-
},
|
|
26713
|
-
lastUpdatedAt: ts
|
|
26714
|
-
});
|
|
26715
|
-
}
|
|
26716
|
-
var LEGACY_AUTO_CLOSE_ASK = "auto-closed off a pre-provenance anchor";
|
|
26717
|
-
var KNOWN_CONDITIONS = {
|
|
26718
|
-
parentProblemOpen: true,
|
|
26719
|
-
dependentStale: true
|
|
26720
|
-
};
|
|
26721
|
-
function isRevisitCondition(v) {
|
|
26722
|
-
return typeof v === "string" && Object.prototype.hasOwnProperty.call(KNOWN_CONDITIONS, v);
|
|
26723
|
-
}
|
|
26724
|
-
function conditionOf(node) {
|
|
26725
|
-
const explicit = node.attrs["revisitAnsweredWhen"];
|
|
26726
|
-
if (isRevisitCondition(explicit)) return explicit;
|
|
26727
|
-
if (String(node.attrs["revisitReason"] ?? "").startsWith(LEGACY_AUTO_CLOSE_ASK)) {
|
|
26728
|
-
return "parentProblemOpen";
|
|
26899
|
+
setMeta(key, value) {
|
|
26900
|
+
this.db.prepare(
|
|
26901
|
+
"INSERT INTO store_meta (key, value) VALUES (:key, :value) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
|
26902
|
+
).run({ key, value });
|
|
26729
26903
|
}
|
|
26730
|
-
|
|
26731
|
-
|
|
26732
|
-
|
|
26733
|
-
const report = { cleared: 0, standing: 0 };
|
|
26734
|
-
for (const label of SEMANTIC_NODE_LABELS) {
|
|
26735
|
-
for (const node of store2.findNodesByLabel(label)) {
|
|
26736
|
-
if (node.attrs["revisit"] !== true) continue;
|
|
26737
|
-
const condition = conditionOf(node);
|
|
26738
|
-
if (condition === void 0 || !isRevisitAnswered(store2, node, condition)) {
|
|
26739
|
-
report.standing++;
|
|
26740
|
-
continue;
|
|
26741
|
-
}
|
|
26742
|
-
clearRevisit(store2, node.id, ts);
|
|
26743
|
-
report.cleared++;
|
|
26744
|
-
}
|
|
26904
|
+
dirtyNodeIdsSince(ts) {
|
|
26905
|
+
const rows = this.db.prepare("SELECT id FROM nodes WHERE valid_to IS NULL AND last_updated_at > ?").all(ts);
|
|
26906
|
+
return rows.map((r) => r.id);
|
|
26745
26907
|
}
|
|
26746
|
-
|
|
26747
|
-
|
|
26748
|
-
|
|
26749
|
-
|
|
26750
|
-
|
|
26751
|
-
|
|
26752
|
-
|
|
26753
|
-
|
|
26754
|
-
|
|
26755
|
-
|
|
26756
|
-
return since > 0 && node.lastUpdatedAt > since;
|
|
26908
|
+
labelFingerprint(labels) {
|
|
26909
|
+
if (labels.length === 0) return "0";
|
|
26910
|
+
if (labels.length === FINGERPRINT_INDEXED_LABELS.length && FINGERPRINT_INDEXED_LABELS.every((l) => labels.includes(l))) {
|
|
26911
|
+
const r2 = this.db.prepare(
|
|
26912
|
+
`SELECT COUNT(*) AS n, MAX(last_updated_at) AS t,
|
|
26913
|
+
MAX(COALESCE(last_reinforced_at_seq, created_at_seq, 0)) AS s
|
|
26914
|
+
FROM nodes INDEXED BY nodes_instance_fingerprint_v2
|
|
26915
|
+
WHERE valid_to IS NULL AND label IN ('Problem','Vulnerability','Solution','RootCause')`
|
|
26916
|
+
).get();
|
|
26917
|
+
return `${r2.n}:${r2.t ?? 0}:${r2.s ?? 0}`;
|
|
26757
26918
|
}
|
|
26919
|
+
const r = this.db.prepare(
|
|
26920
|
+
`SELECT COUNT(*) AS n, MAX(last_updated_at) AS t,
|
|
26921
|
+
MAX(COALESCE(last_reinforced_at_seq, created_at_seq, 0)) AS s
|
|
26922
|
+
FROM nodes WHERE valid_to IS NULL AND label IN (${labels.map(() => "?").join(",")})`
|
|
26923
|
+
).get(...labels);
|
|
26924
|
+
return `${r.n}:${r.t ?? 0}:${r.s ?? 0}`;
|
|
26758
26925
|
}
|
|
26759
|
-
|
|
26760
|
-
|
|
26761
|
-
|
|
26762
|
-
|
|
26763
|
-
|
|
26764
|
-
|
|
26765
|
-
|
|
26766
|
-
|
|
26767
|
-
|
|
26768
|
-
}
|
|
26769
|
-
|
|
26770
|
-
return store2.nodesAsOfWhereTrue(asOf, "revisit").filter((n) => !n.attrs["retiredReason"]).map((n) => ({
|
|
26771
|
-
id: n.id,
|
|
26772
|
-
label: n.label,
|
|
26773
|
-
description: n.description,
|
|
26774
|
-
reason: String(n.attrs["revisitReason"] ?? "")
|
|
26775
|
-
}));
|
|
26776
|
-
}
|
|
26777
|
-
|
|
26778
|
-
// ../../packages/local-graph/src/credit.ts
|
|
26779
|
-
init_src2();
|
|
26780
|
-
var CREDIT_FAMILY = [
|
|
26781
|
-
...CAUSAL_EDGES,
|
|
26782
|
-
...RESOLUTION_EDGES,
|
|
26783
|
-
...JUSTIFICATION_EDGES
|
|
26784
|
-
];
|
|
26785
|
-
|
|
26786
|
-
// ../../packages/local-graph/src/claims.ts
|
|
26787
|
-
init_src();
|
|
26788
|
-
init_src();
|
|
26789
|
-
|
|
26790
|
-
// ../../packages/local-graph/src/crystallize.ts
|
|
26791
|
-
var LONG_TERM_SUPPORT = 2;
|
|
26792
|
-
var PERSISTENT_SUPPORT = 4;
|
|
26793
|
-
var PERSISTENT_CONFIDENCE = 0.85;
|
|
26794
|
-
var SCOPE_AXES = ["lang", "versionRange", "codebase", "regime"];
|
|
26795
|
-
function splittingAxis(a, b) {
|
|
26796
|
-
for (const ax of SCOPE_AXES) {
|
|
26797
|
-
if (a[ax] !== void 0 && b[ax] !== void 0 && a[ax] !== b[ax]) return ax;
|
|
26926
|
+
/** Endpoints of edges TOUCHED since `ts` — created, re-seen, or CLOSED.
|
|
26927
|
+
* Closures matter as much as additions: a node that lost inflow is rank-dirty
|
|
26928
|
+
* while its own row never updated, so removal endpoints must seed the
|
|
26929
|
+
* incremental region or the stale inflow persists until the full backstop. */
|
|
26930
|
+
edgeEndpointsTouchedSince(ts) {
|
|
26931
|
+
const rows = this.db.prepare(
|
|
26932
|
+
`SELECT from_id, to_id FROM edges
|
|
26933
|
+
WHERE (valid_to IS NULL AND (created_at > :ts OR last_seen_at > :ts))
|
|
26934
|
+
OR (valid_to IS NOT NULL AND valid_to > :ts)`
|
|
26935
|
+
).all({ ts });
|
|
26936
|
+
return rows.map((r) => ({ from: r.from_id, to: r.to_id }));
|
|
26798
26937
|
}
|
|
26799
|
-
|
|
26800
|
-
|
|
26801
|
-
|
|
26802
|
-
|
|
26803
|
-
|
|
26804
|
-
|
|
26805
|
-
|
|
26806
|
-
|
|
26807
|
-
}
|
|
26808
|
-
|
|
26809
|
-
|
|
26810
|
-
|
|
26811
|
-
|
|
26812
|
-
|
|
26813
|
-
|
|
26814
|
-
|
|
26815
|
-
|
|
26816
|
-
|
|
26817
|
-
|
|
26818
|
-
|
|
26819
|
-
|
|
26820
|
-
const s = support(c) + Math.max(0, sourcesCount(c) - 1);
|
|
26821
|
-
const f = contradictions;
|
|
26822
|
-
const confidence = (1 + s) / (2 + s + f);
|
|
26823
|
-
store2.updateNode(c.id, {
|
|
26824
|
-
attrs: { ...c.attrs, confidence },
|
|
26825
|
-
lastUpdatedAt: opts.ts
|
|
26826
|
-
});
|
|
26827
|
-
const sup = support(c);
|
|
26828
|
-
let tier = c.memoryTier;
|
|
26829
|
-
if (sup >= PERSISTENT_SUPPORT && confidence >= PERSISTENT_CONFIDENCE) tier = "persistent";
|
|
26830
|
-
else if (sup >= LONG_TERM_SUPPORT) tier = "long-term";
|
|
26831
|
-
if (tier !== c.memoryTier) {
|
|
26832
|
-
store2.updateNode(c.id, { attrs: { ...store2.getNode(c.id).attrs }, memoryTier: tier, lastUpdatedAt: opts.ts });
|
|
26833
|
-
result.promoted.push(c.id);
|
|
26938
|
+
/** Lightweight out-edges for a SET of sources, chunked IN-lists — the region
|
|
26939
|
+
* assembly path for incremental PageRank (per-node outEdges() at region
|
|
26940
|
+
* scale re-creates the 106k-prepared-calls problem the batch scan solved). */
|
|
26941
|
+
outEdgesForMany(ids) {
|
|
26942
|
+
return this.edgesForMany(ids, "from_id");
|
|
26943
|
+
}
|
|
26944
|
+
inEdgesForMany(ids) {
|
|
26945
|
+
return this.edgesForMany(ids, "to_id");
|
|
26946
|
+
}
|
|
26947
|
+
/** Stored pageRank for a SET of ids (live rows only — a closed id is simply
|
|
26948
|
+
* absent, which is how incremental region assembly drops dead endpoints). */
|
|
26949
|
+
ranksForMany(ids) {
|
|
26950
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
26951
|
+
const CHUNK = 400;
|
|
26952
|
+
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
26953
|
+
const chunk = ids.slice(i2, i2 + CHUNK);
|
|
26954
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
26955
|
+
const rows = this.db.prepare(
|
|
26956
|
+
`SELECT id, page_rank FROM nodes WHERE id IN (${placeholders}) AND valid_to IS NULL`
|
|
26957
|
+
).all(...chunk);
|
|
26958
|
+
for (const r of rows) out2.set(r.id, r.page_rank);
|
|
26834
26959
|
}
|
|
26960
|
+
return out2;
|
|
26835
26961
|
}
|
|
26836
|
-
|
|
26837
|
-
|
|
26838
|
-
|
|
26839
|
-
|
|
26840
|
-
|
|
26841
|
-
|
|
26842
|
-
|
|
26843
|
-
|
|
26844
|
-
|
|
26845
|
-
|
|
26846
|
-
|
|
26847
|
-
|
|
26848
|
-
|
|
26849
|
-
|
|
26850
|
-
|
|
26851
|
-
|
|
26852
|
-
|
|
26853
|
-
|
|
26854
|
-
|
|
26855
|
-
|
|
26856
|
-
|
|
26857
|
-
|
|
26858
|
-
|
|
26859
|
-
|
|
26860
|
-
|
|
26861
|
-
|
|
26862
|
-
|
|
26863
|
-
|
|
26864
|
-
|
|
26865
|
-
|
|
26866
|
-
} else if (axis) {
|
|
26867
|
-
markBoth(store2, a, b, { crystallized: "hypothesis" }, opts.ts);
|
|
26868
|
-
} else {
|
|
26869
|
-
markBoth(
|
|
26870
|
-
store2,
|
|
26871
|
-
a,
|
|
26872
|
-
b,
|
|
26873
|
-
{
|
|
26874
|
-
revisit: true,
|
|
26875
|
-
revisitReason: "contested: contradictory contextual claim at the same scope, no splitting axis",
|
|
26876
|
-
revisitSinceTs: opts.ts
|
|
26877
|
-
},
|
|
26878
|
-
opts.ts
|
|
26879
|
-
);
|
|
26880
|
-
result.contested.push(a.id, b.id);
|
|
26881
|
-
}
|
|
26962
|
+
/** The minimal row set the landmark sweep needs — landmark CANDIDATES
|
|
26963
|
+
* (Pattern/RootCause), everything currently flagged, and every
|
|
26964
|
+
* persistent-tier node (force-landmarks). A few thousand rows, so the
|
|
26965
|
+
* incremental path can refresh the GLOBAL landmark set without the 179k-row
|
|
26966
|
+
* scanLiveNodes materialization. */
|
|
26967
|
+
landmarkSweepRows() {
|
|
26968
|
+
const rows = this.db.prepare(
|
|
26969
|
+
`SELECT id, label, page_rank, is_landmark, memory_tier, extraction_source FROM nodes
|
|
26970
|
+
WHERE valid_to IS NULL
|
|
26971
|
+
AND (memory_tier = 'persistent' OR is_landmark = 1 OR label IN ('Pattern', 'RootCause'))`
|
|
26972
|
+
).all();
|
|
26973
|
+
return rows.map((r) => ({
|
|
26974
|
+
id: r.id,
|
|
26975
|
+
label: r.label,
|
|
26976
|
+
pageRank: r.page_rank,
|
|
26977
|
+
isLandmark: r.is_landmark === 1,
|
|
26978
|
+
memoryTier: r.memory_tier,
|
|
26979
|
+
extractionSource: r.extraction_source
|
|
26980
|
+
}));
|
|
26981
|
+
}
|
|
26982
|
+
edgesForMany(ids, col) {
|
|
26983
|
+
const out2 = [];
|
|
26984
|
+
const CHUNK = 400;
|
|
26985
|
+
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
26986
|
+
const chunk = ids.slice(i2, i2 + CHUNK);
|
|
26987
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
26988
|
+
const rows = this.db.prepare(
|
|
26989
|
+
`SELECT from_id, to_id, type FROM edges WHERE ${col} IN (${placeholders}) AND valid_to IS NULL`
|
|
26990
|
+
).all(...chunk);
|
|
26991
|
+
for (const r of rows) out2.push({ from: r.from_id, to: r.to_id, type: r.type });
|
|
26882
26992
|
}
|
|
26993
|
+
return out2;
|
|
26883
26994
|
}
|
|
26884
|
-
|
|
26885
|
-
|
|
26886
|
-
|
|
26887
|
-
|
|
26888
|
-
|
|
26889
|
-
|
|
26890
|
-
}
|
|
26891
|
-
function markBoth(store2, a, b, patch, ts) {
|
|
26892
|
-
for (const n of [a, b]) {
|
|
26893
|
-
const fresh = store2.getNode(n.id);
|
|
26894
|
-
store2.updateNode(n.id, { attrs: { ...fresh.attrs, ...patch }, lastUpdatedAt: ts });
|
|
26995
|
+
checkpointWal() {
|
|
26996
|
+
try {
|
|
26997
|
+
const r = this.db.prepare("PRAGMA wal_checkpoint(TRUNCATE)").get();
|
|
26998
|
+
return r ?? null;
|
|
26999
|
+
} catch {
|
|
27000
|
+
return null;
|
|
27001
|
+
}
|
|
26895
27002
|
}
|
|
26896
|
-
|
|
26897
|
-
|
|
26898
|
-
|
|
26899
|
-
|
|
26900
|
-
|
|
26901
|
-
|
|
26902
|
-
|
|
26903
|
-
|
|
26904
|
-
|
|
26905
|
-
|
|
26906
|
-
|
|
26907
|
-
|
|
26908
|
-
|
|
26909
|
-
|
|
26910
|
-
if (!name2) continue;
|
|
26911
|
-
const key = name2.toLowerCase();
|
|
26912
|
-
const prev = idx.get(key);
|
|
26913
|
-
if (!prev || n.attrs["resolved"] === true && prev.attrs["resolved"] !== true) {
|
|
26914
|
-
idx.set(key, n);
|
|
27003
|
+
reviveReobserved(relPaths, workspaceId, since) {
|
|
27004
|
+
let revived = 0;
|
|
27005
|
+
const CHUNK = 400;
|
|
27006
|
+
for (let i2 = 0; i2 < relPaths.length; i2 += CHUNK) {
|
|
27007
|
+
const slice = relPaths.slice(i2, i2 + CHUNK);
|
|
27008
|
+
if (slice.length === 0) continue;
|
|
27009
|
+
const r = this.db.prepare(
|
|
27010
|
+
`UPDATE nodes SET valid_to = NULL
|
|
27011
|
+
WHERE valid_to IS NOT NULL
|
|
27012
|
+
AND last_updated_at >= ?
|
|
27013
|
+
AND json_extract(attrs_json, '$.workspaceId') = ?
|
|
27014
|
+
AND json_extract(attrs_json, '$.relPath') IN (${slice.map(() => "?").join(",")})`
|
|
27015
|
+
).run(since, workspaceId, ...slice);
|
|
27016
|
+
revived += Number(r.changes);
|
|
26915
27017
|
}
|
|
27018
|
+
if (revived > 0) this.mutations++;
|
|
27019
|
+
return revived;
|
|
26916
27020
|
}
|
|
26917
|
-
|
|
26918
|
-
|
|
26919
|
-
|
|
26920
|
-
|
|
26921
|
-
|
|
26922
|
-
|
|
26923
|
-
|
|
26924
|
-
|
|
26925
|
-
|
|
26926
|
-
for (const [key, node] of index) {
|
|
26927
|
-
if (key.length < MIN_NAME_LEN) continue;
|
|
26928
|
-
if (!hay.includes(key)) continue;
|
|
26929
|
-
const esc2 = escapeRe2(key);
|
|
26930
|
-
const bounded = new RegExp(`(?<![\\w@/.\\-])${esc2}(?![\\w])`);
|
|
26931
|
-
if (!bounded.test(hay)) continue;
|
|
26932
|
-
const verMatch = new RegExp(`${esc2}\\s*@?\\s*v?(\\d+(?:\\.\\d+){0,2})\\b`).exec(hay);
|
|
26933
|
-
const hasSep = NAME_SEPARATOR.test(key);
|
|
26934
|
-
if (!hasSep && !verMatch) continue;
|
|
26935
|
-
if (seen.has(node.id)) continue;
|
|
26936
|
-
seen.add(node.id);
|
|
26937
|
-
out2.push(verMatch ? { node, version: verMatch[1] } : { node });
|
|
27021
|
+
recentEdgeAttrCoverage(marker, attr, limit) {
|
|
27022
|
+
const r = this.db.prepare(
|
|
27023
|
+
`SELECT COUNT(*) AS total,
|
|
27024
|
+
COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
|
|
27025
|
+
FROM (SELECT attrs_json FROM edges
|
|
27026
|
+
WHERE valid_to IS NULL AND attrs_json LIKE ?
|
|
27027
|
+
ORDER BY created_at DESC LIMIT ?)`
|
|
27028
|
+
).get(attr, `%${marker}%`, limit);
|
|
27029
|
+
return { count: Number(r.count), total: Number(r.total) };
|
|
26938
27030
|
}
|
|
26939
|
-
|
|
26940
|
-
|
|
26941
|
-
|
|
26942
|
-
|
|
26943
|
-
|
|
26944
|
-
|
|
26945
|
-
|
|
26946
|
-
|
|
26947
|
-
|
|
26948
|
-
|
|
26949
|
-
|
|
26950
|
-
|
|
26951
|
-
fromId: problemId,
|
|
26952
|
-
toId: node.id,
|
|
26953
|
-
relation: "depends-on",
|
|
26954
|
-
evidence: version3 ? `named in problem (@${version3})` : "named in problem statement",
|
|
26955
|
-
ts,
|
|
26956
|
-
extractionSource: "daemon-extracted"
|
|
26957
|
-
});
|
|
26958
|
-
already.add(node.id);
|
|
26959
|
-
linked.push(node.id);
|
|
27031
|
+
liveNodeIds(ids) {
|
|
27032
|
+
const live = /* @__PURE__ */ new Set();
|
|
27033
|
+
const CHUNK = 900;
|
|
27034
|
+
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
27035
|
+
const slice = ids.slice(i2, i2 + CHUNK);
|
|
27036
|
+
if (slice.length === 0) continue;
|
|
27037
|
+
const rows = this.db.prepare(
|
|
27038
|
+
`SELECT id FROM nodes WHERE valid_to IS NULL AND id IN (${slice.map(() => "?").join(",")})`
|
|
27039
|
+
).all(...slice);
|
|
27040
|
+
for (const r of rows) live.add(r.id);
|
|
27041
|
+
}
|
|
27042
|
+
return live;
|
|
26960
27043
|
}
|
|
26961
|
-
|
|
26962
|
-
|
|
26963
|
-
|
|
26964
|
-
|
|
26965
|
-
|
|
26966
|
-
|
|
26967
|
-
|
|
27044
|
+
/**
|
|
27045
|
+
* Live edges WITH their endpoint labels and ids, resolved in ONE join.
|
|
27046
|
+
*
|
|
27047
|
+
* The ontology sweep needs (id, type, fromLabel, toLabel) for every live edge.
|
|
27048
|
+
* Doing that as `scanLiveEdges()` + two `getNode()` calls is 2N node reads —
|
|
27049
|
+
* 550,000 on this store — and each one deserializes the node's embedding blob.
|
|
27050
|
+
* Measured: the sweep did not finish in 10 minutes. As a single join it is one
|
|
27051
|
+
* query over an index-covered scan. Labels only; nothing here touches embeddings.
|
|
27052
|
+
*/
|
|
27053
|
+
scanLiveEdgeRows() {
|
|
27054
|
+
const rows = this.db.prepare(
|
|
27055
|
+
`SELECT e.id, e.from_id, e.to_id, e.type, a.label AS from_label, b.label AS to_label
|
|
27056
|
+
FROM edges e
|
|
27057
|
+
LEFT JOIN nodes a ON a.id = e.from_id AND a.valid_to IS NULL
|
|
27058
|
+
LEFT JOIN nodes b ON b.id = e.to_id AND b.valid_to IS NULL
|
|
27059
|
+
WHERE e.valid_to IS NULL`
|
|
27060
|
+
).all();
|
|
27061
|
+
return rows.map((r) => ({
|
|
27062
|
+
id: r.id,
|
|
27063
|
+
from: r.from_id,
|
|
27064
|
+
to: r.to_id,
|
|
27065
|
+
type: r.type,
|
|
27066
|
+
fromLabel: r.from_label ?? void 0,
|
|
27067
|
+
toLabel: r.to_label ?? void 0
|
|
27068
|
+
}));
|
|
26968
27069
|
}
|
|
26969
|
-
|
|
26970
|
-
|
|
26971
|
-
|
|
26972
|
-
|
|
26973
|
-
|
|
26974
|
-
|
|
26975
|
-
|
|
26976
|
-
|
|
26977
|
-
const
|
|
26978
|
-
|
|
26979
|
-
|
|
26980
|
-
|
|
27070
|
+
/** Live nodes whose attrs.relPath is one of `relPaths`. Uses the nodes_relpath
|
|
27071
|
+
* expression index so the incremental reindex fetches only the changed files'
|
|
27072
|
+
* symbols instead of scanning every versioned node. */
|
|
27073
|
+
/** Live nodes of `labels` whose description is exactly one of `names`, via
|
|
27074
|
+
* the nodes_live_description index. Chunked under the bound-parameter cap. */
|
|
27075
|
+
liveNodesByDescription(names, labels) {
|
|
27076
|
+
if (names.length === 0 || labels.length === 0) return [];
|
|
27077
|
+
const out2 = [];
|
|
27078
|
+
const labelPh = labels.map(() => "?").join(",");
|
|
27079
|
+
for (let i2 = 0; i2 < names.length; i2 += 400) {
|
|
27080
|
+
const chunk = names.slice(i2, i2 + 400);
|
|
27081
|
+
const rows = this.db.prepare(
|
|
27082
|
+
`SELECT * FROM nodes WHERE valid_to IS NULL AND description IN (${chunk.map(() => "?").join(",")}) AND label IN (${labelPh})`
|
|
27083
|
+
).all(...chunk, ...labels);
|
|
27084
|
+
for (const r of rows) out2.push(rowToNode(r));
|
|
26981
27085
|
}
|
|
27086
|
+
return out2;
|
|
26982
27087
|
}
|
|
26983
|
-
|
|
26984
|
-
|
|
26985
|
-
|
|
26986
|
-
|
|
26987
|
-
}
|
|
26988
|
-
|
|
26989
|
-
|
|
26990
|
-
|
|
26991
|
-
|
|
26992
|
-
|
|
26993
|
-
|
|
26994
|
-
|
|
26995
|
-
|
|
26996
|
-
|
|
26997
|
-
|
|
26998
|
-
|
|
26999
|
-
|
|
27000
|
-
|
|
27001
|
-
|
|
27002
|
-
|
|
27003
|
-
|
|
27004
|
-
|
|
27005
|
-
|
|
27006
|
-
|
|
27007
|
-
|
|
27008
|
-
}
|
|
27009
|
-
|
|
27010
|
-
const report = { languages: [], packages: [] };
|
|
27011
|
-
const p = store2.getNode(problemId);
|
|
27012
|
-
if (!p || p.label !== "Problem" && p.label !== "Vulnerability") return report;
|
|
27013
|
-
const files = store2.outEdges(problemId, ["ANCHORED_AT"]).map((e) => store2.getNode(e.to)).filter((n) => !!n && n.label === "File");
|
|
27014
|
-
if (files.length === 0) return report;
|
|
27015
|
-
const idx = pkgIndex ?? buildPackageIndex(store2);
|
|
27016
|
-
const haveLang = new Set(store2.outEdges(problemId, ["OCCURS_IN"]).map((e) => e.to));
|
|
27017
|
-
const havePkg = new Set(store2.outEdges(problemId, ["DEPENDS_ON"]).map((e) => e.to));
|
|
27018
|
-
for (const file2 of files) {
|
|
27019
|
-
const lang = String(file2.attrs["language"] ?? "").trim();
|
|
27020
|
-
if (lang) {
|
|
27021
|
-
const langId = langNodeId(lang);
|
|
27022
|
-
if (!store2.getNode(langId)) {
|
|
27023
|
-
store2.mergeNode({
|
|
27024
|
-
id: langId,
|
|
27025
|
-
label: "Language",
|
|
27026
|
-
description: lang,
|
|
27027
|
-
extractionConfidence: 1,
|
|
27028
|
-
extractionSource: "daemon-extracted",
|
|
27029
|
-
embedding: [],
|
|
27030
|
-
cumulativeSurprise: 0,
|
|
27031
|
-
peakSurprise: 0,
|
|
27032
|
-
cumulativeHits: 0,
|
|
27033
|
-
lastUpdatedAt: ts,
|
|
27034
|
-
createdAt: ts,
|
|
27035
|
-
memoryTier: "short-term",
|
|
27036
|
-
pageRank: 0,
|
|
27037
|
-
isLandmark: false,
|
|
27038
|
-
community: null,
|
|
27039
|
-
stability: "stable",
|
|
27040
|
-
attrs: { name: lang, workspaceId: file2.attrs["workspaceId"] }
|
|
27041
|
-
});
|
|
27042
|
-
}
|
|
27043
|
-
if (!haveLang.has(langId)) {
|
|
27044
|
-
mergeContextEdge(store2, problemId, langId, "OCCURS_IN", ts);
|
|
27045
|
-
haveLang.add(langId);
|
|
27046
|
-
report.languages.push(langId);
|
|
27047
|
-
}
|
|
27048
|
-
}
|
|
27049
|
-
for (const e of store2.outEdges(file2.id, ["IMPORTS"])) {
|
|
27050
|
-
const mod = store2.getNode(e.to);
|
|
27051
|
-
if (!mod) continue;
|
|
27052
|
-
const name2 = specToPackageName(mod.description);
|
|
27053
|
-
if (!name2) continue;
|
|
27054
|
-
const pkg = idx.get(name2);
|
|
27055
|
-
if (!pkg || havePkg.has(pkg.id)) continue;
|
|
27056
|
-
addDependency(store2, {
|
|
27057
|
-
fromId: problemId,
|
|
27058
|
-
toId: pkg.id,
|
|
27059
|
-
relation: "depends-on",
|
|
27060
|
-
evidence: "imported by the anchored code",
|
|
27061
|
-
ts,
|
|
27062
|
-
extractionSource: "daemon-extracted"
|
|
27063
|
-
});
|
|
27064
|
-
havePkg.add(pkg.id);
|
|
27065
|
-
report.packages.push(pkg.id);
|
|
27088
|
+
nodesByRelPath(relPaths) {
|
|
27089
|
+
if (relPaths.length === 0) return [];
|
|
27090
|
+
const placeholders = relPaths.map(() => "?").join(",");
|
|
27091
|
+
const rows = this.db.prepare(
|
|
27092
|
+
`SELECT * FROM nodes WHERE valid_to IS NULL AND json_extract(attrs_json, '$.relPath') IN (${placeholders})`
|
|
27093
|
+
).all(...relPaths);
|
|
27094
|
+
return rows.map(rowToNode);
|
|
27095
|
+
}
|
|
27096
|
+
findLiveNodesByAttr(label, attrPath, value) {
|
|
27097
|
+
if (!/^[A-Za-z0-9_]+(\.[A-Za-z0-9_]+)*$/.test(attrPath)) throw new Error(`invalid attr path: ${attrPath}`);
|
|
27098
|
+
const rows = this.db.prepare(
|
|
27099
|
+
`SELECT * FROM nodes WHERE json_extract(attrs_json, '$.${attrPath}') = ? AND valid_to IS NULL AND label = ?`
|
|
27100
|
+
).all(value, label);
|
|
27101
|
+
return rows.map(rowToNode);
|
|
27102
|
+
}
|
|
27103
|
+
// ── Bi-temporal versioning ───────────────────────────────────────────
|
|
27104
|
+
// NOTE: the adapter's transaction() is non-reentrant (raw BEGIN/COMMIT), and
|
|
27105
|
+
// the indexer calls freezeNode inside its own store.transaction(). So this
|
|
27106
|
+
// method does NOT open a transaction — callers needing atomicity wrap it.
|
|
27107
|
+
freezeNode(liveId, t) {
|
|
27108
|
+
const live = this.getNode(liveId);
|
|
27109
|
+
if (!live) return null;
|
|
27110
|
+
const version3 = live.version ?? 1;
|
|
27111
|
+
const frozenId = `${liveId}@v${version3}`;
|
|
27112
|
+
if (this.getNode(frozenId)) {
|
|
27113
|
+
this.stmts.advanceLive.run({ live_id: liveId, t });
|
|
27114
|
+
return frozenId;
|
|
27066
27115
|
}
|
|
27116
|
+
this.stmts.freezeCopy.run({ frozen_id: frozenId, live_id: liveId, t });
|
|
27117
|
+
this.mergeEdge({
|
|
27118
|
+
id: `edge_superseded_${frozenId}`,
|
|
27119
|
+
from: frozenId,
|
|
27120
|
+
to: liveId,
|
|
27121
|
+
type: "SUPERSEDED_BY",
|
|
27122
|
+
confidence: 1,
|
|
27123
|
+
extractionSource: "daemon-extracted",
|
|
27124
|
+
createdAt: t,
|
|
27125
|
+
lastSeenAt: t,
|
|
27126
|
+
navSuccesses: 0,
|
|
27127
|
+
navFailures: 0,
|
|
27128
|
+
validFrom: t,
|
|
27129
|
+
validTo: null,
|
|
27130
|
+
attrs: {}
|
|
27131
|
+
});
|
|
27132
|
+
this.stmts.advanceLive.run({ live_id: liveId, t });
|
|
27133
|
+
return frozenId;
|
|
27067
27134
|
}
|
|
27068
|
-
|
|
27069
|
-
}
|
|
27070
|
-
function backfillProblemContext(store2, ts) {
|
|
27071
|
-
const report = { problems: 0, edges: 0 };
|
|
27072
|
-
const idx = buildPackageIndex(store2);
|
|
27073
|
-
for (const p of store2.findNodesByLabel("Problem")) {
|
|
27074
|
-
if (p.attrs["resolvedAs"] === "false_positive") continue;
|
|
27075
|
-
const r = deriveContextFromAnchors(store2, p.id, ts, idx);
|
|
27076
|
-
const n = r.languages.length + r.packages.length;
|
|
27077
|
-
if (n > 0) {
|
|
27078
|
-
report.problems++;
|
|
27079
|
-
report.edges += n;
|
|
27080
|
-
}
|
|
27135
|
+
closeNode(id, t) {
|
|
27136
|
+
this.stmts.closeNode.run({ id, t });
|
|
27081
27137
|
}
|
|
27082
|
-
|
|
27138
|
+
closeEdge(id, t) {
|
|
27139
|
+
this.stmts.closeEdge.run({ id, t });
|
|
27140
|
+
}
|
|
27141
|
+
nodeHistory(logicalId) {
|
|
27142
|
+
const rows = this.stmts.nodeHistory.all(logicalId);
|
|
27143
|
+
return rows.map(rowToNode);
|
|
27144
|
+
}
|
|
27145
|
+
nodesAsOf(t) {
|
|
27146
|
+
const rows = this.stmts.nodesAsOf.all({ t });
|
|
27147
|
+
return rows.map(rowToNode);
|
|
27148
|
+
}
|
|
27149
|
+
/** `nodesAsOf(t).filter((n) => n.attrs[attr] === true)` with the attr test in
|
|
27150
|
+
* SQL, so only the flagged rows are hydrated. The filter-after form hydrated
|
|
27151
|
+
* the whole graph for a handful of revisit flags: 6.7s per context render on
|
|
27152
|
+
* a 227k-node store (2026-09-24). `attr` is interpolated into a JSON path,
|
|
27153
|
+
* so it must be a plain identifier. */
|
|
27154
|
+
nodesAsOfWhereTrue(t, attr) {
|
|
27155
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attr)) throw new Error(`nodesAsOfWhereTrue: bad attr ${JSON.stringify(attr)}`);
|
|
27156
|
+
const rows = this.db.prepare(
|
|
27157
|
+
`SELECT * FROM nodes WHERE valid_from <= :t AND (valid_to IS NULL OR valid_to > :t)
|
|
27158
|
+
AND json_type(attrs_json, '$.${attr}') = 'true'`
|
|
27159
|
+
).all({ t });
|
|
27160
|
+
return rows.map(rowToNode);
|
|
27161
|
+
}
|
|
27162
|
+
edgesAsOf(t) {
|
|
27163
|
+
const rows = this.stmts.edgesAsOf.all({ t });
|
|
27164
|
+
return rows.map(rowToEdge);
|
|
27165
|
+
}
|
|
27166
|
+
transaction(fn) {
|
|
27167
|
+
return this.db.transaction(fn);
|
|
27168
|
+
}
|
|
27169
|
+
close() {
|
|
27170
|
+
this.db.close();
|
|
27171
|
+
}
|
|
27172
|
+
};
|
|
27173
|
+
function openGraphStore(opts) {
|
|
27174
|
+
return new SqliteGraphStore(opts);
|
|
27083
27175
|
}
|
|
27084
27176
|
|
|
27085
|
-
// ../../packages/local-graph/src/
|
|
27177
|
+
// ../../packages/local-graph/src/traversal.ts
|
|
27086
27178
|
init_src2();
|
|
27087
|
-
|
|
27088
|
-
|
|
27089
|
-
|
|
27090
|
-
|
|
27091
|
-
var REDIRECT_EDGES = [
|
|
27092
|
-
"CAUSED_BY",
|
|
27093
|
-
"SOLVED_BY",
|
|
27094
|
-
"FIXED_BY",
|
|
27095
|
-
"ANCHORED_AT",
|
|
27096
|
-
"MANIFESTED_IN",
|
|
27097
|
-
"EVIDENCED_BY"
|
|
27179
|
+
var MAX_EDGE_WEIGHT = Math.max(...Object.values(EDGE_WEIGHT));
|
|
27180
|
+
var CAUSAL_FAMILY = [
|
|
27181
|
+
...CAUSAL_EDGES,
|
|
27182
|
+
...RESOLUTION_EDGES
|
|
27098
27183
|
];
|
|
27099
|
-
function corroborations(n) {
|
|
27100
|
-
return Number(n.attrs["corroborations"] ?? 0);
|
|
27101
|
-
}
|
|
27102
|
-
function overlap(a, b) {
|
|
27103
|
-
if (a.size === 0 || b.size === 0) return 0;
|
|
27104
|
-
let inter = 0;
|
|
27105
|
-
for (const x of a) if (b.has(x)) inter++;
|
|
27106
|
-
return inter / Math.min(a.size, b.size);
|
|
27107
|
-
}
|
|
27108
|
-
function mergeDuplicateProblems(store2, opts) {
|
|
27109
|
-
const minOverlap = opts.minOverlap ?? 0.85;
|
|
27110
|
-
const minTokens = opts.minTokens ?? 4;
|
|
27111
|
-
const report = { clusters: 0, merged: 0 };
|
|
27112
|
-
const open = store2.findNodesByLabel("Problem").filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0);
|
|
27113
|
-
const tokens = /* @__PURE__ */ new Map();
|
|
27114
|
-
for (const p of open) tokens.set(p.id, new Set(retrievalTokens(p.description)));
|
|
27115
|
-
const consumed = /* @__PURE__ */ new Set();
|
|
27116
|
-
store2.transaction(() => {
|
|
27117
|
-
for (let i2 = 0; i2 < open.length; i2++) {
|
|
27118
|
-
const a = open[i2];
|
|
27119
|
-
if (consumed.has(a.id)) continue;
|
|
27120
|
-
const cluster = [a];
|
|
27121
|
-
const ta = tokens.get(a.id);
|
|
27122
|
-
for (let j = i2 + 1; j < open.length; j++) {
|
|
27123
|
-
const b = open[j];
|
|
27124
|
-
if (consumed.has(b.id)) continue;
|
|
27125
|
-
const tb = tokens.get(b.id);
|
|
27126
|
-
if (Math.min(ta.size, tb.size) < minTokens) continue;
|
|
27127
|
-
if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
|
|
27128
|
-
if (overlap(ta, tb) >= minOverlap) {
|
|
27129
|
-
cluster.push(b);
|
|
27130
|
-
consumed.add(b.id);
|
|
27131
|
-
}
|
|
27132
|
-
}
|
|
27133
|
-
if (cluster.length < 2) continue;
|
|
27134
|
-
report.clusters++;
|
|
27135
|
-
cluster.sort((x, y) => corroborations(y) - corroborations(x) || x.createdAt - y.createdAt);
|
|
27136
|
-
const survivor = cluster[0];
|
|
27137
|
-
for (const dup of cluster.slice(1)) {
|
|
27138
|
-
foldDuplicateProblem(store2, dup, survivor, opts.ts);
|
|
27139
|
-
report.merged++;
|
|
27140
|
-
}
|
|
27141
|
-
}
|
|
27142
|
-
});
|
|
27143
|
-
return report;
|
|
27144
|
-
}
|
|
27145
|
-
function foldDuplicateProblem(store2, dup, survivor, ts) {
|
|
27146
|
-
const surv = store2.getNode(survivor.id);
|
|
27147
|
-
if (!surv) return;
|
|
27148
|
-
const sources = new Set(surv.attrs["sources"] ?? []);
|
|
27149
|
-
for (const s of dup.attrs["sources"] ?? []) sources.add(s);
|
|
27150
|
-
const dupSeq = dup.attrs["createdAtSeq"] ?? store2.currentIngestSeq();
|
|
27151
|
-
const exposure = priorExposure(store2, survivor.id, dupSeq);
|
|
27152
|
-
const expField = `reinforce${exposure[0].toUpperCase()}${exposure.slice(1)}Count`;
|
|
27153
|
-
store2.updateNode(survivor.id, {
|
|
27154
|
-
attrs: {
|
|
27155
|
-
...surv.attrs,
|
|
27156
|
-
sources: [...sources],
|
|
27157
|
-
corroborations: corroborations(surv) + corroborations(dup) + 1,
|
|
27158
|
-
[expField]: (surv.attrs[expField] ?? 0) + 1,
|
|
27159
|
-
lastReinforceExposure: exposure
|
|
27160
|
-
},
|
|
27161
|
-
cumulativeHits: (surv.cumulativeHits ?? 0) + (dup.cumulativeHits ?? 0),
|
|
27162
|
-
lastUpdatedAt: ts
|
|
27163
|
-
});
|
|
27164
|
-
for (const e of store2.outEdges(dup.id, REDIRECT_EDGES)) {
|
|
27165
|
-
if (e.to === survivor.id) continue;
|
|
27166
|
-
const id = `edge_${digest({ from: survivor.id, type: e.type, to: e.to })}`.slice(0, 24);
|
|
27167
|
-
if (store2.getEdge(id)) continue;
|
|
27168
|
-
const redirected = {
|
|
27169
|
-
...e,
|
|
27170
|
-
id,
|
|
27171
|
-
from: survivor.id,
|
|
27172
|
-
createdAt: ts,
|
|
27173
|
-
lastSeenAt: ts
|
|
27174
|
-
};
|
|
27175
|
-
store2.mergeEdge(redirected);
|
|
27176
|
-
}
|
|
27177
|
-
store2.updateNode(dup.id, {
|
|
27178
|
-
attrs: { ...dup.attrs, mergedInto: survivor.id, resolvedAs: "duplicate", resolvedAt: ts },
|
|
27179
|
-
lastUpdatedAt: ts
|
|
27180
|
-
});
|
|
27181
|
-
store2.closeNode(dup.id, ts);
|
|
27182
|
-
}
|
|
27183
27184
|
|
|
27184
|
-
// ../../packages/local-graph/src/
|
|
27185
|
+
// ../../packages/local-graph/src/justification.ts
|
|
27185
27186
|
init_src();
|
|
27186
|
-
|
|
27187
|
-
|
|
27188
|
-
}
|
|
27189
|
-
|
|
27190
|
-
|
|
27191
|
-
|
|
27192
|
-
|
|
27193
|
-
|
|
27194
|
-
|
|
27195
|
-
|
|
27196
|
-
|
|
27197
|
-
|
|
27198
|
-
|
|
27199
|
-
|
|
27200
|
-
|
|
27201
|
-
|
|
27202
|
-
|
|
27203
|
-
|
|
27204
|
-
|
|
27205
|
-
function isAutoMinted(n) {
|
|
27206
|
-
return isEditPlaceholderSolution(n);
|
|
27207
|
-
}
|
|
27208
|
-
function priorsForFile(store2, relPath) {
|
|
27209
|
-
const want = relPath.trim().replace(/^\.?\//, "");
|
|
27210
|
-
let file2 = null;
|
|
27211
|
-
for (const n of store2.findNodesByLabel("File")) {
|
|
27212
|
-
if (n.description === want || n.attrs["relPath"] === want) {
|
|
27213
|
-
file2 = n;
|
|
27214
|
-
break;
|
|
27215
|
-
}
|
|
27216
|
-
}
|
|
27217
|
-
if (!file2) return null;
|
|
27218
|
-
const openProblems = [];
|
|
27219
|
-
const constraints = [];
|
|
27220
|
-
const resolvedProblems = [];
|
|
27221
|
-
const seenProblem = /* @__PURE__ */ new Set();
|
|
27222
|
-
const related = /* @__PURE__ */ new Map();
|
|
27223
|
-
const anchorEdges = [
|
|
27224
|
-
...store2.inEdges(file2.id, ["ANCHORED_AT"]),
|
|
27225
|
-
...store2.outEdges(file2.id, ["DEFINES"]).flatMap((d) => store2.inEdges(d.to, ["ANCHORED_AT"]))
|
|
27226
|
-
];
|
|
27227
|
-
for (const e of anchorEdges) {
|
|
27228
|
-
const n = store2.getNode(e.from);
|
|
27229
|
-
if (!n) continue;
|
|
27230
|
-
if (n.label === "Problem" || n.label === "Vulnerability") {
|
|
27231
|
-
if (seenProblem.has(n.id)) continue;
|
|
27232
|
-
seenProblem.add(n.id);
|
|
27233
|
-
if (!n.attrs["resolvedAt"]) {
|
|
27234
|
-
(isConstraintProblem(n) ? constraints : openProblems).push(n);
|
|
27235
|
-
} else if (n.attrs["resolvedAs"] == null && n.attrs["mergedInto"] === void 0 && e.attrs?.["anchorProvenance"] !== "legacy") {
|
|
27236
|
-
resolvedProblems.push(n);
|
|
27237
|
-
}
|
|
27238
|
-
} else if (CITABLE_PRIOR_LABELS.has(n.label) && !isAutoMinted(n)) {
|
|
27239
|
-
related.set(n.id, n);
|
|
27240
|
-
}
|
|
27241
|
-
}
|
|
27242
|
-
const solutionsByProblem = /* @__PURE__ */ new Map();
|
|
27243
|
-
for (const p of [...openProblems, ...constraints, ...resolvedProblems]) {
|
|
27244
|
-
for (const e of store2.outEdges(p.id, ["SOLVED_BY", "CAUSED_BY", "INSTANCE_OF"])) {
|
|
27245
|
-
const n = store2.getNode(e.to);
|
|
27246
|
-
if (n && CITABLE_PRIOR_LABELS.has(n.label) && !isAutoMinted(n)) related.set(n.id, n);
|
|
27247
|
-
if (n && n.label === "Solution" && e.type === "SOLVED_BY" && !isAutoMinted(n)) {
|
|
27248
|
-
const list = solutionsByProblem.get(p.id) ?? [];
|
|
27249
|
-
if (!list.some((s) => s.id === n.id)) list.push(n);
|
|
27250
|
-
solutionsByProblem.set(p.id, list);
|
|
27251
|
-
}
|
|
27252
|
-
}
|
|
27253
|
-
}
|
|
27254
|
-
if (openProblems.length === 0 && constraints.length === 0 && resolvedProblems.length === 0 && related.size === 0)
|
|
27255
|
-
return null;
|
|
27256
|
-
const recentFirst = (a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0);
|
|
27257
|
-
openProblems.sort(recentFirst);
|
|
27258
|
-
constraints.sort(recentFirst);
|
|
27259
|
-
resolvedProblems.sort(
|
|
27260
|
-
(a, b) => Number(b.attrs["resolvedAt"] ?? 0) - Number(a.attrs["resolvedAt"] ?? 0)
|
|
27261
|
-
);
|
|
27262
|
-
return {
|
|
27263
|
-
file: file2,
|
|
27264
|
-
openProblems,
|
|
27265
|
-
constraints,
|
|
27266
|
-
resolvedProblems,
|
|
27267
|
-
related: [...related.values()],
|
|
27268
|
-
solutionsByProblem
|
|
27187
|
+
init_src2();
|
|
27188
|
+
function addDependency(store2, opts) {
|
|
27189
|
+
const id = digest({ from: opts.fromId, type: "DEPENDS_ON", to: opts.toId });
|
|
27190
|
+
const attrs = { relation: opts.relation };
|
|
27191
|
+
if (opts.introduced !== void 0) attrs.introduced = opts.introduced;
|
|
27192
|
+
if (opts.fixed !== void 0) attrs.fixed = opts.fixed;
|
|
27193
|
+
if (opts.evidence !== void 0) attrs.evidence = opts.evidence;
|
|
27194
|
+
const edge = {
|
|
27195
|
+
id,
|
|
27196
|
+
from: opts.fromId,
|
|
27197
|
+
to: opts.toId,
|
|
27198
|
+
type: "DEPENDS_ON",
|
|
27199
|
+
confidence: 1,
|
|
27200
|
+
extractionSource: opts.extractionSource ?? "agent-observed",
|
|
27201
|
+
createdAt: opts.ts,
|
|
27202
|
+
lastSeenAt: opts.ts,
|
|
27203
|
+
navSuccesses: 0,
|
|
27204
|
+
navFailures: 0,
|
|
27205
|
+
attrs
|
|
27269
27206
|
};
|
|
27207
|
+
store2.mergeEdge(edge);
|
|
27208
|
+
return edge;
|
|
27270
27209
|
}
|
|
27271
|
-
function
|
|
27272
|
-
const
|
|
27273
|
-
if (
|
|
27274
|
-
|
|
27275
|
-
|
|
27276
|
-
|
|
27277
|
-
|
|
27278
|
-
|
|
27279
|
-
|
|
27280
|
-
|
|
27281
|
-
|
|
27282
|
-
|
|
27283
|
-
|
|
27284
|
-
|
|
27285
|
-
|
|
27286
|
-
|
|
27287
|
-
|
|
27288
|
-
|
|
27289
|
-
|
|
27290
|
-
|
|
27291
|
-
|
|
27292
|
-
|
|
27293
|
-
|
|
27210
|
+
function markRevisit(store2, node, reason, ts, answeredWhen) {
|
|
27211
|
+
const fresh = store2.getNode(node.id) ?? node;
|
|
27212
|
+
if (fresh.validTo != null || fresh.attrs["retiredReason"]) return;
|
|
27213
|
+
store2.updateNode(node.id, {
|
|
27214
|
+
attrs: {
|
|
27215
|
+
...fresh.attrs,
|
|
27216
|
+
revisit: true,
|
|
27217
|
+
revisitReason: reason,
|
|
27218
|
+
revisitSinceTs: ts,
|
|
27219
|
+
revisitAnsweredWhen: answeredWhen
|
|
27220
|
+
},
|
|
27221
|
+
lastUpdatedAt: ts
|
|
27222
|
+
});
|
|
27223
|
+
}
|
|
27224
|
+
var LEGACY_AUTO_CLOSE_ASK = "auto-closed off a pre-provenance anchor";
|
|
27225
|
+
var KNOWN_CONDITIONS = {
|
|
27226
|
+
parentProblemOpen: true,
|
|
27227
|
+
dependentStale: true
|
|
27228
|
+
};
|
|
27229
|
+
function isRevisitCondition(v) {
|
|
27230
|
+
return typeof v === "string" && Object.prototype.hasOwnProperty.call(KNOWN_CONDITIONS, v);
|
|
27231
|
+
}
|
|
27232
|
+
function conditionOf(node) {
|
|
27233
|
+
const explicit = node.attrs["revisitAnsweredWhen"];
|
|
27234
|
+
if (isRevisitCondition(explicit)) return explicit;
|
|
27235
|
+
if (String(node.attrs["revisitReason"] ?? "").startsWith(LEGACY_AUTO_CLOSE_ASK)) {
|
|
27236
|
+
return "parentProblemOpen";
|
|
27294
27237
|
}
|
|
27295
|
-
|
|
27296
|
-
|
|
27297
|
-
|
|
27298
|
-
|
|
27299
|
-
|
|
27300
|
-
|
|
27301
|
-
|
|
27302
|
-
|
|
27303
|
-
|
|
27304
|
-
|
|
27305
|
-
|
|
27306
|
-
|
|
27307
|
-
|
|
27308
|
-
|
|
27309
|
-
attrs: {
|
|
27310
|
-
provisional: true,
|
|
27311
|
-
captureTime: true,
|
|
27312
|
-
anchorProvenance: provenance,
|
|
27313
|
-
anchorLine: opts.line,
|
|
27314
|
-
enclosingSymbol: true,
|
|
27315
|
-
// PJ-anchor-version: WHICH version of the symbol this was witnessed
|
|
27316
|
-
// against. `bodyHash` is the indexer's 16-hex sha256 of the body;
|
|
27317
|
-
// `headSha` is the checkout's HEAD at capture. Both opaque, both
|
|
27318
|
-
// cross the membrane on the anchor edge, and a teammate's reanchor
|
|
27319
|
-
// compares the hash with their own symbol to tell "current" from
|
|
27320
|
-
// "pre-change" — the branch hazard where a fix witnessed on feat/x
|
|
27321
|
-
// would otherwise read as a fact about the unchanged symbol on dev.
|
|
27322
|
-
...typeof sym.attrs["bodyHash"] === "string" ? { symbolContentHash: sym.attrs["bodyHash"] } : {},
|
|
27323
|
-
...opts.headSha ? { observedAtCommit: opts.headSha } : {}
|
|
27324
|
-
}
|
|
27325
|
-
});
|
|
27238
|
+
return void 0;
|
|
27239
|
+
}
|
|
27240
|
+
function clearAnsweredRevisits(store2, ts) {
|
|
27241
|
+
const report = { cleared: 0, standing: 0 };
|
|
27242
|
+
for (const label of SEMANTIC_NODE_LABELS) {
|
|
27243
|
+
for (const node of store2.findNodesByLabel(label)) {
|
|
27244
|
+
if (node.attrs["revisit"] !== true) continue;
|
|
27245
|
+
const condition = conditionOf(node);
|
|
27246
|
+
if (condition === void 0 || !isRevisitAnswered(store2, node, condition)) {
|
|
27247
|
+
report.standing++;
|
|
27248
|
+
continue;
|
|
27249
|
+
}
|
|
27250
|
+
clearRevisit(store2, node.id, ts);
|
|
27251
|
+
report.cleared++;
|
|
27326
27252
|
}
|
|
27327
27253
|
}
|
|
27328
|
-
return
|
|
27254
|
+
return report;
|
|
27329
27255
|
}
|
|
27330
|
-
|
|
27331
|
-
|
|
27332
|
-
|
|
27333
|
-
|
|
27334
|
-
|
|
27335
|
-
"Interface",
|
|
27336
|
-
"Const",
|
|
27337
|
-
"Enum",
|
|
27338
|
-
"TypeAlias",
|
|
27339
|
-
"Namespace",
|
|
27340
|
-
"Property"
|
|
27341
|
-
]);
|
|
27342
|
-
var ENCLOSING_WALK_DEPTH = 8;
|
|
27343
|
-
function resolveEnclosingSymbol(store2, fileId, line, fileText) {
|
|
27344
|
-
if (!Number.isFinite(line) || line < 1) return null;
|
|
27345
|
-
const row = Math.floor(line) - 1;
|
|
27346
|
-
const lineStarts = [0];
|
|
27347
|
-
for (let i2 = 0; i2 < fileText.length; i2++) if (fileText.charCodeAt(i2) === 10) lineStarts.push(i2 + 1);
|
|
27348
|
-
const rowOf = (index) => {
|
|
27349
|
-
let lo = 0;
|
|
27350
|
-
let hi = lineStarts.length - 1;
|
|
27351
|
-
while (lo < hi) {
|
|
27352
|
-
const mid = lo + hi + 1 >> 1;
|
|
27353
|
-
if (lineStarts[mid] <= index) lo = mid;
|
|
27354
|
-
else hi = mid - 1;
|
|
27256
|
+
function isRevisitAnswered(store2, node, condition) {
|
|
27257
|
+
switch (condition) {
|
|
27258
|
+
case "parentProblemOpen": {
|
|
27259
|
+
const parents = store2.inEdges(node.id, ["SOLVED_BY"]).map((e) => store2.getNode(e.from)).filter((n) => n !== null && n.validTo == null);
|
|
27260
|
+
return parents.every((p) => p.attrs["resolvedAt"] == null);
|
|
27355
27261
|
}
|
|
27356
|
-
|
|
27357
|
-
|
|
27358
|
-
|
|
27359
|
-
let bestStart = -1;
|
|
27360
|
-
let bestSpan = Number.POSITIVE_INFINITY;
|
|
27361
|
-
const seen = /* @__PURE__ */ new Set([fileId]);
|
|
27362
|
-
let frontier = [fileId];
|
|
27363
|
-
for (let depth = 0; depth < ENCLOSING_WALK_DEPTH && frontier.length > 0; depth++) {
|
|
27364
|
-
const next = [];
|
|
27365
|
-
for (const id of frontier) {
|
|
27366
|
-
for (const e of store2.outEdges(id, ["DEFINES"])) {
|
|
27367
|
-
if (seen.has(e.to)) continue;
|
|
27368
|
-
seen.add(e.to);
|
|
27369
|
-
const n = store2.getNode(e.to);
|
|
27370
|
-
if (!n) continue;
|
|
27371
|
-
next.push(n.id);
|
|
27372
|
-
if (!ENCLOSING_SYMBOL_KINDS.has(n.label)) continue;
|
|
27373
|
-
const startRow = n.attrs["startRow"];
|
|
27374
|
-
const startByte = n.attrs["startByte"];
|
|
27375
|
-
const endByte = n.attrs["endByte"];
|
|
27376
|
-
if (typeof startRow !== "number" || typeof startByte !== "number" || typeof endByte !== "number") continue;
|
|
27377
|
-
if (startRow < 0 || endByte < startByte) continue;
|
|
27378
|
-
const endRow = rowOf(Math.max(startByte, Math.min(endByte, fileText.length) - 1));
|
|
27379
|
-
if (row < startRow || row > endRow) continue;
|
|
27380
|
-
const span = endByte - startByte;
|
|
27381
|
-
if (startRow > bestStart || startRow === bestStart && span < bestSpan) {
|
|
27382
|
-
best = n;
|
|
27383
|
-
bestStart = startRow;
|
|
27384
|
-
bestSpan = span;
|
|
27385
|
-
}
|
|
27386
|
-
}
|
|
27262
|
+
case "dependentStale": {
|
|
27263
|
+
const since = Number(node.attrs["revisitSinceTs"] ?? 0);
|
|
27264
|
+
return since > 0 && node.lastUpdatedAt > since;
|
|
27387
27265
|
}
|
|
27388
|
-
frontier = next;
|
|
27389
27266
|
}
|
|
27390
|
-
return best;
|
|
27391
|
-
}
|
|
27392
|
-
function recordAnchorHint(store2, problemId, relPath, provenance, ts) {
|
|
27393
|
-
const p = store2.getNode(problemId);
|
|
27394
|
-
if (!p || p.label !== "Problem") return false;
|
|
27395
|
-
if (p.attrs["anchorHint"]) return false;
|
|
27396
|
-
store2.updateNode(problemId, {
|
|
27397
|
-
attrs: { ...p.attrs, anchorHint: { path: relPath, provenance, ts } }
|
|
27398
|
-
});
|
|
27399
|
-
return true;
|
|
27400
27267
|
}
|
|
27401
|
-
function
|
|
27402
|
-
const
|
|
27403
|
-
if (!
|
|
27404
|
-
|
|
27268
|
+
function clearRevisit(store2, nodeId, ts) {
|
|
27269
|
+
const n = store2.getNode(nodeId);
|
|
27270
|
+
if (!n) return;
|
|
27271
|
+
const attrs = { ...n.attrs };
|
|
27272
|
+
delete attrs["revisit"];
|
|
27273
|
+
delete attrs["revisitReason"];
|
|
27274
|
+
delete attrs["revisitSinceTs"];
|
|
27275
|
+
store2.updateNode(nodeId, { attrs, lastUpdatedAt: ts });
|
|
27405
27276
|
}
|
|
27406
|
-
function
|
|
27407
|
-
|
|
27408
|
-
|
|
27409
|
-
|
|
27277
|
+
function listNeedsRevisit(store2, asOf) {
|
|
27278
|
+
return store2.nodesAsOfWhereTrue(asOf, "revisit").filter((n) => !n.attrs["retiredReason"]).map((n) => ({
|
|
27279
|
+
id: n.id,
|
|
27280
|
+
label: n.label,
|
|
27281
|
+
description: n.description,
|
|
27282
|
+
reason: String(n.attrs["revisitReason"] ?? "")
|
|
27283
|
+
}));
|
|
27410
27284
|
}
|
|
27411
|
-
|
|
27412
|
-
|
|
27413
|
-
|
|
27414
|
-
|
|
27415
|
-
|
|
27416
|
-
|
|
27417
|
-
|
|
27418
|
-
|
|
27419
|
-
|
|
27420
|
-
|
|
27421
|
-
|
|
27422
|
-
|
|
27423
|
-
|
|
27424
|
-
|
|
27425
|
-
|
|
27426
|
-
|
|
27427
|
-
|
|
27428
|
-
|
|
27429
|
-
|
|
27430
|
-
|
|
27431
|
-
|
|
27432
|
-
const changedAt = Number(file2.attrs["contentChangedAt"] ?? 0);
|
|
27433
|
-
if (changedAt <= hint.ts) continue;
|
|
27434
|
-
if (anchorProblemToWorkingFile(store2, p.id, hint.path, workspaceId, ts, "hint-confirmed")) {
|
|
27435
|
-
consumeAnchorHint(store2, p, ts);
|
|
27436
|
-
upgraded++;
|
|
27437
|
-
standing--;
|
|
27438
|
-
} else {
|
|
27439
|
-
blocked++;
|
|
27440
|
-
}
|
|
27285
|
+
|
|
27286
|
+
// ../../packages/local-graph/src/credit.ts
|
|
27287
|
+
init_src2();
|
|
27288
|
+
var CREDIT_FAMILY = [
|
|
27289
|
+
...CAUSAL_EDGES,
|
|
27290
|
+
...RESOLUTION_EDGES,
|
|
27291
|
+
...JUSTIFICATION_EDGES
|
|
27292
|
+
];
|
|
27293
|
+
|
|
27294
|
+
// ../../packages/local-graph/src/claims.ts
|
|
27295
|
+
init_src();
|
|
27296
|
+
init_src();
|
|
27297
|
+
|
|
27298
|
+
// ../../packages/local-graph/src/crystallize.ts
|
|
27299
|
+
var LONG_TERM_SUPPORT = 2;
|
|
27300
|
+
var PERSISTENT_SUPPORT = 4;
|
|
27301
|
+
var PERSISTENT_CONFIDENCE = 0.85;
|
|
27302
|
+
var SCOPE_AXES = ["lang", "versionRange", "codebase", "regime"];
|
|
27303
|
+
function splittingAxis(a, b) {
|
|
27304
|
+
for (const ax of SCOPE_AXES) {
|
|
27305
|
+
if (a[ax] !== void 0 && b[ax] !== void 0 && a[ax] !== b[ax]) return ax;
|
|
27441
27306
|
}
|
|
27442
|
-
return
|
|
27307
|
+
return null;
|
|
27308
|
+
}
|
|
27309
|
+
function support(n) {
|
|
27310
|
+
return n.attrs["groundedSupport"] ?? 0;
|
|
27443
27311
|
}
|
|
27444
|
-
function
|
|
27445
|
-
const
|
|
27446
|
-
|
|
27447
|
-
|
|
27448
|
-
|
|
27449
|
-
|
|
27450
|
-
|
|
27451
|
-
|
|
27452
|
-
|
|
27453
|
-
|
|
27454
|
-
|
|
27455
|
-
|
|
27456
|
-
|
|
27457
|
-
|
|
27458
|
-
|
|
27312
|
+
function sourcesCount(n) {
|
|
27313
|
+
const s = n.attrs["sources"];
|
|
27314
|
+
return Array.isArray(s) ? s.length : 0;
|
|
27315
|
+
}
|
|
27316
|
+
function crystallize(store2, opts = { ts: 0 }) {
|
|
27317
|
+
const claims = store2.findNodesByLabel("Claim");
|
|
27318
|
+
const result = {
|
|
27319
|
+
evaluated: claims.length,
|
|
27320
|
+
promoted: [],
|
|
27321
|
+
derived: [],
|
|
27322
|
+
plural: [],
|
|
27323
|
+
discriminators: [],
|
|
27324
|
+
contested: []
|
|
27325
|
+
};
|
|
27326
|
+
for (const c of claims) {
|
|
27327
|
+
const contradictions = store2.outEdges(c.id, ["CONTRADICTS"]).concat(store2.inEdges(c.id, ["CONTRADICTS"])).length;
|
|
27328
|
+
const s = support(c) + Math.max(0, sourcesCount(c) - 1);
|
|
27329
|
+
const f = contradictions;
|
|
27330
|
+
const confidence = (1 + s) / (2 + s + f);
|
|
27331
|
+
store2.updateNode(c.id, {
|
|
27332
|
+
attrs: { ...c.attrs, confidence },
|
|
27333
|
+
lastUpdatedAt: opts.ts
|
|
27334
|
+
});
|
|
27335
|
+
const sup = support(c);
|
|
27336
|
+
let tier = c.memoryTier;
|
|
27337
|
+
if (sup >= PERSISTENT_SUPPORT && confidence >= PERSISTENT_CONFIDENCE) tier = "persistent";
|
|
27338
|
+
else if (sup >= LONG_TERM_SUPPORT) tier = "long-term";
|
|
27339
|
+
if (tier !== c.memoryTier) {
|
|
27340
|
+
store2.updateNode(c.id, { attrs: { ...store2.getNode(c.id).attrs }, memoryTier: tier, lastUpdatedAt: opts.ts });
|
|
27341
|
+
result.promoted.push(c.id);
|
|
27459
27342
|
}
|
|
27460
|
-
if (p.attrs["resolutionSuspect"] === true) report.alreadySuspect++;
|
|
27461
|
-
const attrs = { ...p.attrs };
|
|
27462
|
-
delete attrs["resolvedAt"];
|
|
27463
|
-
attrs["reopenedFrom"] = "auto-close";
|
|
27464
|
-
attrs["reopenedAt"] = t;
|
|
27465
|
-
store2.updateNode(p.id, { attrs, lastUpdatedAt: t });
|
|
27466
|
-
report.reopened++;
|
|
27467
27343
|
}
|
|
27468
|
-
|
|
27469
|
-
|
|
27470
|
-
|
|
27471
|
-
|
|
27472
|
-
|
|
27473
|
-
|
|
27474
|
-
|
|
27475
|
-
|
|
27476
|
-
|
|
27477
|
-
|
|
27478
|
-
|
|
27479
|
-
|
|
27480
|
-
|
|
27481
|
-
if (
|
|
27482
|
-
|
|
27483
|
-
|
|
27484
|
-
|
|
27485
|
-
if (sym && changedAt > since) {
|
|
27486
|
-
edited = true;
|
|
27487
|
-
symName = sym.description;
|
|
27488
|
-
symRelPath = sym.attrs["relPath"] ?? void 0;
|
|
27489
|
-
break;
|
|
27344
|
+
const seenPair = /* @__PURE__ */ new Set();
|
|
27345
|
+
for (const c of claims) {
|
|
27346
|
+
for (const e of store2.outEdges(c.id, ["CONTRADICTS"])) {
|
|
27347
|
+
const key = [e.from, e.to].sort().join("|");
|
|
27348
|
+
if (seenPair.has(key)) continue;
|
|
27349
|
+
seenPair.add(key);
|
|
27350
|
+
const a = store2.getNode(e.from);
|
|
27351
|
+
const b = store2.getNode(e.to);
|
|
27352
|
+
if (!a || !b) continue;
|
|
27353
|
+
const kind = mergeTruthKind(
|
|
27354
|
+
a.attrs["truthKind"],
|
|
27355
|
+
b.attrs["truthKind"]
|
|
27356
|
+
);
|
|
27357
|
+
if (kind === "objective") {
|
|
27358
|
+
markBoth(store2, a, b, { needsDiscriminator: true }, opts.ts);
|
|
27359
|
+
result.discriminators.push(a.id, b.id);
|
|
27360
|
+
continue;
|
|
27490
27361
|
}
|
|
27491
|
-
|
|
27492
|
-
|
|
27493
|
-
|
|
27494
|
-
|
|
27495
|
-
|
|
27496
|
-
|
|
27497
|
-
|
|
27498
|
-
|
|
27499
|
-
|
|
27500
|
-
|
|
27501
|
-
|
|
27502
|
-
|
|
27503
|
-
|
|
27504
|
-
|
|
27505
|
-
|
|
27506
|
-
|
|
27507
|
-
|
|
27508
|
-
|
|
27509
|
-
|
|
27510
|
-
|
|
27511
|
-
|
|
27362
|
+
if (kind === "taste") {
|
|
27363
|
+
markBoth(store2, a, b, { crystallized: "plural" }, opts.ts);
|
|
27364
|
+
result.plural.push(a.id, b.id);
|
|
27365
|
+
continue;
|
|
27366
|
+
}
|
|
27367
|
+
const axis = splittingAxis(
|
|
27368
|
+
a.attrs["scope"] ?? {},
|
|
27369
|
+
b.attrs["scope"] ?? {}
|
|
27370
|
+
);
|
|
27371
|
+
if (axis && support(a) > 0 && support(b) > 0) {
|
|
27372
|
+
markBoth(store2, a, b, { crystallized: "derived", splitAxis: axis }, opts.ts);
|
|
27373
|
+
result.derived.push(a.id, b.id);
|
|
27374
|
+
} else if (axis) {
|
|
27375
|
+
markBoth(store2, a, b, { crystallized: "hypothesis" }, opts.ts);
|
|
27376
|
+
} else {
|
|
27377
|
+
markBoth(
|
|
27378
|
+
store2,
|
|
27379
|
+
a,
|
|
27380
|
+
b,
|
|
27381
|
+
{
|
|
27382
|
+
revisit: true,
|
|
27383
|
+
revisitReason: "contested: contradictory contextual claim at the same scope, no splitting axis",
|
|
27384
|
+
revisitSinceTs: opts.ts
|
|
27385
|
+
},
|
|
27386
|
+
opts.ts
|
|
27387
|
+
);
|
|
27388
|
+
result.contested.push(a.id, b.id);
|
|
27512
27389
|
}
|
|
27513
|
-
// No `lastUpdatedAt`: raising a question is bookkeeping, not a new
|
|
27514
|
-
// observation of the problem, and the passive block's recent-problems band
|
|
27515
|
-
// sorts on that field (see clearSettledFixCandidates).
|
|
27516
|
-
});
|
|
27517
|
-
resolved++;
|
|
27518
|
-
}
|
|
27519
|
-
return resolved;
|
|
27520
|
-
}
|
|
27521
|
-
var FIX_CANDIDATE_ASK_LIMIT = 5;
|
|
27522
|
-
function clearSettledFixCandidates(store2, t) {
|
|
27523
|
-
const report = { answered: 0, ignored: 0, unsubstantiated: 0, standing: 0 };
|
|
27524
|
-
for (const p of store2.findNodesByLabel("Problem")) {
|
|
27525
|
-
if (p.attrs["fixCandidateAt"] === void 0) continue;
|
|
27526
|
-
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");
|
|
27527
|
-
const unsubstantiated = fileAnchors.length > 0 && fileAnchors.every((f) => f.attrs["contentChangedAt"] === void 0);
|
|
27528
|
-
const answered = p.attrs["resolvedAt"] != null || store2.outEdges(p.id, ["SOLVED_BY"]).some((e) => {
|
|
27529
|
-
const s = store2.getNode(e.to);
|
|
27530
|
-
return s !== null && !String(s.description ?? "").startsWith(AUTO_MINT_PREFIX);
|
|
27531
|
-
});
|
|
27532
|
-
const base = Number(
|
|
27533
|
-
p.attrs["fixCandidateShownBase"] ?? p.attrs["shownCount"] ?? 0
|
|
27534
|
-
);
|
|
27535
|
-
const shownSinceAsk = Number(p.attrs["shownCount"] ?? 0) - base;
|
|
27536
|
-
const ignored = shownSinceAsk >= FIX_CANDIDATE_ASK_LIMIT;
|
|
27537
|
-
if (!answered && !ignored && !unsubstantiated) {
|
|
27538
|
-
report.standing++;
|
|
27539
|
-
continue;
|
|
27540
27390
|
}
|
|
27541
|
-
const attrs = { ...p.attrs };
|
|
27542
|
-
delete attrs["fixCandidateAt"];
|
|
27543
|
-
delete attrs["fixCandidateSymbol"];
|
|
27544
|
-
delete attrs["fixCandidateFile"];
|
|
27545
|
-
attrs["fixCandidateClearedAt"] = t;
|
|
27546
|
-
store2.updateNode(p.id, { attrs });
|
|
27547
|
-
if (answered) report.answered++;
|
|
27548
|
-
else if (ignored) report.ignored++;
|
|
27549
|
-
else report.unsubstantiated++;
|
|
27550
27391
|
}
|
|
27551
|
-
return
|
|
27392
|
+
return result;
|
|
27552
27393
|
}
|
|
27553
|
-
function
|
|
27554
|
-
|
|
27555
|
-
if (
|
|
27556
|
-
|
|
27557
|
-
|
|
27558
|
-
|
|
27559
|
-
|
|
27560
|
-
|
|
27561
|
-
|
|
27394
|
+
function mergeTruthKind(a, b) {
|
|
27395
|
+
if (a === "objective" || b === "objective") return "objective";
|
|
27396
|
+
if (a === "contextual" || b === "contextual") return "contextual";
|
|
27397
|
+
return "taste";
|
|
27398
|
+
}
|
|
27399
|
+
function markBoth(store2, a, b, patch, ts) {
|
|
27400
|
+
for (const n of [a, b]) {
|
|
27401
|
+
const fresh = store2.getNode(n.id);
|
|
27402
|
+
store2.updateNode(n.id, { attrs: { ...fresh.attrs, ...patch }, lastUpdatedAt: ts });
|
|
27403
|
+
}
|
|
27562
27404
|
}
|
|
27563
27405
|
|
|
27564
|
-
// ../../packages/local-graph/src/
|
|
27406
|
+
// ../../packages/local-graph/src/design-problem.ts
|
|
27407
|
+
init_src();
|
|
27565
27408
|
init_src();
|
|
27566
27409
|
|
|
27567
|
-
// ../../packages/local-graph/src/
|
|
27410
|
+
// ../../packages/local-graph/src/problem-package-link.ts
|
|
27568
27411
|
init_src();
|
|
27569
|
-
|
|
27570
|
-
|
|
27571
|
-
|
|
27412
|
+
var NAME_SEPARATOR = /[@/\-._]/;
|
|
27413
|
+
var MIN_NAME_LEN = 3;
|
|
27414
|
+
function buildPackageIndex(store2) {
|
|
27415
|
+
const idx = /* @__PURE__ */ new Map();
|
|
27416
|
+
for (const n of store2.findNodesByLabel("Package")) {
|
|
27417
|
+
const name2 = String(n.attrs["name"] ?? "").trim();
|
|
27418
|
+
if (!name2) continue;
|
|
27419
|
+
const key = name2.toLowerCase();
|
|
27420
|
+
const prev = idx.get(key);
|
|
27421
|
+
if (!prev || n.attrs["resolved"] === true && prev.attrs["resolved"] !== true) {
|
|
27422
|
+
idx.set(key, n);
|
|
27423
|
+
}
|
|
27424
|
+
}
|
|
27425
|
+
return idx;
|
|
27426
|
+
}
|
|
27427
|
+
function escapeRe2(s) {
|
|
27428
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
27429
|
+
}
|
|
27430
|
+
function matchPackagesInText(text, index) {
|
|
27431
|
+
const hay = text.toLowerCase();
|
|
27432
|
+
const out2 = [];
|
|
27433
|
+
const seen = /* @__PURE__ */ new Set();
|
|
27434
|
+
for (const [key, node] of index) {
|
|
27435
|
+
if (key.length < MIN_NAME_LEN) continue;
|
|
27436
|
+
if (!hay.includes(key)) continue;
|
|
27437
|
+
const esc2 = escapeRe2(key);
|
|
27438
|
+
const bounded = new RegExp(`(?<![\\w@/.\\-])${esc2}(?![\\w])`);
|
|
27439
|
+
if (!bounded.test(hay)) continue;
|
|
27440
|
+
const verMatch = new RegExp(`${esc2}\\s*@?\\s*v?(\\d+(?:\\.\\d+){0,2})\\b`).exec(hay);
|
|
27441
|
+
const hasSep = NAME_SEPARATOR.test(key);
|
|
27442
|
+
if (!hasSep && !verMatch) continue;
|
|
27443
|
+
if (seen.has(node.id)) continue;
|
|
27444
|
+
seen.add(node.id);
|
|
27445
|
+
out2.push(verMatch ? { node, version: verMatch[1] } : { node });
|
|
27446
|
+
}
|
|
27447
|
+
return out2;
|
|
27448
|
+
}
|
|
27449
|
+
function linkProblemToPackages(store2, problemId, text, ts, index) {
|
|
27450
|
+
const p = store2.getNode(problemId);
|
|
27451
|
+
if (!p || p.label !== "Problem") return { linked: [] };
|
|
27452
|
+
const idx = index ?? buildPackageIndex(store2);
|
|
27453
|
+
if (idx.size === 0) return { linked: [] };
|
|
27454
|
+
const already = new Set(store2.outEdges(problemId, ["DEPENDS_ON"]).map((e) => e.to));
|
|
27455
|
+
const linked = [];
|
|
27456
|
+
for (const { node, version: version3 } of matchPackagesInText(text, idx)) {
|
|
27457
|
+
if (already.has(node.id)) continue;
|
|
27458
|
+
addDependency(store2, {
|
|
27459
|
+
fromId: problemId,
|
|
27460
|
+
toId: node.id,
|
|
27461
|
+
relation: "depends-on",
|
|
27462
|
+
evidence: version3 ? `named in problem (@${version3})` : "named in problem statement",
|
|
27463
|
+
ts,
|
|
27464
|
+
extractionSource: "daemon-extracted"
|
|
27465
|
+
});
|
|
27466
|
+
already.add(node.id);
|
|
27467
|
+
linked.push(node.id);
|
|
27468
|
+
}
|
|
27469
|
+
return { linked };
|
|
27572
27470
|
}
|
|
27573
|
-
|
|
27574
|
-
|
|
27575
|
-
"
|
|
27576
|
-
);
|
|
27577
|
-
|
|
27578
|
-
const byPath = /* @__PURE__ */ new Map();
|
|
27579
|
-
for (const f of store2.findNodesByLabel("File")) {
|
|
27580
|
-
const path = String(f.attrs["relPath"] ?? f.description ?? "");
|
|
27581
|
-
if (path) byPath.set(path, { id: f.id, path });
|
|
27471
|
+
function problemText(store2, p) {
|
|
27472
|
+
const parts2 = [p.description];
|
|
27473
|
+
for (const e of store2.outEdges(p.id, ["CAUSED_BY"])) {
|
|
27474
|
+
const c = store2.getNode(e.to);
|
|
27475
|
+
if (c) parts2.push(c.description);
|
|
27582
27476
|
}
|
|
27583
|
-
return (
|
|
27584
|
-
const exact = byPath.get(named);
|
|
27585
|
-
if (exact) return exact;
|
|
27586
|
-
const hits = [...byPath.values()].filter(
|
|
27587
|
-
(f) => f.path.endsWith(`/${named}`) || named.endsWith(`/${f.path}`)
|
|
27588
|
-
);
|
|
27589
|
-
return hits.length === 1 ? hits[0] : null;
|
|
27590
|
-
};
|
|
27477
|
+
return parts2.join(" \n ");
|
|
27591
27478
|
}
|
|
27592
|
-
function
|
|
27593
|
-
|
|
27594
|
-
|
|
27595
|
-
|
|
27479
|
+
function backfillProblemPackageLinks(store2, ts) {
|
|
27480
|
+
const report = { problems: 0, edges: 0 };
|
|
27481
|
+
const idx = buildPackageIndex(store2);
|
|
27482
|
+
if (idx.size === 0) return report;
|
|
27483
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
27484
|
+
if (p.attrs["resolvedAs"] === "false_positive") continue;
|
|
27485
|
+
const { linked } = linkProblemToPackages(store2, p.id, problemText(store2, p), ts, idx);
|
|
27486
|
+
if (linked.length > 0) {
|
|
27487
|
+
report.problems++;
|
|
27488
|
+
report.edges += linked.length;
|
|
27489
|
+
}
|
|
27596
27490
|
}
|
|
27597
|
-
return
|
|
27491
|
+
return report;
|
|
27598
27492
|
}
|
|
27599
|
-
function
|
|
27600
|
-
return {
|
|
27601
|
-
|
|
27602
|
-
|
|
27603
|
-
|
|
27604
|
-
|
|
27605
|
-
|
|
27606
|
-
|
|
27493
|
+
function langNodeId(name2) {
|
|
27494
|
+
return `lang:${name2.trim().toLowerCase()}`;
|
|
27495
|
+
}
|
|
27496
|
+
function specToPackageName(spec) {
|
|
27497
|
+
const s = spec.trim();
|
|
27498
|
+
if (!s || s.startsWith(".") || s.startsWith("/") || s.startsWith("node:")) return null;
|
|
27499
|
+
const parts2 = s.split("/");
|
|
27500
|
+
return (s.startsWith("@") ? parts2.slice(0, 2).join("/") : parts2[0]).toLowerCase();
|
|
27501
|
+
}
|
|
27502
|
+
function mergeContextEdge(store2, from, to, type, ts) {
|
|
27503
|
+
store2.mergeEdge({
|
|
27504
|
+
id: `edge_${digest({ from, type, to })}`.slice(0, 24),
|
|
27505
|
+
from,
|
|
27506
|
+
to,
|
|
27507
|
+
type,
|
|
27607
27508
|
confidence: 0.4,
|
|
27608
|
-
extractionSource: "
|
|
27509
|
+
extractionSource: "daemon-extracted",
|
|
27609
27510
|
createdAt: ts,
|
|
27610
27511
|
lastSeenAt: ts,
|
|
27611
27512
|
navSuccesses: 0,
|
|
27612
27513
|
navFailures: 0,
|
|
27613
|
-
attrs: {
|
|
27614
|
-
};
|
|
27514
|
+
attrs: { provisional: true, structural: true }
|
|
27515
|
+
});
|
|
27615
27516
|
}
|
|
27616
|
-
function
|
|
27617
|
-
const report = {
|
|
27618
|
-
|
|
27619
|
-
|
|
27620
|
-
|
|
27621
|
-
|
|
27622
|
-
|
|
27623
|
-
|
|
27624
|
-
const
|
|
27625
|
-
for (const
|
|
27626
|
-
|
|
27627
|
-
|
|
27628
|
-
const
|
|
27629
|
-
if (
|
|
27630
|
-
|
|
27631
|
-
|
|
27632
|
-
|
|
27633
|
-
|
|
27634
|
-
|
|
27635
|
-
|
|
27636
|
-
|
|
27637
|
-
|
|
27638
|
-
|
|
27639
|
-
|
|
27640
|
-
|
|
27641
|
-
|
|
27642
|
-
|
|
27643
|
-
|
|
27644
|
-
|
|
27645
|
-
|
|
27646
|
-
|
|
27647
|
-
|
|
27648
|
-
|
|
27649
|
-
continue;
|
|
27650
|
-
}
|
|
27651
|
-
const guessedPaths = legacy.map((e) => {
|
|
27652
|
-
const f = store2.getNode(e.to);
|
|
27653
|
-
return String(f?.attrs["relPath"] ?? f?.description ?? "");
|
|
27654
|
-
});
|
|
27655
|
-
if (named.some((x) => guessedPaths.some((g) => g.endsWith(x) || x.endsWith(g)))) {
|
|
27656
|
-
report.alreadyCorrect++;
|
|
27657
|
-
continue;
|
|
27658
|
-
}
|
|
27659
|
-
const target = named.map(resolvePath).find((f) => f !== null);
|
|
27660
|
-
if (!target) {
|
|
27661
|
-
report.pathUnknown++;
|
|
27662
|
-
continue;
|
|
27517
|
+
function deriveContextFromAnchors(store2, problemId, ts, pkgIndex) {
|
|
27518
|
+
const report = { languages: [], packages: [] };
|
|
27519
|
+
const p = store2.getNode(problemId);
|
|
27520
|
+
if (!p || p.label !== "Problem" && p.label !== "Vulnerability") return report;
|
|
27521
|
+
const files = store2.outEdges(problemId, ["ANCHORED_AT"]).map((e) => store2.getNode(e.to)).filter((n) => !!n && n.label === "File");
|
|
27522
|
+
if (files.length === 0) return report;
|
|
27523
|
+
const idx = pkgIndex ?? buildPackageIndex(store2);
|
|
27524
|
+
const haveLang = new Set(store2.outEdges(problemId, ["OCCURS_IN"]).map((e) => e.to));
|
|
27525
|
+
const havePkg = new Set(store2.outEdges(problemId, ["DEPENDS_ON"]).map((e) => e.to));
|
|
27526
|
+
for (const file2 of files) {
|
|
27527
|
+
const lang = String(file2.attrs["language"] ?? "").trim();
|
|
27528
|
+
if (lang) {
|
|
27529
|
+
const langId = langNodeId(lang);
|
|
27530
|
+
if (!store2.getNode(langId)) {
|
|
27531
|
+
store2.mergeNode({
|
|
27532
|
+
id: langId,
|
|
27533
|
+
label: "Language",
|
|
27534
|
+
description: lang,
|
|
27535
|
+
extractionConfidence: 1,
|
|
27536
|
+
extractionSource: "daemon-extracted",
|
|
27537
|
+
embedding: [],
|
|
27538
|
+
cumulativeSurprise: 0,
|
|
27539
|
+
peakSurprise: 0,
|
|
27540
|
+
cumulativeHits: 0,
|
|
27541
|
+
lastUpdatedAt: ts,
|
|
27542
|
+
createdAt: ts,
|
|
27543
|
+
memoryTier: "short-term",
|
|
27544
|
+
pageRank: 0,
|
|
27545
|
+
isLandmark: false,
|
|
27546
|
+
community: null,
|
|
27547
|
+
stability: "stable",
|
|
27548
|
+
attrs: { name: lang, workspaceId: file2.attrs["workspaceId"] }
|
|
27549
|
+
});
|
|
27663
27550
|
}
|
|
27664
|
-
|
|
27665
|
-
|
|
27666
|
-
|
|
27551
|
+
if (!haveLang.has(langId)) {
|
|
27552
|
+
mergeContextEdge(store2, problemId, langId, "OCCURS_IN", ts);
|
|
27553
|
+
haveLang.add(langId);
|
|
27554
|
+
report.languages.push(langId);
|
|
27667
27555
|
}
|
|
27668
|
-
report.retiredGuesses += legacy.filter((e) => e.to !== target.id).length;
|
|
27669
|
-
report.repaired.push({
|
|
27670
|
-
problemId: p.id,
|
|
27671
|
-
problem: p.description,
|
|
27672
|
-
guessed: guessedPaths[0] ?? "",
|
|
27673
|
-
restated: target.path
|
|
27674
|
-
});
|
|
27675
|
-
} catch {
|
|
27676
27556
|
}
|
|
27677
|
-
|
|
27678
|
-
|
|
27679
|
-
|
|
27680
|
-
|
|
27681
|
-
|
|
27682
|
-
|
|
27683
|
-
|
|
27684
|
-
|
|
27685
|
-
|
|
27686
|
-
|
|
27687
|
-
|
|
27688
|
-
|
|
27689
|
-
|
|
27690
|
-
|
|
27691
|
-
|
|
27692
|
-
|
|
27693
|
-
|
|
27694
|
-
|
|
27695
|
-
|
|
27696
|
-
|
|
27697
|
-
|
|
27698
|
-
|
|
27699
|
-
|
|
27700
|
-
|
|
27701
|
-
|
|
27702
|
-
|
|
27703
|
-
|
|
27704
|
-
|
|
27705
|
-
|
|
27706
|
-
|
|
27707
|
-
|
|
27708
|
-
if (path) recordAnchorHint(store2, p.id, path, "legacy", ts);
|
|
27709
|
-
store2.closeEdge(e.id, ts);
|
|
27710
|
-
report.demotedEdges++;
|
|
27711
|
-
}
|
|
27712
|
-
if (retired.length > 0) report.demotedProblems++;
|
|
27713
|
-
continue;
|
|
27714
|
-
}
|
|
27715
|
-
for (const e of legacy) {
|
|
27716
|
-
store2.mergeEdge({ ...e, attrs: { ...e.attrs, anchorProvenance: "legacy" }, lastSeenAt: ts });
|
|
27717
|
-
report.labeledEdges++;
|
|
27718
|
-
}
|
|
27719
|
-
for (const se of store2.outEdges(p.id, ["SOLVED_BY"])) {
|
|
27720
|
-
const sol = store2.getNode(se.to);
|
|
27721
|
-
if (!sol || !sol.description.startsWith(AUTO_MINT_PREFIX)) continue;
|
|
27722
|
-
const guessedAnchor = sol.description.slice(AUTO_MINT_PREFIX.length);
|
|
27723
|
-
store2.updateNode(sol.id, {
|
|
27724
|
-
attrs: { ...sol.attrs, provisional: true, resolutionSuspect: true },
|
|
27725
|
-
lastUpdatedAt: ts
|
|
27726
|
-
});
|
|
27727
|
-
markRevisit(
|
|
27728
|
-
store2,
|
|
27729
|
-
sol,
|
|
27730
|
-
`auto-closed off a pre-provenance anchor (guessed ${guessedAnchor}) \u2014 confirm the problem is really fixed, or reopen it`,
|
|
27731
|
-
ts,
|
|
27732
|
-
// "or reopen it" is half the ask, and the nightly reopen takes that
|
|
27733
|
-
// branch — so record what would answer this, or the flag can never be
|
|
27734
|
-
// lowered and the band fills with settled questions.
|
|
27735
|
-
"parentProblemOpen"
|
|
27736
|
-
);
|
|
27737
|
-
store2.updateNode(p.id, {
|
|
27738
|
-
attrs: { ...p.attrs, resolutionSuspect: true },
|
|
27739
|
-
lastUpdatedAt: ts
|
|
27740
|
-
});
|
|
27741
|
-
report.suspects.push({
|
|
27742
|
-
problemId: p.id,
|
|
27743
|
-
problem: p.description,
|
|
27744
|
-
solutionId: sol.id,
|
|
27745
|
-
guessedAnchor
|
|
27746
|
-
});
|
|
27747
|
-
}
|
|
27748
|
-
} catch {
|
|
27557
|
+
for (const e of store2.outEdges(file2.id, ["IMPORTS"])) {
|
|
27558
|
+
const mod = store2.getNode(e.to);
|
|
27559
|
+
if (!mod) continue;
|
|
27560
|
+
const name2 = specToPackageName(mod.description);
|
|
27561
|
+
if (!name2) continue;
|
|
27562
|
+
const pkg = idx.get(name2);
|
|
27563
|
+
if (!pkg || havePkg.has(pkg.id)) continue;
|
|
27564
|
+
addDependency(store2, {
|
|
27565
|
+
fromId: problemId,
|
|
27566
|
+
toId: pkg.id,
|
|
27567
|
+
relation: "depends-on",
|
|
27568
|
+
evidence: "imported by the anchored code",
|
|
27569
|
+
ts,
|
|
27570
|
+
extractionSource: "daemon-extracted"
|
|
27571
|
+
});
|
|
27572
|
+
havePkg.add(pkg.id);
|
|
27573
|
+
report.packages.push(pkg.id);
|
|
27574
|
+
}
|
|
27575
|
+
}
|
|
27576
|
+
return report;
|
|
27577
|
+
}
|
|
27578
|
+
function backfillProblemContext(store2, ts) {
|
|
27579
|
+
const report = { problems: 0, edges: 0 };
|
|
27580
|
+
const idx = buildPackageIndex(store2);
|
|
27581
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
27582
|
+
if (p.attrs["resolvedAs"] === "false_positive") continue;
|
|
27583
|
+
const r = deriveContextFromAnchors(store2, p.id, ts, idx);
|
|
27584
|
+
const n = r.languages.length + r.packages.length;
|
|
27585
|
+
if (n > 0) {
|
|
27586
|
+
report.problems++;
|
|
27587
|
+
report.edges += n;
|
|
27749
27588
|
}
|
|
27750
27589
|
}
|
|
27751
27590
|
return report;
|
|
27752
27591
|
}
|
|
27753
27592
|
|
|
27754
|
-
// ../../packages/local-graph/src/
|
|
27593
|
+
// ../../packages/local-graph/src/design-problem.ts
|
|
27755
27594
|
init_src2();
|
|
27756
|
-
init_src();
|
|
27757
27595
|
|
|
27758
|
-
// ../../packages/local-graph/src/
|
|
27596
|
+
// ../../packages/local-graph/src/problem-dedup.ts
|
|
27759
27597
|
init_src();
|
|
27760
|
-
|
|
27761
|
-
|
|
27762
|
-
|
|
27763
|
-
|
|
27764
|
-
|
|
27765
|
-
|
|
27766
|
-
|
|
27767
|
-
|
|
27768
|
-
|
|
27769
|
-
|
|
27770
|
-
|
|
27771
|
-
|
|
27772
|
-
|
|
27773
|
-
|
|
27774
|
-
|
|
27775
|
-
const
|
|
27776
|
-
|
|
27777
|
-
|
|
27778
|
-
|
|
27779
|
-
|
|
27780
|
-
|
|
27781
|
-
|
|
27782
|
-
|
|
27783
|
-
|
|
27784
|
-
|
|
27785
|
-
|
|
27786
|
-
|
|
27787
|
-
|
|
27788
|
-
|
|
27789
|
-
|
|
27598
|
+
init_src();
|
|
27599
|
+
var REDIRECT_EDGES = [
|
|
27600
|
+
"CAUSED_BY",
|
|
27601
|
+
"SOLVED_BY",
|
|
27602
|
+
"FIXED_BY",
|
|
27603
|
+
"ANCHORED_AT",
|
|
27604
|
+
"MANIFESTED_IN",
|
|
27605
|
+
"EVIDENCED_BY"
|
|
27606
|
+
];
|
|
27607
|
+
function corroborations(n) {
|
|
27608
|
+
return Number(n.attrs["corroborations"] ?? 0);
|
|
27609
|
+
}
|
|
27610
|
+
function overlap(a, b) {
|
|
27611
|
+
if (a.size === 0 || b.size === 0) return 0;
|
|
27612
|
+
let inter = 0;
|
|
27613
|
+
for (const x of a) if (b.has(x)) inter++;
|
|
27614
|
+
return inter / Math.min(a.size, b.size);
|
|
27615
|
+
}
|
|
27616
|
+
function mergeDuplicateProblems(store2, opts) {
|
|
27617
|
+
const minOverlap = opts.minOverlap ?? 0.85;
|
|
27618
|
+
const minTokens = opts.minTokens ?? 4;
|
|
27619
|
+
const report = { clusters: 0, merged: 0 };
|
|
27620
|
+
const open = store2.findNodesByLabel("Problem").filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0);
|
|
27621
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
27622
|
+
for (const p of open) tokens.set(p.id, new Set(retrievalTokens(p.description)));
|
|
27623
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
27624
|
+
store2.transaction(() => {
|
|
27625
|
+
for (let i2 = 0; i2 < open.length; i2++) {
|
|
27626
|
+
const a = open[i2];
|
|
27627
|
+
if (consumed.has(a.id)) continue;
|
|
27628
|
+
const cluster = [a];
|
|
27629
|
+
const ta = tokens.get(a.id);
|
|
27630
|
+
for (let j = i2 + 1; j < open.length; j++) {
|
|
27631
|
+
const b = open[j];
|
|
27632
|
+
if (consumed.has(b.id)) continue;
|
|
27633
|
+
const tb = tokens.get(b.id);
|
|
27634
|
+
if (Math.min(ta.size, tb.size) < minTokens) continue;
|
|
27635
|
+
if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
|
|
27636
|
+
if (overlap(ta, tb) >= minOverlap) {
|
|
27637
|
+
cluster.push(b);
|
|
27638
|
+
consumed.add(b.id);
|
|
27639
|
+
}
|
|
27640
|
+
}
|
|
27641
|
+
if (cluster.length < 2) continue;
|
|
27642
|
+
report.clusters++;
|
|
27643
|
+
cluster.sort((x, y) => corroborations(y) - corroborations(x) || x.createdAt - y.createdAt);
|
|
27644
|
+
const survivor = cluster[0];
|
|
27645
|
+
for (const dup of cluster.slice(1)) {
|
|
27646
|
+
foldDuplicateProblem(store2, dup, survivor, opts.ts);
|
|
27647
|
+
report.merged++;
|
|
27648
|
+
}
|
|
27790
27649
|
}
|
|
27791
|
-
|
|
27792
|
-
|
|
27650
|
+
});
|
|
27651
|
+
return report;
|
|
27652
|
+
}
|
|
27653
|
+
function foldDuplicateProblem(store2, dup, survivor, ts) {
|
|
27654
|
+
const surv = store2.getNode(survivor.id);
|
|
27655
|
+
if (!surv) return;
|
|
27656
|
+
const sources = new Set(surv.attrs["sources"] ?? []);
|
|
27657
|
+
for (const s of dup.attrs["sources"] ?? []) sources.add(s);
|
|
27658
|
+
const dupSeq = dup.attrs["createdAtSeq"] ?? store2.currentIngestSeq();
|
|
27659
|
+
const exposure = priorExposure(store2, survivor.id, dupSeq);
|
|
27660
|
+
const expField = `reinforce${exposure[0].toUpperCase()}${exposure.slice(1)}Count`;
|
|
27661
|
+
store2.updateNode(survivor.id, {
|
|
27662
|
+
attrs: {
|
|
27663
|
+
...surv.attrs,
|
|
27664
|
+
sources: [...sources],
|
|
27665
|
+
corroborations: corroborations(surv) + corroborations(dup) + 1,
|
|
27666
|
+
[expField]: (surv.attrs[expField] ?? 0) + 1,
|
|
27667
|
+
lastReinforceExposure: exposure
|
|
27668
|
+
},
|
|
27669
|
+
cumulativeHits: (surv.cumulativeHits ?? 0) + (dup.cumulativeHits ?? 0),
|
|
27670
|
+
lastUpdatedAt: ts
|
|
27671
|
+
});
|
|
27672
|
+
for (const e of store2.outEdges(dup.id, REDIRECT_EDGES)) {
|
|
27673
|
+
if (e.to === survivor.id) continue;
|
|
27674
|
+
const id = `edge_${digest({ from: survivor.id, type: e.type, to: e.to })}`.slice(0, 24);
|
|
27675
|
+
if (store2.getEdge(id)) continue;
|
|
27676
|
+
const redirected = {
|
|
27677
|
+
...e,
|
|
27678
|
+
id,
|
|
27679
|
+
from: survivor.id,
|
|
27680
|
+
createdAt: ts,
|
|
27681
|
+
lastSeenAt: ts
|
|
27682
|
+
};
|
|
27683
|
+
store2.mergeEdge(redirected);
|
|
27793
27684
|
}
|
|
27794
|
-
|
|
27795
|
-
|
|
27796
|
-
|
|
27797
|
-
|
|
27798
|
-
|
|
27799
|
-
|
|
27800
|
-
|
|
27801
|
-
|
|
27802
|
-
|
|
27803
|
-
|
|
27804
|
-
|
|
27805
|
-
|
|
27806
|
-
|
|
27807
|
-
|
|
27808
|
-
|
|
27809
|
-
|
|
27685
|
+
store2.updateNode(dup.id, {
|
|
27686
|
+
attrs: { ...dup.attrs, mergedInto: survivor.id, resolvedAs: "duplicate", resolvedAt: ts },
|
|
27687
|
+
lastUpdatedAt: ts
|
|
27688
|
+
});
|
|
27689
|
+
store2.closeNode(dup.id, ts);
|
|
27690
|
+
}
|
|
27691
|
+
|
|
27692
|
+
// ../../packages/local-graph/src/design-problem.ts
|
|
27693
|
+
init_src();
|
|
27694
|
+
function isConstraintProblem(node) {
|
|
27695
|
+
return node.attrs["kind"] === "constraint";
|
|
27696
|
+
}
|
|
27697
|
+
var RECURRENCE_DEBOUNCE_MS = 60 * 60 * 1e3;
|
|
27698
|
+
function resolveFileNode(store2, path, workspaceId) {
|
|
27699
|
+
const want = path.trim().replace(/^\.?\//, "");
|
|
27700
|
+
for (const n of store2.findNodesByLabel("File")) {
|
|
27701
|
+
if (n.attrs["workspaceId"] && n.attrs["workspaceId"] !== workspaceId) continue;
|
|
27702
|
+
if (n.attrs["relPath"] === want || n.description === want) return n;
|
|
27810
27703
|
}
|
|
27811
|
-
|
|
27812
|
-
|
|
27813
|
-
|
|
27814
|
-
|
|
27815
|
-
|
|
27816
|
-
|
|
27817
|
-
|
|
27818
|
-
|
|
27819
|
-
|
|
27820
|
-
|
|
27821
|
-
|
|
27822
|
-
|
|
27823
|
-
|
|
27824
|
-
|
|
27825
|
-
|
|
27826
|
-
|
|
27827
|
-
|
|
27828
|
-
|
|
27829
|
-
}
|
|
27830
|
-
let diff = 0;
|
|
27831
|
-
for (let i2 = 0; i2 < n; i2++) diff += Math.abs(next[i2] - score2[i2]);
|
|
27832
|
-
const tmp = score2;
|
|
27833
|
-
score2 = next;
|
|
27834
|
-
next = tmp;
|
|
27835
|
-
if (diff < tol) {
|
|
27836
|
-
iter++;
|
|
27837
|
-
converged = true;
|
|
27704
|
+
return null;
|
|
27705
|
+
}
|
|
27706
|
+
var CITABLE_PRIOR_LABELS = /* @__PURE__ */ new Set([
|
|
27707
|
+
"Solution",
|
|
27708
|
+
"RootCause",
|
|
27709
|
+
"Pattern",
|
|
27710
|
+
"Technique",
|
|
27711
|
+
"AntiPattern"
|
|
27712
|
+
]);
|
|
27713
|
+
function isAutoMinted(n) {
|
|
27714
|
+
return isEditPlaceholderSolution(n);
|
|
27715
|
+
}
|
|
27716
|
+
function priorsForFile(store2, relPath) {
|
|
27717
|
+
const want = relPath.trim().replace(/^\.?\//, "");
|
|
27718
|
+
let file2 = null;
|
|
27719
|
+
for (const n of store2.findNodesByLabel("File")) {
|
|
27720
|
+
if (n.description === want || n.attrs["relPath"] === want) {
|
|
27721
|
+
file2 = n;
|
|
27838
27722
|
break;
|
|
27839
27723
|
}
|
|
27840
27724
|
}
|
|
27841
|
-
|
|
27842
|
-
|
|
27843
|
-
|
|
27844
|
-
|
|
27845
|
-
|
|
27846
|
-
|
|
27847
|
-
|
|
27848
|
-
|
|
27849
|
-
|
|
27850
|
-
|
|
27851
|
-
|
|
27852
|
-
|
|
27853
|
-
|
|
27725
|
+
if (!file2) return null;
|
|
27726
|
+
const openProblems = [];
|
|
27727
|
+
const constraints = [];
|
|
27728
|
+
const resolvedProblems = [];
|
|
27729
|
+
const seenProblem = /* @__PURE__ */ new Set();
|
|
27730
|
+
const related = /* @__PURE__ */ new Map();
|
|
27731
|
+
const anchorEdges = [
|
|
27732
|
+
...store2.inEdges(file2.id, ["ANCHORED_AT"]),
|
|
27733
|
+
...store2.outEdges(file2.id, ["DEFINES"]).flatMap((d) => store2.inEdges(d.to, ["ANCHORED_AT"]))
|
|
27734
|
+
];
|
|
27735
|
+
for (const e of anchorEdges) {
|
|
27736
|
+
const n = store2.getNode(e.from);
|
|
27737
|
+
if (!n) continue;
|
|
27738
|
+
if (n.label === "Problem" || n.label === "Vulnerability") {
|
|
27739
|
+
if (seenProblem.has(n.id)) continue;
|
|
27740
|
+
seenProblem.add(n.id);
|
|
27741
|
+
if (!n.attrs["resolvedAt"]) {
|
|
27742
|
+
(isConstraintProblem(n) ? constraints : openProblems).push(n);
|
|
27743
|
+
} else if (n.attrs["resolvedAs"] == null && n.attrs["mergedInto"] === void 0 && e.attrs?.["anchorProvenance"] !== "legacy") {
|
|
27744
|
+
resolvedProblems.push(n);
|
|
27745
|
+
}
|
|
27746
|
+
} else if (CITABLE_PRIOR_LABELS.has(n.label) && !isAutoMinted(n)) {
|
|
27747
|
+
related.set(n.id, n);
|
|
27854
27748
|
}
|
|
27855
27749
|
}
|
|
27856
|
-
|
|
27857
|
-
|
|
27858
|
-
|
|
27859
|
-
|
|
27860
|
-
|
|
27861
|
-
|
|
27750
|
+
const solutionsByProblem = /* @__PURE__ */ new Map();
|
|
27751
|
+
for (const p of [...openProblems, ...constraints, ...resolvedProblems]) {
|
|
27752
|
+
for (const e of store2.outEdges(p.id, ["SOLVED_BY", "CAUSED_BY", "INSTANCE_OF"])) {
|
|
27753
|
+
const n = store2.getNode(e.to);
|
|
27754
|
+
if (n && CITABLE_PRIOR_LABELS.has(n.label) && !isAutoMinted(n)) related.set(n.id, n);
|
|
27755
|
+
if (n && n.label === "Solution" && e.type === "SOLVED_BY" && !isAutoMinted(n)) {
|
|
27756
|
+
const list = solutionsByProblem.get(p.id) ?? [];
|
|
27757
|
+
if (!list.some((s) => s.id === n.id)) list.push(n);
|
|
27758
|
+
solutionsByProblem.set(p.id, list);
|
|
27759
|
+
}
|
|
27760
|
+
}
|
|
27862
27761
|
}
|
|
27863
|
-
|
|
27762
|
+
if (openProblems.length === 0 && constraints.length === 0 && resolvedProblems.length === 0 && related.size === 0)
|
|
27763
|
+
return null;
|
|
27764
|
+
const recentFirst = (a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0);
|
|
27765
|
+
openProblems.sort(recentFirst);
|
|
27766
|
+
constraints.sort(recentFirst);
|
|
27767
|
+
resolvedProblems.sort(
|
|
27768
|
+
(a, b) => Number(b.attrs["resolvedAt"] ?? 0) - Number(a.attrs["resolvedAt"] ?? 0)
|
|
27769
|
+
);
|
|
27770
|
+
return {
|
|
27771
|
+
file: file2,
|
|
27772
|
+
openProblems,
|
|
27773
|
+
constraints,
|
|
27774
|
+
resolvedProblems,
|
|
27775
|
+
related: [...related.values()],
|
|
27776
|
+
solutionsByProblem
|
|
27777
|
+
};
|
|
27864
27778
|
}
|
|
27865
|
-
|
|
27866
|
-
|
|
27867
|
-
|
|
27868
|
-
|
|
27869
|
-
|
|
27870
|
-
const
|
|
27871
|
-
|
|
27872
|
-
|
|
27873
|
-
|
|
27779
|
+
function anchorProblemToWorkingFile(store2, problemId, relPath, workspaceId, ts, provenance = "edited", opts = {}) {
|
|
27780
|
+
const p = store2.getNode(problemId);
|
|
27781
|
+
if (!p || p.label !== "Problem" && p.label !== "Vulnerability") return false;
|
|
27782
|
+
if (p.label === "Vulnerability" && provenance !== "witnessed") return false;
|
|
27783
|
+
if (!isAnchorableCodePath(relPath)) return false;
|
|
27784
|
+
const file2 = resolveFileNode(store2, relPath, workspaceId);
|
|
27785
|
+
if (!file2) return false;
|
|
27786
|
+
const already = new Set(store2.outEdges(problemId, ["ANCHORED_AT"]).map((e) => e.to));
|
|
27787
|
+
const confidence = provenance === "witnessed" ? 0.4 : 0.3;
|
|
27788
|
+
if (!already.has(file2.id)) {
|
|
27789
|
+
store2.mergeEdge({
|
|
27790
|
+
id: `edge_${digest({ from: problemId, type: "ANCHORED_AT", to: file2.id })}`.slice(0, 24),
|
|
27791
|
+
from: problemId,
|
|
27792
|
+
to: file2.id,
|
|
27793
|
+
type: "ANCHORED_AT",
|
|
27794
|
+
confidence,
|
|
27795
|
+
extractionSource: "agent-observed",
|
|
27796
|
+
createdAt: ts,
|
|
27797
|
+
lastSeenAt: ts,
|
|
27798
|
+
navSuccesses: 0,
|
|
27799
|
+
navFailures: 0,
|
|
27800
|
+
attrs: { provisional: true, captureTime: true, anchorProvenance: provenance }
|
|
27801
|
+
});
|
|
27874
27802
|
}
|
|
27875
|
-
|
|
27876
|
-
|
|
27877
|
-
|
|
27878
|
-
|
|
27879
|
-
|
|
27880
|
-
|
|
27881
|
-
|
|
27882
|
-
|
|
27803
|
+
if (opts.line !== void 0 && opts.fileText !== void 0) {
|
|
27804
|
+
const sym = resolveEnclosingSymbol(store2, file2.id, opts.line, opts.fileText);
|
|
27805
|
+
if (sym && !already.has(sym.id)) {
|
|
27806
|
+
store2.mergeEdge({
|
|
27807
|
+
id: `edge_${digest({ from: problemId, type: "ANCHORED_AT", to: sym.id })}`.slice(0, 24),
|
|
27808
|
+
from: problemId,
|
|
27809
|
+
to: sym.id,
|
|
27810
|
+
type: "ANCHORED_AT",
|
|
27811
|
+
confidence,
|
|
27812
|
+
extractionSource: "agent-observed",
|
|
27813
|
+
createdAt: ts,
|
|
27814
|
+
lastSeenAt: ts,
|
|
27815
|
+
navSuccesses: 0,
|
|
27816
|
+
navFailures: 0,
|
|
27817
|
+
attrs: {
|
|
27818
|
+
provisional: true,
|
|
27819
|
+
captureTime: true,
|
|
27820
|
+
anchorProvenance: provenance,
|
|
27821
|
+
anchorLine: opts.line,
|
|
27822
|
+
enclosingSymbol: true,
|
|
27823
|
+
// PJ-anchor-version: WHICH version of the symbol this was witnessed
|
|
27824
|
+
// against. `bodyHash` is the indexer's 16-hex sha256 of the body;
|
|
27825
|
+
// `headSha` is the checkout's HEAD at capture. Both opaque, both
|
|
27826
|
+
// cross the membrane on the anchor edge, and a teammate's reanchor
|
|
27827
|
+
// compares the hash with their own symbol to tell "current" from
|
|
27828
|
+
// "pre-change" — the branch hazard where a fix witnessed on feat/x
|
|
27829
|
+
// would otherwise read as a fact about the unchanged symbol on dev.
|
|
27830
|
+
...typeof sym.attrs["bodyHash"] === "string" ? { symbolContentHash: sym.attrs["bodyHash"] } : {},
|
|
27831
|
+
...opts.headSha ? { observedAtCommit: opts.headSha } : {}
|
|
27832
|
+
}
|
|
27833
|
+
});
|
|
27883
27834
|
}
|
|
27884
27835
|
}
|
|
27885
|
-
return
|
|
27836
|
+
return true;
|
|
27886
27837
|
}
|
|
27887
|
-
|
|
27888
|
-
|
|
27889
|
-
|
|
27890
|
-
|
|
27891
|
-
|
|
27892
|
-
|
|
27893
|
-
|
|
27894
|
-
|
|
27895
|
-
|
|
27896
|
-
|
|
27897
|
-
|
|
27898
|
-
|
|
27899
|
-
|
|
27900
|
-
|
|
27901
|
-
|
|
27902
|
-
|
|
27903
|
-
|
|
27904
|
-
|
|
27905
|
-
|
|
27906
|
-
|
|
27907
|
-
|
|
27908
|
-
|
|
27909
|
-
|
|
27910
|
-
|
|
27911
|
-
|
|
27912
|
-
|
|
27913
|
-
|
|
27914
|
-
|
|
27915
|
-
|
|
27838
|
+
var ENCLOSING_SYMBOL_KINDS = /* @__PURE__ */ new Set([
|
|
27839
|
+
"Function",
|
|
27840
|
+
"Class",
|
|
27841
|
+
"Method",
|
|
27842
|
+
"Module",
|
|
27843
|
+
"Interface",
|
|
27844
|
+
"Const",
|
|
27845
|
+
"Enum",
|
|
27846
|
+
"TypeAlias",
|
|
27847
|
+
"Namespace",
|
|
27848
|
+
"Property"
|
|
27849
|
+
]);
|
|
27850
|
+
var ENCLOSING_WALK_DEPTH = 8;
|
|
27851
|
+
function resolveEnclosingSymbol(store2, fileId, line, fileText) {
|
|
27852
|
+
if (!Number.isFinite(line) || line < 1) return null;
|
|
27853
|
+
const row = Math.floor(line) - 1;
|
|
27854
|
+
const lineStarts = [0];
|
|
27855
|
+
for (let i2 = 0; i2 < fileText.length; i2++) if (fileText.charCodeAt(i2) === 10) lineStarts.push(i2 + 1);
|
|
27856
|
+
const rowOf = (index) => {
|
|
27857
|
+
let lo = 0;
|
|
27858
|
+
let hi = lineStarts.length - 1;
|
|
27859
|
+
while (lo < hi) {
|
|
27860
|
+
const mid = lo + hi + 1 >> 1;
|
|
27861
|
+
if (lineStarts[mid] <= index) lo = mid;
|
|
27862
|
+
else hi = mid - 1;
|
|
27863
|
+
}
|
|
27864
|
+
return lo;
|
|
27865
|
+
};
|
|
27866
|
+
let best = null;
|
|
27867
|
+
let bestStart = -1;
|
|
27868
|
+
let bestSpan = Number.POSITIVE_INFINITY;
|
|
27869
|
+
const seen = /* @__PURE__ */ new Set([fileId]);
|
|
27870
|
+
let frontier = [fileId];
|
|
27871
|
+
for (let depth = 0; depth < ENCLOSING_WALK_DEPTH && frontier.length > 0; depth++) {
|
|
27872
|
+
const next = [];
|
|
27873
|
+
for (const id of frontier) {
|
|
27874
|
+
for (const e of store2.outEdges(id, ["DEFINES"])) {
|
|
27875
|
+
if (seen.has(e.to)) continue;
|
|
27876
|
+
seen.add(e.to);
|
|
27877
|
+
const n = store2.getNode(e.to);
|
|
27878
|
+
if (!n) continue;
|
|
27879
|
+
next.push(n.id);
|
|
27880
|
+
if (!ENCLOSING_SYMBOL_KINDS.has(n.label)) continue;
|
|
27881
|
+
const startRow = n.attrs["startRow"];
|
|
27882
|
+
const startByte = n.attrs["startByte"];
|
|
27883
|
+
const endByte = n.attrs["endByte"];
|
|
27884
|
+
if (typeof startRow !== "number" || typeof startByte !== "number" || typeof endByte !== "number") continue;
|
|
27885
|
+
if (startRow < 0 || endByte < startByte) continue;
|
|
27886
|
+
const endRow = rowOf(Math.max(startByte, Math.min(endByte, fileText.length) - 1));
|
|
27887
|
+
if (row < startRow || row > endRow) continue;
|
|
27888
|
+
const span = endByte - startByte;
|
|
27889
|
+
if (startRow > bestStart || startRow === bestStart && span < bestSpan) {
|
|
27890
|
+
best = n;
|
|
27891
|
+
bestStart = startRow;
|
|
27892
|
+
bestSpan = span;
|
|
27916
27893
|
}
|
|
27917
27894
|
}
|
|
27918
|
-
community.set(node, bestC);
|
|
27919
|
-
comTot.set(bestC, (comTot.get(bestC) ?? 0) + ki);
|
|
27920
|
-
if (bestC !== ci) improved = true;
|
|
27921
27895
|
}
|
|
27896
|
+
frontier = next;
|
|
27922
27897
|
}
|
|
27923
|
-
return
|
|
27898
|
+
return best;
|
|
27924
27899
|
}
|
|
27925
|
-
function
|
|
27926
|
-
const
|
|
27927
|
-
|
|
27928
|
-
|
|
27929
|
-
|
|
27930
|
-
|
|
27900
|
+
function recordAnchorHint(store2, problemId, relPath, provenance, ts) {
|
|
27901
|
+
const p = store2.getNode(problemId);
|
|
27902
|
+
if (!p || p.label !== "Problem") return false;
|
|
27903
|
+
if (p.attrs["anchorHint"]) return false;
|
|
27904
|
+
store2.updateNode(problemId, {
|
|
27905
|
+
attrs: { ...p.attrs, anchorHint: { path: relPath, provenance, ts } }
|
|
27906
|
+
});
|
|
27907
|
+
return true;
|
|
27908
|
+
}
|
|
27909
|
+
function openAnchorHint(p) {
|
|
27910
|
+
const h = p.attrs["anchorHint"];
|
|
27911
|
+
if (!h || typeof h.path !== "string" || h.upgradedAt) return null;
|
|
27912
|
+
return { path: h.path, ts: typeof h.ts === "number" ? h.ts : 0 };
|
|
27913
|
+
}
|
|
27914
|
+
function consumeAnchorHint(store2, p, ts) {
|
|
27915
|
+
const hint = p.attrs["anchorHint"];
|
|
27916
|
+
store2.updateNode(p.id, { attrs: { ...p.attrs, anchorHint: { ...hint, upgradedAt: ts } } });
|
|
27917
|
+
deriveContextFromAnchors(store2, p.id, ts);
|
|
27918
|
+
}
|
|
27919
|
+
function upgradeAnchorHints(store2, workspaceId, ts) {
|
|
27920
|
+
let upgraded = 0;
|
|
27921
|
+
let standing = 0;
|
|
27922
|
+
let unresolvable = 0;
|
|
27923
|
+
let blocked = 0;
|
|
27924
|
+
const filesByPath = /* @__PURE__ */ new Map();
|
|
27925
|
+
for (const f of store2.findNodesByLabel("File")) {
|
|
27926
|
+
if (f.attrs["workspaceId"] && f.attrs["workspaceId"] !== workspaceId) continue;
|
|
27927
|
+
const rel = typeof f.attrs["relPath"] === "string" ? f.attrs["relPath"] : f.description;
|
|
27928
|
+
if (rel && !filesByPath.has(rel)) filesByPath.set(rel, f);
|
|
27931
27929
|
}
|
|
27932
|
-
const
|
|
27933
|
-
|
|
27934
|
-
const
|
|
27935
|
-
|
|
27936
|
-
|
|
27937
|
-
|
|
27938
|
-
|
|
27939
|
-
|
|
27940
|
-
|
|
27941
|
-
|
|
27942
|
-
|
|
27943
|
-
|
|
27944
|
-
|
|
27945
|
-
|
|
27946
|
-
|
|
27947
|
-
|
|
27948
|
-
|
|
27949
|
-
|
|
27950
|
-
}
|
|
27951
|
-
const anchor = [...comp].sort()[0];
|
|
27952
|
-
for (const id of comp) refined.set(id, anchor);
|
|
27930
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
27931
|
+
if (p.validTo != null || p.attrs["resolvedAt"]) continue;
|
|
27932
|
+
const hint = openAnchorHint(p);
|
|
27933
|
+
if (!hint) continue;
|
|
27934
|
+
const file2 = filesByPath.get(hint.path.trim().replace(/^\.?\//, ""));
|
|
27935
|
+
if (!file2 || !isAnchorableCodePath(hint.path)) {
|
|
27936
|
+
unresolvable++;
|
|
27937
|
+
continue;
|
|
27938
|
+
}
|
|
27939
|
+
standing++;
|
|
27940
|
+
const changedAt = Number(file2.attrs["contentChangedAt"] ?? 0);
|
|
27941
|
+
if (changedAt <= hint.ts) continue;
|
|
27942
|
+
if (anchorProblemToWorkingFile(store2, p.id, hint.path, workspaceId, ts, "hint-confirmed")) {
|
|
27943
|
+
consumeAnchorHint(store2, p, ts);
|
|
27944
|
+
upgraded++;
|
|
27945
|
+
standing--;
|
|
27946
|
+
} else {
|
|
27947
|
+
blocked++;
|
|
27953
27948
|
}
|
|
27954
27949
|
}
|
|
27955
|
-
return
|
|
27950
|
+
return { upgraded, standing, unresolvable, blocked };
|
|
27956
27951
|
}
|
|
27957
|
-
function
|
|
27958
|
-
const
|
|
27959
|
-
for (const
|
|
27960
|
-
|
|
27961
|
-
|
|
27962
|
-
|
|
27963
|
-
|
|
27964
|
-
|
|
27965
|
-
|
|
27966
|
-
|
|
27967
|
-
const
|
|
27968
|
-
|
|
27952
|
+
function reopenAutoClosedProblems(store2, t) {
|
|
27953
|
+
const report = { reopened: 0, alreadySuspect: 0, agentDescribed: 0 };
|
|
27954
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
27955
|
+
if (p.attrs["resolvedAt"] == null) continue;
|
|
27956
|
+
if (p.attrs["resolvedAs"] != null) continue;
|
|
27957
|
+
if (p.attrs["mergedInto"] !== void 0) continue;
|
|
27958
|
+
if (p.attrs["resolutionWitnessed"] === true) {
|
|
27959
|
+
report.agentDescribed++;
|
|
27960
|
+
continue;
|
|
27961
|
+
}
|
|
27962
|
+
const sols = store2.outEdges(p.id, ["SOLVED_BY"]).map((e) => store2.getNode(e.to)).filter((n) => n !== null);
|
|
27963
|
+
if (sols.length === 0 && p.attrs["retiredEditPlaceholder"] !== true) continue;
|
|
27964
|
+
if (!sols.every((s) => String(s.description ?? "").startsWith(AUTO_MINT_PREFIX))) {
|
|
27965
|
+
report.agentDescribed++;
|
|
27966
|
+
continue;
|
|
27967
|
+
}
|
|
27968
|
+
if (p.attrs["resolutionSuspect"] === true) report.alreadySuspect++;
|
|
27969
|
+
const attrs = { ...p.attrs };
|
|
27970
|
+
delete attrs["resolvedAt"];
|
|
27971
|
+
attrs["reopenedFrom"] = "auto-close";
|
|
27972
|
+
attrs["reopenedAt"] = t;
|
|
27973
|
+
store2.updateNode(p.id, { attrs, lastUpdatedAt: t });
|
|
27974
|
+
report.reopened++;
|
|
27969
27975
|
}
|
|
27970
|
-
return
|
|
27976
|
+
return report;
|
|
27971
27977
|
}
|
|
27972
|
-
function
|
|
27973
|
-
|
|
27974
|
-
const
|
|
27975
|
-
|
|
27976
|
-
|
|
27977
|
-
|
|
27978
|
-
|
|
27979
|
-
|
|
27980
|
-
|
|
27981
|
-
|
|
27978
|
+
function markFixCandidates(store2, t) {
|
|
27979
|
+
let resolved = 0;
|
|
27980
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
27981
|
+
if (!p.id.startsWith("dprob_")) continue;
|
|
27982
|
+
if (p.attrs["resolvedAt"]) continue;
|
|
27983
|
+
if (isConstraintProblem(p)) continue;
|
|
27984
|
+
let symName = "";
|
|
27985
|
+
let symRelPath;
|
|
27986
|
+
let edited = false;
|
|
27987
|
+
const since = Math.max(p.createdAt, Number(p.attrs["fixCandidateClearedAt"] ?? 0));
|
|
27988
|
+
for (const e of store2.outEdges(p.id, ["ANCHORED_AT"])) {
|
|
27989
|
+
if (e.attrs?.["mention"] === true) continue;
|
|
27990
|
+
if (e.attrs?.["anchorProvenance"] === "legacy") continue;
|
|
27991
|
+
const sym = store2.getNode(e.to);
|
|
27992
|
+
const changedAt = sym?.label === "File" ? Number(sym.attrs["contentChangedAt"] ?? 0) : sym?.lastUpdatedAt ?? 0;
|
|
27993
|
+
if (sym && changedAt > since) {
|
|
27994
|
+
edited = true;
|
|
27995
|
+
symName = sym.description;
|
|
27996
|
+
symRelPath = sym.attrs["relPath"] ?? void 0;
|
|
27997
|
+
break;
|
|
27998
|
+
}
|
|
27982
27999
|
}
|
|
27983
|
-
|
|
27984
|
-
|
|
27985
|
-
|
|
27986
|
-
|
|
27987
|
-
|
|
27988
|
-
|
|
28000
|
+
if (!edited) continue;
|
|
28001
|
+
if (p.attrs["fixCandidateAt"] !== void 0) continue;
|
|
28002
|
+
if (store2.outEdges(p.id, ["SOLVED_BY"]).some((e) => {
|
|
28003
|
+
const s = store2.getNode(e.to);
|
|
28004
|
+
return s !== null && !String(s.description ?? "").startsWith(AUTO_MINT_PREFIX);
|
|
28005
|
+
}))
|
|
28006
|
+
continue;
|
|
28007
|
+
store2.updateNode(p.id, {
|
|
28008
|
+
attrs: {
|
|
28009
|
+
...p.attrs,
|
|
28010
|
+
// The cue, kept as structured data so the render can name the file it is
|
|
28011
|
+
// asking about. Deliberately NOT `resolvedAt` — this is a question.
|
|
28012
|
+
fixCandidateAt: t,
|
|
28013
|
+
fixCandidateSymbol: symName,
|
|
28014
|
+
...symRelPath ? { fixCandidateFile: symRelPath } : {},
|
|
28015
|
+
// Snapshot the render-ledger counter so "shown since THIS ask" is
|
|
28016
|
+
// measurable. `shownCount` is the node's LIFETIME count across every
|
|
28017
|
+
// band, so comparing it raw would retire a fresh ask on a
|
|
28018
|
+
// frequently-surfaced problem before anyone had seen the question once.
|
|
28019
|
+
fixCandidateShownBase: Number(p.attrs["shownCount"] ?? 0)
|
|
28020
|
+
}
|
|
28021
|
+
// No `lastUpdatedAt`: raising a question is bookkeeping, not a new
|
|
28022
|
+
// observation of the problem, and the passive block's recent-problems band
|
|
28023
|
+
// sorts on that field (see clearSettledFixCandidates).
|
|
28024
|
+
});
|
|
28025
|
+
resolved++;
|
|
27989
28026
|
}
|
|
27990
|
-
return
|
|
28027
|
+
return resolved;
|
|
27991
28028
|
}
|
|
27992
|
-
|
|
27993
|
-
|
|
27994
|
-
const
|
|
27995
|
-
|
|
27996
|
-
|
|
27997
|
-
|
|
27998
|
-
const
|
|
27999
|
-
|
|
28000
|
-
|
|
28001
|
-
|
|
28002
|
-
|
|
28003
|
-
|
|
28004
|
-
|
|
28005
|
-
|
|
28006
|
-
const
|
|
28007
|
-
|
|
28008
|
-
|
|
28009
|
-
|
|
28010
|
-
|
|
28011
|
-
const row = acc.get(ra) ?? /* @__PURE__ */ new Map();
|
|
28012
|
-
row.set(rb, (row.get(rb) ?? 0) + weight);
|
|
28013
|
-
acc.set(ra, row);
|
|
28029
|
+
var FIX_CANDIDATE_ASK_LIMIT = 5;
|
|
28030
|
+
function clearSettledFixCandidates(store2, t) {
|
|
28031
|
+
const report = { answered: 0, ignored: 0, unsubstantiated: 0, standing: 0 };
|
|
28032
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
28033
|
+
if (p.attrs["fixCandidateAt"] === void 0) continue;
|
|
28034
|
+
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");
|
|
28035
|
+
const unsubstantiated = fileAnchors.length > 0 && fileAnchors.every((f) => f.attrs["contentChangedAt"] === void 0);
|
|
28036
|
+
const answered = p.attrs["resolvedAt"] != null || store2.outEdges(p.id, ["SOLVED_BY"]).some((e) => {
|
|
28037
|
+
const s = store2.getNode(e.to);
|
|
28038
|
+
return s !== null && !String(s.description ?? "").startsWith(AUTO_MINT_PREFIX);
|
|
28039
|
+
});
|
|
28040
|
+
const base = Number(
|
|
28041
|
+
p.attrs["fixCandidateShownBase"] ?? p.attrs["shownCount"] ?? 0
|
|
28042
|
+
);
|
|
28043
|
+
const shownSinceAsk = Number(p.attrs["shownCount"] ?? 0) - base;
|
|
28044
|
+
const ignored = shownSinceAsk >= FIX_CANDIDATE_ASK_LIMIT;
|
|
28045
|
+
if (!answered && !ignored && !unsubstantiated) {
|
|
28046
|
+
report.standing++;
|
|
28047
|
+
continue;
|
|
28014
28048
|
}
|
|
28049
|
+
const attrs = { ...p.attrs };
|
|
28050
|
+
delete attrs["fixCandidateAt"];
|
|
28051
|
+
delete attrs["fixCandidateSymbol"];
|
|
28052
|
+
delete attrs["fixCandidateFile"];
|
|
28053
|
+
attrs["fixCandidateClearedAt"] = t;
|
|
28054
|
+
store2.updateNode(p.id, { attrs });
|
|
28055
|
+
if (answered) report.answered++;
|
|
28056
|
+
else if (ignored) report.ignored++;
|
|
28057
|
+
else report.unsubstantiated++;
|
|
28015
28058
|
}
|
|
28016
|
-
|
|
28017
|
-
const sg = buildGraph(superIds, superAdj);
|
|
28018
|
-
const superComm = refineConnectivity(localMoving(superIds, sg), sg);
|
|
28019
|
-
const assignment = /* @__PURE__ */ new Map();
|
|
28020
|
-
for (const id of input.nodeIds) assignment.set(id, superComm.get(rep(id)) ?? rep(id));
|
|
28021
|
-
return normalize(assignment);
|
|
28059
|
+
return report;
|
|
28022
28060
|
}
|
|
28023
|
-
|
|
28024
|
-
|
|
28025
|
-
|
|
28026
|
-
|
|
28027
|
-
|
|
28028
|
-
|
|
28029
|
-
|
|
28030
|
-
|
|
28031
|
-
|
|
28032
|
-
rule: "A1"
|
|
28033
|
-
};
|
|
28034
|
-
}
|
|
28035
|
-
return { shouldMarkStable: false, confidence: 0, rule: "A1" };
|
|
28061
|
+
function priorExposure(store2, nodeId, atSeq) {
|
|
28062
|
+
const n = store2.getNode(nodeId);
|
|
28063
|
+
if (!n || !n.attrs) return "unshown";
|
|
28064
|
+
const shownSeq = n.attrs["lastShownAtSeq"];
|
|
28065
|
+
if (shownSeq !== void 0 && shownSeq <= atSeq) return "shown";
|
|
28066
|
+
const evictedSeq = n.attrs["lastEvictedAtSeq"];
|
|
28067
|
+
if (evictedSeq !== void 0 && evictedSeq <= atSeq) return "evicted";
|
|
28068
|
+
if ((n.attrs["evictedCount"] ?? 0) > 0) return "evicted";
|
|
28069
|
+
return "unshown";
|
|
28036
28070
|
}
|
|
28037
28071
|
|
|
28038
|
-
// ../../packages/
|
|
28039
|
-
|
|
28040
|
-
var MIN_QUALITY = 0.65;
|
|
28041
|
-
var MAX_FAILURE_RATE = 0.15;
|
|
28042
|
-
var QUALITY_BOOST = 1.2;
|
|
28043
|
-
function a2MotifQuality(input) {
|
|
28044
|
-
if (input.instances.length === 0) {
|
|
28045
|
-
return {
|
|
28046
|
-
qualityScore: 0,
|
|
28047
|
-
qualityFailureRate: 0,
|
|
28048
|
-
qualified: false,
|
|
28049
|
-
qualityBoost: 1,
|
|
28050
|
-
rule: "A2"
|
|
28051
|
-
};
|
|
28052
|
-
}
|
|
28053
|
-
const mean = input.instances.reduce((s, i2) => s + i2.confidence, 0) / input.instances.length;
|
|
28054
|
-
const failures = input.instances.filter((i2) => i2.failed).length;
|
|
28055
|
-
const failureRate = failures / input.instances.length;
|
|
28056
|
-
const qualified = input.instances.length >= MIN_INSTANCES2 && mean >= MIN_QUALITY && failureRate <= MAX_FAILURE_RATE;
|
|
28057
|
-
return {
|
|
28058
|
-
qualityScore: mean,
|
|
28059
|
-
qualityFailureRate: failureRate,
|
|
28060
|
-
qualified,
|
|
28061
|
-
qualityBoost: qualified ? QUALITY_BOOST : 1,
|
|
28062
|
-
rule: "A2"
|
|
28063
|
-
};
|
|
28064
|
-
}
|
|
28072
|
+
// ../../packages/local-graph/src/intent.ts
|
|
28073
|
+
init_src();
|
|
28065
28074
|
|
|
28066
|
-
// ../../packages/
|
|
28067
|
-
|
|
28068
|
-
|
|
28069
|
-
function
|
|
28070
|
-
|
|
28071
|
-
|
|
28072
|
-
|
|
28073
|
-
|
|
28074
|
-
|
|
28075
|
-
|
|
28076
|
-
|
|
28077
|
-
|
|
28078
|
-
|
|
28075
|
+
// ../../packages/local-graph/src/anchor-backfill.ts
|
|
28076
|
+
init_src();
|
|
28077
|
+
init_src2();
|
|
28078
|
+
function isLegacyAnchor(attrs) {
|
|
28079
|
+
return attrs?.["captureTime"] === true && attrs["anchorProvenance"] === void 0;
|
|
28080
|
+
}
|
|
28081
|
+
var STATEMENT_PATH_RE = new RegExp(
|
|
28082
|
+
String.raw`(?:[\w.+-]+\/)+[\w.+-]+\.(?:${anchorableExtensionAlternation()})`,
|
|
28083
|
+
"g"
|
|
28084
|
+
);
|
|
28085
|
+
function filePathResolver(store2) {
|
|
28086
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
28087
|
+
for (const f of store2.findNodesByLabel("File")) {
|
|
28088
|
+
const path = String(f.attrs["relPath"] ?? f.description ?? "");
|
|
28089
|
+
if (path) byPath.set(path, { id: f.id, path });
|
|
28090
|
+
}
|
|
28091
|
+
return (named) => {
|
|
28092
|
+
const exact = byPath.get(named);
|
|
28093
|
+
if (exact) return exact;
|
|
28094
|
+
const hits = [...byPath.values()].filter(
|
|
28095
|
+
(f) => f.path.endsWith(`/${named}`) || named.endsWith(`/${f.path}`)
|
|
28096
|
+
);
|
|
28097
|
+
return hits.length === 1 ? hits[0] : null;
|
|
28098
|
+
};
|
|
28099
|
+
}
|
|
28100
|
+
function statementTarget(statement, resolvePath) {
|
|
28101
|
+
for (const m of statement.matchAll(STATEMENT_PATH_RE)) {
|
|
28102
|
+
const f = resolvePath(m[0]);
|
|
28103
|
+
if (f) return f;
|
|
28079
28104
|
}
|
|
28080
|
-
|
|
28081
|
-
|
|
28082
|
-
|
|
28083
|
-
const stable = a1.shouldMarkStable;
|
|
28084
|
-
const promoted = survivedRuns >= MOTIF_MIN_SURVIVED && stable && a2.qualified;
|
|
28105
|
+
return null;
|
|
28106
|
+
}
|
|
28107
|
+
function restatedAnchor(problemId, fileId, restatedFrom, ts) {
|
|
28085
28108
|
return {
|
|
28086
|
-
|
|
28087
|
-
|
|
28088
|
-
|
|
28089
|
-
|
|
28090
|
-
|
|
28091
|
-
|
|
28109
|
+
id: `edge_${digest({ from: problemId, type: "ANCHORED_AT", to: fileId })}`.slice(0, 24),
|
|
28110
|
+
from: problemId,
|
|
28111
|
+
to: fileId,
|
|
28112
|
+
type: "ANCHORED_AT",
|
|
28113
|
+
// Same confidence a capture-time anchor enters at: the source is the
|
|
28114
|
+
// agent's own statement either way, only the moment of reading differs.
|
|
28115
|
+
confidence: 0.4,
|
|
28116
|
+
extractionSource: "agent-observed",
|
|
28117
|
+
createdAt: ts,
|
|
28118
|
+
lastSeenAt: ts,
|
|
28119
|
+
navSuccesses: 0,
|
|
28120
|
+
navFailures: 0,
|
|
28121
|
+
attrs: { anchorProvenance: "restated", restatedFrom, restatedAt: ts }
|
|
28092
28122
|
};
|
|
28093
28123
|
}
|
|
28094
|
-
|
|
28095
|
-
|
|
28096
|
-
|
|
28097
|
-
|
|
28098
|
-
|
|
28099
|
-
|
|
28100
|
-
|
|
28101
|
-
|
|
28102
|
-
|
|
28103
|
-
|
|
28104
|
-
|
|
28105
|
-
|
|
28106
|
-
|
|
28107
|
-
|
|
28108
|
-
|
|
28109
|
-
|
|
28110
|
-
|
|
28111
|
-
|
|
28112
|
-
|
|
28113
|
-
|
|
28114
|
-
|
|
28115
|
-
|
|
28116
|
-
|
|
28117
|
-
|
|
28118
|
-
|
|
28119
|
-
|
|
28120
|
-
|
|
28121
|
-
|
|
28122
|
-
|
|
28123
|
-
|
|
28124
|
-
|
|
28125
|
-
|
|
28126
|
-
|
|
28127
|
-
|
|
28128
|
-
|
|
28129
|
-
|
|
28130
|
-
|
|
28131
|
-
|
|
28132
|
-
|
|
28133
|
-
|
|
28134
|
-
|
|
28135
|
-
|
|
28136
|
-
|
|
28137
|
-
|
|
28138
|
-
|
|
28139
|
-
|
|
28140
|
-
|
|
28141
|
-
|
|
28124
|
+
function repairLegacyAnchorsFromStatement(store2, ts) {
|
|
28125
|
+
const report = {
|
|
28126
|
+
repaired: [],
|
|
28127
|
+
retiredGuesses: 0,
|
|
28128
|
+
noPathNamed: 0,
|
|
28129
|
+
pathUnknown: 0,
|
|
28130
|
+
alreadyCorrect: 0
|
|
28131
|
+
};
|
|
28132
|
+
const resolvePath = filePathResolver(store2);
|
|
28133
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
28134
|
+
try {
|
|
28135
|
+
const anchors = store2.outEdges(p.id, ["ANCHORED_AT"]);
|
|
28136
|
+
const legacy = anchors.filter((e) => e.attrs?.["anchorProvenance"] === "legacy");
|
|
28137
|
+
if (legacy.length === 0) continue;
|
|
28138
|
+
const better = anchors.filter((e) => {
|
|
28139
|
+
const prov = e.attrs?.["anchorProvenance"];
|
|
28140
|
+
return prov === "restated" || prov === "witnessed" || prov === "edited";
|
|
28141
|
+
});
|
|
28142
|
+
if (better.length > 0) {
|
|
28143
|
+
const betterTargets = new Set(better.map((e) => e.to));
|
|
28144
|
+
let retired = 0;
|
|
28145
|
+
for (const e of legacy) {
|
|
28146
|
+
if (!betterTargets.has(e.to)) {
|
|
28147
|
+
store2.closeEdge(e.id, ts);
|
|
28148
|
+
retired++;
|
|
28149
|
+
}
|
|
28150
|
+
}
|
|
28151
|
+
report.retiredGuesses += retired;
|
|
28152
|
+
continue;
|
|
28153
|
+
}
|
|
28154
|
+
const named = [...String(p.description ?? "").matchAll(STATEMENT_PATH_RE)].map((m) => m[0]);
|
|
28155
|
+
if (named.length === 0) {
|
|
28156
|
+
report.noPathNamed++;
|
|
28157
|
+
continue;
|
|
28158
|
+
}
|
|
28159
|
+
const guessedPaths = legacy.map((e) => {
|
|
28160
|
+
const f = store2.getNode(e.to);
|
|
28161
|
+
return String(f?.attrs["relPath"] ?? f?.description ?? "");
|
|
28162
|
+
});
|
|
28163
|
+
if (named.some((x) => guessedPaths.some((g) => g.endsWith(x) || x.endsWith(g)))) {
|
|
28164
|
+
report.alreadyCorrect++;
|
|
28165
|
+
continue;
|
|
28166
|
+
}
|
|
28167
|
+
const target = named.map(resolvePath).find((f) => f !== null);
|
|
28168
|
+
if (!target) {
|
|
28169
|
+
report.pathUnknown++;
|
|
28170
|
+
continue;
|
|
28171
|
+
}
|
|
28172
|
+
store2.mergeEdge(restatedAnchor(p.id, target.id, guessedPaths[0] ?? "", ts));
|
|
28173
|
+
for (const e of legacy) {
|
|
28174
|
+
if (e.to !== target.id) store2.closeEdge(e.id, ts);
|
|
28175
|
+
}
|
|
28176
|
+
report.retiredGuesses += legacy.filter((e) => e.to !== target.id).length;
|
|
28177
|
+
report.repaired.push({
|
|
28178
|
+
problemId: p.id,
|
|
28179
|
+
problem: p.description,
|
|
28180
|
+
guessed: guessedPaths[0] ?? "",
|
|
28181
|
+
restated: target.path
|
|
28182
|
+
});
|
|
28183
|
+
} catch {
|
|
28142
28184
|
}
|
|
28143
28185
|
}
|
|
28144
|
-
|
|
28145
|
-
|
|
28146
|
-
|
|
28147
|
-
|
|
28148
|
-
|
|
28149
|
-
|
|
28150
|
-
|
|
28151
|
-
|
|
28152
|
-
|
|
28153
|
-
|
|
28154
|
-
|
|
28155
|
-
|
|
28156
|
-
|
|
28157
|
-
|
|
28158
|
-
|
|
28159
|
-
|
|
28160
|
-
|
|
28161
|
-
|
|
28162
|
-
|
|
28163
|
-
|
|
28164
|
-
|
|
28165
|
-
|
|
28166
|
-
|
|
28167
|
-
|
|
28168
|
-
|
|
28186
|
+
return report;
|
|
28187
|
+
}
|
|
28188
|
+
function backfillLegacyAnchors(store2, ts) {
|
|
28189
|
+
const report = {
|
|
28190
|
+
demotedEdges: 0,
|
|
28191
|
+
demotedProblems: 0,
|
|
28192
|
+
restatedProblems: 0,
|
|
28193
|
+
labeledEdges: 0,
|
|
28194
|
+
suspects: []
|
|
28195
|
+
};
|
|
28196
|
+
let resolvePath;
|
|
28197
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
28198
|
+
const resolved = p.attrs["resolvedAt"] !== void 0;
|
|
28199
|
+
const legacy = store2.outEdges(p.id, ["ANCHORED_AT"]).filter((e) => isLegacyAnchor(e.attrs) || !resolved && e.attrs?.["anchorProvenance"] === "legacy");
|
|
28200
|
+
if (legacy.length === 0) continue;
|
|
28201
|
+
try {
|
|
28202
|
+
if (!resolved) {
|
|
28203
|
+
resolvePath ??= filePathResolver(store2);
|
|
28204
|
+
const target = statementTarget(String(p.description ?? ""), resolvePath);
|
|
28205
|
+
if (target) {
|
|
28206
|
+
const guessed = store2.getNode(legacy[0].to);
|
|
28207
|
+
store2.mergeEdge(
|
|
28208
|
+
restatedAnchor(p.id, target.id, String(guessed?.attrs["relPath"] ?? guessed?.description ?? ""), ts)
|
|
28209
|
+
);
|
|
28210
|
+
report.restatedProblems++;
|
|
28211
|
+
}
|
|
28212
|
+
const retired = legacy.filter((e) => !(target && e.to === target.id));
|
|
28213
|
+
for (const e of retired) {
|
|
28214
|
+
const file2 = store2.getNode(e.to);
|
|
28215
|
+
const path = String(file2?.attrs["relPath"] ?? file2?.description ?? "");
|
|
28216
|
+
if (path) recordAnchorHint(store2, p.id, path, "legacy", ts);
|
|
28217
|
+
store2.closeEdge(e.id, ts);
|
|
28218
|
+
report.demotedEdges++;
|
|
28219
|
+
}
|
|
28220
|
+
if (retired.length > 0) report.demotedProblems++;
|
|
28221
|
+
continue;
|
|
28222
|
+
}
|
|
28223
|
+
for (const e of legacy) {
|
|
28224
|
+
store2.mergeEdge({ ...e, attrs: { ...e.attrs, anchorProvenance: "legacy" }, lastSeenAt: ts });
|
|
28225
|
+
report.labeledEdges++;
|
|
28226
|
+
}
|
|
28227
|
+
for (const se of store2.outEdges(p.id, ["SOLVED_BY"])) {
|
|
28228
|
+
const sol = store2.getNode(se.to);
|
|
28229
|
+
if (!sol || !sol.description.startsWith(AUTO_MINT_PREFIX)) continue;
|
|
28230
|
+
const guessedAnchor = sol.description.slice(AUTO_MINT_PREFIX.length);
|
|
28231
|
+
store2.updateNode(sol.id, {
|
|
28232
|
+
attrs: { ...sol.attrs, provisional: true, resolutionSuspect: true },
|
|
28233
|
+
lastUpdatedAt: ts
|
|
28234
|
+
});
|
|
28235
|
+
markRevisit(
|
|
28236
|
+
store2,
|
|
28237
|
+
sol,
|
|
28238
|
+
`auto-closed off a pre-provenance anchor (guessed ${guessedAnchor}) \u2014 confirm the problem is really fixed, or reopen it`,
|
|
28239
|
+
ts,
|
|
28240
|
+
// "or reopen it" is half the ask, and the nightly reopen takes that
|
|
28241
|
+
// branch — so record what would answer this, or the flag can never be
|
|
28242
|
+
// lowered and the band fills with settled questions.
|
|
28243
|
+
"parentProblemOpen"
|
|
28244
|
+
);
|
|
28245
|
+
store2.updateNode(p.id, {
|
|
28246
|
+
attrs: { ...p.attrs, resolutionSuspect: true },
|
|
28247
|
+
lastUpdatedAt: ts
|
|
28248
|
+
});
|
|
28249
|
+
report.suspects.push({
|
|
28250
|
+
problemId: p.id,
|
|
28251
|
+
problem: p.description,
|
|
28252
|
+
solutionId: sol.id,
|
|
28253
|
+
guessedAnchor
|
|
28254
|
+
});
|
|
28255
|
+
}
|
|
28256
|
+
} catch {
|
|
28169
28257
|
}
|
|
28170
28258
|
}
|
|
28171
|
-
|
|
28172
|
-
for (let i2 = 0; i2 < n; i2++) scores.set(ids[i2], score2[i2]);
|
|
28173
|
-
return { scores, iterations: iter, converged };
|
|
28259
|
+
return report;
|
|
28174
28260
|
}
|
|
28175
28261
|
|
|
28262
|
+
// ../../packages/local-graph/src/percolate.ts
|
|
28263
|
+
init_src2();
|
|
28264
|
+
init_src();
|
|
28265
|
+
|
|
28266
|
+
// ../../packages/local-graph/src/abstraction.ts
|
|
28267
|
+
init_src();
|
|
28268
|
+
init_src2();
|
|
28269
|
+
|
|
28176
28270
|
// ../../packages/local-graph/src/triage.ts
|
|
28177
28271
|
init_src();
|
|
28178
28272
|
init_src();
|
|
@@ -28813,6 +28907,7 @@ function tryIncrementalRescore(store2, lastRescoreAt, started) {
|
|
|
28813
28907
|
store2.setPageRank(id, score2);
|
|
28814
28908
|
pageRankWritten++;
|
|
28815
28909
|
}
|
|
28910
|
+
if (pageRankWritten > 0) store2.refreshPageRankScale?.();
|
|
28816
28911
|
});
|
|
28817
28912
|
const sweep = store2.landmarkSweepRows();
|
|
28818
28913
|
const candidates = /* @__PURE__ */ new Map();
|
|
@@ -28883,6 +28978,7 @@ function runFullRescore(store2) {
|
|
|
28883
28978
|
store2.setPageRank(id, score2);
|
|
28884
28979
|
pageRankWritten++;
|
|
28885
28980
|
}
|
|
28981
|
+
store2.refreshPageRankScale?.();
|
|
28886
28982
|
});
|
|
28887
28983
|
const landmarkCandidates = /* @__PURE__ */ new Map();
|
|
28888
28984
|
for (const [id, score2] of result.scores) {
|