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