@inerrata-corporation/errata 2.0.21 → 2.0.22-dev.4049

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.
@@ -594,6 +594,13 @@ function aliasRegex(alias) {
594
594
  const body = escapeRe(alias).replace(/\\?\s+/g, "\\s+");
595
595
  return new RegExp(`(?<![\\w-])${body}(?![\\w-])`, "i");
596
596
  }
597
+ function withoutEntityMentions(text, entityId) {
598
+ let out = text;
599
+ for (const { entity, re } of COMPILED) {
600
+ if (entity.id === entityId) out = out.replace(new RegExp(re.source, "gi"), " ");
601
+ }
602
+ return out;
603
+ }
597
604
  function prefilterEntities(text) {
598
605
  const seen = /* @__PURE__ */ new Set();
599
606
  const hits = [];
@@ -770,13 +777,19 @@ var init_concept_bag = __esm({
770
777
 
771
778
  // ../../packages/shared/src/nlp/claim-id.ts
772
779
  function claimStructure(text, scope) {
773
- const entities = prefilterEntities(text).map((e) => e.id).sort();
780
+ const extracted = prefilterEntities(text);
781
+ const entities = extracted.map((e) => e.id).sort();
774
782
  const predicate = normalizePredicate(text);
775
783
  const stance = detectStance(text);
776
784
  const base = { entities, predicate, stance, scope: scope ?? {} };
777
- if (predicate === "") base.raw = conceptBag(text);
785
+ if (predicate === "" || entities.length === 0 || predicateIsTheEntity(text, extracted[0].id, entities.length)) {
786
+ base.raw = conceptBag(text);
787
+ }
778
788
  return base;
779
789
  }
790
+ function predicateIsTheEntity(text, entityId, entityCount) {
791
+ return entityCount === 1 && normalizePredicate(withoutEntityMentions(text, entityId)) === "";
792
+ }
780
793
  function canonicalClaimId(text, scope) {
781
794
  const s = claimStructure(text, scope);
782
795
  return digest(
@@ -15242,6 +15255,36 @@ var init_history_evidence = __esm({
15242
15255
  });
15243
15256
 
15244
15257
  // ../../packages/shared/src/history-dependencies.ts
15258
+ function historyDependencyIdentity(raw) {
15259
+ const compact = compactDependencyIdentity(raw);
15260
+ if (compact !== void 0) return compact;
15261
+ const parsed = HistoryDependencyContextSchema.safeParse(raw);
15262
+ if (!parsed.success || !parsed.data.complete || parsed.data.conflict) return null;
15263
+ if (parsed.data.dependencies.some((d) => !exactHistoryDependencyVersion(d))) return null;
15264
+ return digest(parsed.data.dependencies.map((d) => [d.packageId, d.manifestId, d.side, d.declaredRange, d.resolvedVersion]).sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b))));
15265
+ }
15266
+ function exactHistoryDependencyVersion(d) {
15267
+ return d.resolvedVersion ?? (/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(d.declaredRange ?? "") ? d.declaredRange : null);
15268
+ }
15269
+ function compactDependencyIdentity(raw) {
15270
+ if (!raw || typeof raw !== "object" || raw.compact !== true) return void 0;
15271
+ const identity = raw.identity;
15272
+ return typeof identity === "string" ? identity : null;
15273
+ }
15274
+ function compactHistoryQuality(quality) {
15275
+ const out = { ...quality };
15276
+ const context = quality["dependencyContext"];
15277
+ if (context !== void 0 && compactDependencyIdentity(context) === void 0) {
15278
+ const compact = { schemaVersion: "history-deps-1", compact: true, identity: historyDependencyIdentity(context) };
15279
+ out["dependencyContext"] = compact;
15280
+ }
15281
+ const evidence = quality["reconciliationEvidence"];
15282
+ if (evidence && typeof evidence === "object" && !Array.isArray(evidence)) {
15283
+ const build = evidence["build"];
15284
+ out["reconciliationEvidence"] = build === void 0 ? {} : { build };
15285
+ }
15286
+ return out;
15287
+ }
15245
15288
  var version2, HistoryDependencySchema, HistoryDependencyContextSchema;
15246
15289
  var init_history_dependencies = __esm({
15247
15290
  "../../packages/shared/src/history-dependencies.ts"() {
@@ -15726,10 +15769,12 @@ var init_history_quality = __esm({
15726
15769
  });
15727
15770
 
15728
15771
  // ../../packages/shared/src/history-read.ts
15772
+ var CLOUD_ATTR_DUPLICATE_KEYS;
15729
15773
  var init_history_read = __esm({
15730
15774
  "../../packages/shared/src/history-read.ts"() {
15731
15775
  "use strict";
15732
15776
  init_history_dependencies();
15777
+ CLOUD_ATTR_DUPLICATE_KEYS = ["attrsJson", "embedding"];
15733
15778
  }
15734
15779
  });
15735
15780
 
@@ -16396,7 +16441,307 @@ function installEditPlaceholderRetirement(db) {
16396
16441
  });
16397
16442
  }
16398
16443
 
16444
+ // ../../packages/local-graph/src/cloud-attr-compaction.ts
16445
+ init_src2();
16446
+ var CLOUD_ATTR_COMPACTION_KEY = "cloud-attr-compaction-1";
16447
+ var DUPES = CLOUD_ATTR_DUPLICATE_KEYS.map((k) => `'${k}'`).join(",");
16448
+ function compacted(row) {
16449
+ const packed = `json_extract(${row}.attrs_json, '$.attrsJson')`;
16450
+ const nested = `CASE WHEN json_type(${row}.attrs_json, '$.attrsJson') = 'text' AND json_valid(${packed})
16451
+ AND json_type(${packed}) = 'object' THEN ${packed} ELSE '{}' END`;
16452
+ const value = `CASE WHEN type IN ('array','object') THEN json(value) WHEN type IN ('true','false') THEN json(type) ELSE value END`;
16453
+ return `(SELECT json_group_object(key, ${value}) FROM (
16454
+ SELECT key, value, type, min(pri) FROM (
16455
+ SELECT key, value, type, 0 AS pri FROM json_each(${row}.attrs_json)
16456
+ UNION ALL
16457
+ SELECT key, value, type, 1 AS pri FROM json_each(${nested})
16458
+ ) WHERE key NOT IN (${DUPES}) GROUP BY key))`;
16459
+ }
16460
+ function dirty(row) {
16461
+ const has = CLOUD_ATTR_DUPLICATE_KEYS.map((k) => `json_type(${row}.attrs_json, '$.${k}') IS NOT NULL`).join(" OR ");
16462
+ return `json_extract(${row}.attrs_json, '$.source') = 'cloud' AND (${has})`;
16463
+ }
16464
+ function installCloudAttrCompaction(db) {
16465
+ if (db.prepare("SELECT value FROM store_meta WHERE key = ?").get(CLOUD_ATTR_COMPACTION_KEY)) return;
16466
+ db.transaction(() => {
16467
+ for (const operation of ["INSERT", "UPDATE"]) {
16468
+ db.exec(`CREATE TRIGGER IF NOT EXISTS cloud_attr_compact_${operation.toLowerCase()}
16469
+ AFTER ${operation} ON nodes WHEN ${dirty("NEW")}
16470
+ BEGIN UPDATE nodes SET attrs_json = ${compacted("nodes")} WHERE id = NEW.id; END;`);
16471
+ }
16472
+ db.exec(`UPDATE nodes SET attrs_json = ${compacted("nodes")} WHERE ${dirty("nodes")}`);
16473
+ db.prepare("INSERT INTO store_meta(key,value) VALUES (?,?)").run(CLOUD_ATTR_COMPACTION_KEY, "installed");
16474
+ });
16475
+ }
16476
+
16477
+ // ../../packages/local-graph/src/history-attr-slim.ts
16478
+ init_src();
16479
+ var HISTORY_ATTR_SLIM_KEY = "history-attr-slim-1";
16480
+ var SLIM_BATCH = 500;
16481
+ function installHistoryAttrSlim(db) {
16482
+ if (db.prepare("SELECT value FROM store_meta WHERE key = ?").get(HISTORY_ATTR_SLIM_KEY)) return;
16483
+ const select = db.prepare(`SELECT rowid AS rid, json_extract(attrs_json, '$.historyQuality') AS q FROM nodes
16484
+ WHERE rowid > ? AND (json_type(attrs_json, '$.historyQuality.dependencyContext.dependencies') IS NOT NULL
16485
+ OR json_type(attrs_json, '$.historyQuality.reconciliationEvidence') = 'object')
16486
+ ORDER BY rowid LIMIT ${SLIM_BATCH}`);
16487
+ const write = db.prepare(`UPDATE nodes SET attrs_json = json_set(attrs_json, '$.historyQuality', json(?)) WHERE rowid = ?`);
16488
+ let after = 0;
16489
+ for (; ; ) {
16490
+ const rows = select.all(after);
16491
+ if (rows.length === 0) break;
16492
+ db.transaction(() => {
16493
+ for (const { rid, q } of rows) {
16494
+ if (!q) continue;
16495
+ let quality;
16496
+ try {
16497
+ quality = JSON.parse(q);
16498
+ } catch {
16499
+ continue;
16500
+ }
16501
+ if (!quality || typeof quality !== "object" || Array.isArray(quality)) continue;
16502
+ const slim = JSON.stringify(compactHistoryQuality(quality));
16503
+ if (slim !== q) write.run(slim, rid);
16504
+ }
16505
+ });
16506
+ after = rows[rows.length - 1].rid;
16507
+ }
16508
+ db.prepare("INSERT INTO store_meta(key,value) VALUES (?,?)").run(HISTORY_ATTR_SLIM_KEY, "done");
16509
+ }
16510
+
16511
+ // ../../packages/local-graph/src/text-index.ts
16512
+ var TEXT_INDEX_KEY = "text-index-trigram-1";
16513
+ var TEXT_INDEX_MIN_TERM = 3;
16514
+ function installTextIndex(db) {
16515
+ if (db.prepare("SELECT value FROM store_meta WHERE key = ?").get(TEXT_INDEX_KEY)) return;
16516
+ try {
16517
+ db.exec(`CREATE VIRTUAL TABLE temp.fts5_probe USING fts5(x, tokenize='trigram'); DROP TABLE temp.fts5_probe`);
16518
+ } catch {
16519
+ db.prepare("INSERT INTO store_meta(key,value) VALUES (?,?)").run(TEXT_INDEX_KEY, "unavailable");
16520
+ return;
16521
+ }
16522
+ db.transaction(() => {
16523
+ db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS nodes_text
16524
+ USING fts5(description, content='nodes', content_rowid='rowid', tokenize='trigram')`);
16525
+ db.exec(`CREATE TRIGGER IF NOT EXISTS nodes_text_insert AFTER INSERT ON nodes WHEN NEW.valid_to IS NULL
16526
+ BEGIN INSERT INTO nodes_text(rowid, description) VALUES (NEW.rowid, NEW.description); END;`);
16527
+ db.exec(`CREATE TRIGGER IF NOT EXISTS nodes_text_update_out AFTER UPDATE OF description, valid_to ON nodes
16528
+ WHEN OLD.valid_to IS NULL
16529
+ BEGIN INSERT INTO nodes_text(nodes_text, rowid, description) VALUES ('delete', OLD.rowid, OLD.description); END;`);
16530
+ db.exec(`CREATE TRIGGER IF NOT EXISTS nodes_text_update_in AFTER UPDATE OF description, valid_to ON nodes
16531
+ WHEN NEW.valid_to IS NULL
16532
+ BEGIN INSERT INTO nodes_text(rowid, description) VALUES (NEW.rowid, NEW.description); END;`);
16533
+ db.exec(`CREATE TRIGGER IF NOT EXISTS nodes_text_delete AFTER DELETE ON nodes WHEN OLD.valid_to IS NULL
16534
+ BEGIN INSERT INTO nodes_text(nodes_text, rowid, description) VALUES ('delete', OLD.rowid, OLD.description); END;`);
16535
+ db.exec(`INSERT INTO nodes_text(rowid, description) SELECT rowid, description FROM nodes WHERE valid_to IS NULL`);
16536
+ db.prepare("INSERT INTO store_meta(key,value) VALUES (?,?)").run(TEXT_INDEX_KEY, "installed");
16537
+ });
16538
+ }
16539
+ function textIndexReady(db) {
16540
+ return db.prepare("SELECT value FROM store_meta WHERE key = ?").get(TEXT_INDEX_KEY)?.["value"] === "installed";
16541
+ }
16542
+
16543
+ // ../../packages/math/src/constants.ts
16544
+ var NEMORI_DEDUP_COSINE = 0.88;
16545
+
16546
+ // ../../packages/math/src/triage.ts
16547
+ var TRIAGE_GLOBAL_BUCKET = "*";
16548
+ function triageContextBuckets(ctx) {
16549
+ const out = [];
16550
+ for (const s of ctx.stack ?? []) {
16551
+ const t = s.trim().toLowerCase();
16552
+ if (t) out.push(`stack:${t}`);
16553
+ }
16554
+ const domains = Array.isArray(ctx.domain) ? ctx.domain : typeof ctx.domain === "string" ? [ctx.domain] : [];
16555
+ for (const d of domains) {
16556
+ const t = d.trim().toLowerCase();
16557
+ if (t) out.push(`domain:${t}`);
16558
+ }
16559
+ return out;
16560
+ }
16561
+
16562
+ // ../../packages/math/src/relevance.ts
16563
+ function pageRankScale(ranks) {
16564
+ const positive = [...ranks].filter((r) => Number.isFinite(r) && r > 0).sort((a, b) => a - b);
16565
+ return positive.length ? Math.max(1e-9, positive[Math.floor((positive.length - 1) * 0.95)]) : 1;
16566
+ }
16567
+
16568
+ // ../../packages/math/src/leiden.ts
16569
+ function buildGraph(nodeIds, adj) {
16570
+ const nbr = /* @__PURE__ */ new Map();
16571
+ const deg = /* @__PURE__ */ new Map();
16572
+ const present = new Set(nodeIds);
16573
+ for (const id of nodeIds) {
16574
+ nbr.set(id, /* @__PURE__ */ new Map());
16575
+ deg.set(id, 0);
16576
+ }
16577
+ let m2 = 0;
16578
+ for (const id of nodeIds) {
16579
+ for (const { to, weight } of adj.get(id) ?? []) {
16580
+ if (!present.has(to)) continue;
16581
+ const row = nbr.get(id);
16582
+ row.set(to, (row.get(to) ?? 0) + weight);
16583
+ deg.set(id, (deg.get(id) ?? 0) + weight);
16584
+ m2 += weight;
16585
+ }
16586
+ }
16587
+ return { nbr, deg, m2 };
16588
+ }
16589
+ function localMoving(nodeIds, g) {
16590
+ const community = new Map(nodeIds.map((n) => [n, n]));
16591
+ const comTot = new Map(nodeIds.map((n) => [n, g.deg.get(n) ?? 0]));
16592
+ const order = [...nodeIds].sort();
16593
+ const m2 = g.m2 > 0 ? g.m2 : 1;
16594
+ let improved = true;
16595
+ let iter = 0;
16596
+ while (improved && iter < 100) {
16597
+ improved = false;
16598
+ iter++;
16599
+ for (const node of order) {
16600
+ const ci = community.get(node);
16601
+ const ki = g.deg.get(node) ?? 0;
16602
+ comTot.set(ci, (comTot.get(ci) ?? 0) - ki);
16603
+ const wToComm = /* @__PURE__ */ new Map();
16604
+ for (const [nb, w] of g.nbr.get(node) ?? []) {
16605
+ if (nb === node) continue;
16606
+ const c = community.get(nb);
16607
+ wToComm.set(c, (wToComm.get(c) ?? 0) + w);
16608
+ }
16609
+ let bestC = ci;
16610
+ let bestGain = (wToComm.get(ci) ?? 0) - (comTot.get(ci) ?? 0) * ki / m2;
16611
+ for (const [c, kiIn] of [...wToComm.entries()].sort(
16612
+ (a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0
16613
+ )) {
16614
+ const gain = kiIn - (comTot.get(c) ?? 0) * ki / m2;
16615
+ if (gain > bestGain) {
16616
+ bestGain = gain;
16617
+ bestC = c;
16618
+ }
16619
+ }
16620
+ community.set(node, bestC);
16621
+ comTot.set(bestC, (comTot.get(bestC) ?? 0) + ki);
16622
+ if (bestC !== ci) improved = true;
16623
+ }
16624
+ }
16625
+ return community;
16626
+ }
16627
+ function refineConnectivity(community, g) {
16628
+ const members = /* @__PURE__ */ new Map();
16629
+ for (const [id, c] of community) {
16630
+ const l = members.get(c) ?? [];
16631
+ l.push(id);
16632
+ members.set(c, l);
16633
+ }
16634
+ const refined = /* @__PURE__ */ new Map();
16635
+ for (const [, group] of members) {
16636
+ const inGroup = new Set(group);
16637
+ const seen = /* @__PURE__ */ new Set();
16638
+ for (const start of [...group].sort()) {
16639
+ if (seen.has(start)) continue;
16640
+ const comp = [];
16641
+ const stack = [start];
16642
+ seen.add(start);
16643
+ while (stack.length) {
16644
+ const cur = stack.pop();
16645
+ comp.push(cur);
16646
+ for (const [nb] of g.nbr.get(cur) ?? []) {
16647
+ if (inGroup.has(nb) && !seen.has(nb)) {
16648
+ seen.add(nb);
16649
+ stack.push(nb);
16650
+ }
16651
+ }
16652
+ }
16653
+ const anchor = [...comp].sort()[0];
16654
+ for (const id of comp) refined.set(id, anchor);
16655
+ }
16656
+ }
16657
+ return refined;
16658
+ }
16659
+ function normalize(assignment) {
16660
+ const groups = /* @__PURE__ */ new Map();
16661
+ for (const [id, c] of assignment) {
16662
+ const l = groups.get(c) ?? [];
16663
+ l.push(id);
16664
+ groups.set(c, l);
16665
+ }
16666
+ const community = /* @__PURE__ */ new Map();
16667
+ for (const grp of groups.values()) {
16668
+ const anchor = [...grp].sort()[0];
16669
+ const cid = `community_${anchor}`;
16670
+ for (const m of grp) community.set(m, cid);
16671
+ }
16672
+ return { community, count: groups.size, iterations: 1 };
16673
+ }
16674
+ function causalComponents(nodeIds, pairs) {
16675
+ const parent = new Map(nodeIds.map((n) => [n, n]));
16676
+ const find = (x) => {
16677
+ let r = x;
16678
+ while (parent.get(r) !== r) r = parent.get(r);
16679
+ let c = x;
16680
+ while (parent.get(c) !== r) {
16681
+ const next = parent.get(c);
16682
+ parent.set(c, r);
16683
+ c = next;
16684
+ }
16685
+ return r;
16686
+ };
16687
+ for (const [a, b] of pairs) {
16688
+ const ra = find(a);
16689
+ const rb = find(b);
16690
+ if (ra !== rb) parent.set(ra < rb ? rb : ra, ra < rb ? ra : rb);
16691
+ }
16692
+ return find;
16693
+ }
16694
+ function detectCommunitiesLeiden(input, opts = {}) {
16695
+ const idSet = new Set(input.nodeIds);
16696
+ const protect = [...opts.protect ?? []].filter(
16697
+ ([a, b]) => a !== b && idSet.has(a) && idSet.has(b)
16698
+ );
16699
+ if (protect.length === 0) {
16700
+ const g = buildGraph(input.nodeIds, input.adj);
16701
+ return normalize(refineConnectivity(localMoving(input.nodeIds, g), g));
16702
+ }
16703
+ const rep = causalComponents(input.nodeIds, protect);
16704
+ const superIds = [...new Set(input.nodeIds.map(rep))];
16705
+ const superAdj = /* @__PURE__ */ new Map();
16706
+ const acc = /* @__PURE__ */ new Map();
16707
+ for (const id of input.nodeIds) {
16708
+ const ra = rep(id);
16709
+ for (const { to, weight } of input.adj.get(id) ?? []) {
16710
+ if (!idSet.has(to)) continue;
16711
+ const rb = rep(to);
16712
+ if (ra === rb) continue;
16713
+ const row = acc.get(ra) ?? /* @__PURE__ */ new Map();
16714
+ row.set(rb, (row.get(rb) ?? 0) + weight);
16715
+ acc.set(ra, row);
16716
+ }
16717
+ }
16718
+ for (const [a, row] of acc) superAdj.set(a, [...row].map(([to, weight]) => ({ to, weight })));
16719
+ const sg = buildGraph(superIds, superAdj);
16720
+ const superComm = refineConnectivity(localMoving(superIds, sg), sg);
16721
+ const assignment = /* @__PURE__ */ new Map();
16722
+ for (const id of input.nodeIds) assignment.set(id, superComm.get(rep(id)) ?? rep(id));
16723
+ return normalize(assignment);
16724
+ }
16725
+
16726
+ // ../../packages/math/src/void-demand.ts
16727
+ var DEMAND_HOT = Math.log1p(10);
16728
+ var DEMAND_WARM = Math.log1p(5);
16729
+
16399
16730
  // ../../packages/local-graph/src/store.ts
16731
+ var PAGE_RANK_SCALE_KEY = "pageRankScale.p95.v1";
16732
+ var TEXT_SEARCH_COLUMNS = `id, label, description, page_rank,
16733
+ json_object('test', json(CASE WHEN json_type(attrs_json, '$.test') = 'true' THEN 'true' ELSE 'false' END),
16734
+ 'relPath', json_extract(attrs_json, '$.relPath'),
16735
+ 'resolutionSuspect', json(CASE WHEN json_type(attrs_json, '$.resolutionSuspect') = 'true' THEN 'true' ELSE 'false' END),
16736
+ 'resolvedAt', json_extract(attrs_json, '$.resolvedAt'),
16737
+ 'refutedAt', json_extract(attrs_json, '$.refutedAt'),
16738
+ 'refuteReasons', json_extract(attrs_json, '$.refuteReasons'),
16739
+ 'reopenedFrom', json_extract(attrs_json, '$.reopenedFrom'),
16740
+ 'resolvedAs', json_extract(attrs_json, '$.resolvedAs')) AS search_attrs`;
16741
+ function textSearchNode(row) {
16742
+ const attrs = JSON.parse(row.search_attrs);
16743
+ return { id: row.id, label: row.label, description: row.description, pageRank: row.page_rank, attrs };
16744
+ }
16400
16745
  var LOCAL_RULE_OVERRIDES = {
16401
16746
  ANCHORED_AT: {
16402
16747
  from: EDGE_RULES["ANCHORED_AT"]?.from,
@@ -16540,6 +16885,9 @@ CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_E
16540
16885
  DROP INDEX IF EXISTS nodes_live_fingerprint;
16541
16886
  DROP INDEX IF EXISTS nodes_instance_fingerprint;
16542
16887
  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');
16888
+ -- Calibrate rank from a covering scalar index; the global page-rank index
16889
+ -- requires a table lookup per node to exclude frozen versions.
16890
+ CREATE INDEX IF NOT EXISTS nodes_live_page_rank ON nodes(valid_to, page_rank);
16543
16891
  CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
16544
16892
  CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
16545
16893
  -- Expression index on the per-node relPath (stored in attrs_json) so the
@@ -16552,6 +16900,16 @@ CREATE INDEX IF NOT EXISTS nodes_live_description ON nodes(description, label) W
16552
16900
  -- Problems by the file their anchor hint names: the harvest upgrades hints for
16553
16901
  -- every edited file of every turn, and a scan parses every Problem's attrs.
16554
16902
  CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_json, '$.anchorHint.path')) WHERE valid_to IS NULL;
16903
+ -- Problems by the command / checker / file that opened them. The generalizer
16904
+ -- looked these up per shell or tool EVENT by hydrating every Problem: ~12s of
16905
+ -- every minute of daemon main-thread time on a 3.4k-Problem store (2026-10-05).
16906
+ CREATE INDEX IF NOT EXISTS nodes_command_signature ON nodes(json_extract(attrs_json, '$.commandSignature')) WHERE valid_to IS NULL;
16907
+ CREATE INDEX IF NOT EXISTS nodes_diag_command ON nodes(json_extract(attrs_json, '$.diagCommand')) WHERE valid_to IS NULL;
16908
+ CREATE INDEX IF NOT EXISTS nodes_lsp_rel_path ON nodes(json_extract(attrs_json, '$.lspRelPath')) WHERE valid_to IS NULL;
16909
+ -- Local nodes by the cloud twin their upload receipt recorded. Every burst /
16910
+ -- search fusion folds the Network copy of the caller's own knowledge back onto
16911
+ -- the local node; unindexed, the reverse lookup was a ~300ms attrs scan per call.
16912
+ CREATE INDEX IF NOT EXISTS nodes_cloud_node_id ON nodes(json_extract(attrs_json, '$.cloudNodeId')) WHERE valid_to IS NULL;
16555
16913
  -- Nodes still waiting on a root-cause ask. The MCP server checks this on EVERY
16556
16914
  -- tool response (the enrichment piggyback), so it must not parse every
16557
16915
  -- Solution's and Problem's attrs per call. Not partial: the Problem pull reads
@@ -16638,6 +16996,7 @@ var SqliteGraphStore = class {
16638
16996
  * ALL upserts incl. code reindex) — this counter is the knowledge-arrival axis
16639
16997
  * Δseq is measured over, so code reindex churn must NOT advance it. */
16640
16998
  ingestSeq = 0;
16999
+ initialPageRankScale;
16641
17000
  constructor(opts) {
16642
17001
  this.db = openDatabase(opts.path);
16643
17002
  this.db.exec(SCHEMA_SQL);
@@ -16654,6 +17013,9 @@ var SqliteGraphStore = class {
16654
17013
  this.db.exec(TEMPORAL_INDEX_SQL);
16655
17014
  installHistoryCacheFloor(this.db);
16656
17015
  installEditPlaceholderRetirement(this.db);
17016
+ installCloudAttrCompaction(this.db);
17017
+ installHistoryAttrSlim(this.db);
17018
+ installTextIndex(this.db);
16657
17019
  if (!this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(SESSION_REPAIR_KEY)) {
16658
17020
  this.db.transaction(() => {
16659
17021
  this.repairSessionAliases();
@@ -16705,6 +17067,12 @@ var SqliteGraphStore = class {
16705
17067
  setPageRank: this.db.prepare("UPDATE nodes SET page_rank = :v WHERE id = :id"),
16706
17068
  setLandmark: this.db.prepare("UPDATE nodes SET is_landmark = :v WHERE id = :id"),
16707
17069
  setCommunity: this.db.prepare("UPDATE nodes SET community = :v WHERE id = :id"),
17070
+ setLocalCommunity: this.db.prepare(`
17071
+ UPDATE nodes SET
17072
+ attrs_json = CASE WHEN :v IS NULL THEN json_remove(attrs_json, '$.localCommunity')
17073
+ ELSE json_set(attrs_json, '$.localCommunity', :v) END,
17074
+ community = CASE WHEN :col = 1 THEN :v ELSE community END
17075
+ WHERE id = :id`),
16708
17076
  updateNode: this.db.prepare(`
16709
17077
  UPDATE nodes SET
16710
17078
  description = COALESCE(:description, description),
@@ -17025,6 +17393,36 @@ var SqliteGraphStore = class {
17025
17393
  setLandmark(id, isLandmark) {
17026
17394
  this.stmts.setLandmark.run({ id, v: isLandmark ? 1 : 0 });
17027
17395
  }
17396
+ setLocalCommunity(id, community, column) {
17397
+ this.stmts.setLocalCommunity.run({ id, v: community, col: column ? 1 : 0 });
17398
+ }
17399
+ fieldStmts = /* @__PURE__ */ new Map();
17400
+ findNodeFieldsByLabel(label, attrKeys) {
17401
+ for (const k of attrKeys) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) throw new Error(`findNodeFieldsByLabel: bad attr key ${k}`);
17402
+ const cacheKey = attrKeys.join(",");
17403
+ let stmt = this.fieldStmts.get(cacheKey);
17404
+ if (!stmt) {
17405
+ const cols = attrKeys.map((k, i) => `, json_extract(attrs_json, '$.${k}') AS a${i}`).join("");
17406
+ stmt = this.db.prepare(
17407
+ `SELECT id, description, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL`
17408
+ );
17409
+ this.fieldStmts.set(cacheKey, stmt);
17410
+ }
17411
+ return stmt.all(label).map((r) => {
17412
+ const attrs = {};
17413
+ attrKeys.forEach((k, i) => {
17414
+ const v = r[`a${i}`];
17415
+ if (v !== null && v !== void 0) attrs[k] = v;
17416
+ });
17417
+ return {
17418
+ id: r["id"],
17419
+ description: r["description"] ?? "",
17420
+ community: r["community"] ?? null,
17421
+ extractionConfidence: r["extraction_confidence"],
17422
+ attrs
17423
+ };
17424
+ });
17425
+ }
17028
17426
  setCommunity(id, community) {
17029
17427
  this.stmts.setCommunity.run({ id, v: community });
17030
17428
  }
@@ -17151,6 +17549,19 @@ var SqliteGraphStore = class {
17151
17549
  * read attrs and never the vector. `findNodesByLabel` decodes every embedding
17152
17550
  * blob; the liveness watch's coverage pass spent 3.4s of a 4.3s main-thread
17153
17551
  * hold doing exactly that on a 227k-node store (2026-09-24). */
17552
+ findNodeStampsByLabel(label, attrKey) {
17553
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attrKey)) throw new Error(`findNodeStampsByLabel: bad attr key ${attrKey}`);
17554
+ const rows = this.db.prepare(
17555
+ `SELECT id, created_at, created_at_seq, json_type(attrs_json, '$.${attrKey}') IS NOT NULL AS has
17556
+ FROM nodes WHERE label = ? AND valid_to IS NULL`
17557
+ ).all(label);
17558
+ return rows.map((r) => ({
17559
+ id: r.id,
17560
+ createdAt: r.created_at,
17561
+ ...r.created_at_seq != null ? { createdAtSeq: r.created_at_seq } : {},
17562
+ has: r.has === 1
17563
+ }));
17564
+ }
17154
17565
  findNodeAttrsByLabel(label) {
17155
17566
  const rows = this.stmts.attrsByLabel.all(label);
17156
17567
  return rows.map((r) => ({
@@ -17257,6 +17668,60 @@ var SqliteGraphStore = class {
17257
17668
  ).all(...terms.map((t) => `%${t}%`), Math.max(1, limit));
17258
17669
  return rows.map((r) => ({ node: rowToNode(r), matched: Number(r.matched) }));
17259
17670
  }
17671
+ searchTextCandidates(terms) {
17672
+ if (terms.length === 0) return [];
17673
+ const indexed = terms.filter((t) => t.length >= TEXT_INDEX_MIN_TERM);
17674
+ if (indexed.length > 0 && textIndexReady(this.db)) {
17675
+ const short = terms.filter((t) => t.length < TEXT_INDEX_MIN_TERM).map((t) => t.toLowerCase());
17676
+ const rows2 = this.db.prepare(`WITH hits AS MATERIALIZED (
17677
+ SELECT t.rowid AS rid, COUNT(*) AS matched FROM json_each(:terms) q, nodes_text t
17678
+ WHERE t.description LIKE '%' || q.value || '%' GROUP BY t.rowid
17679
+ ) SELECT ${TEXT_SEARCH_COLUMNS}, hits.matched FROM hits JOIN nodes ON nodes.rowid = hits.rid
17680
+ WHERE nodes.valid_to IS NULL ORDER BY page_rank DESC, last_updated_at DESC`).all({ terms: JSON.stringify(indexed) });
17681
+ return rows2.map((row) => {
17682
+ const text = row.description.toLowerCase();
17683
+ const extra = short.filter((t) => text.includes(t)).length;
17684
+ return { node: textSearchNode(row), matched: row.matched + extra };
17685
+ });
17686
+ }
17687
+ const rows = this.db.prepare(`WITH matches AS MATERIALIZED (
17688
+ SELECT n.id, COUNT(*) AS matched FROM nodes n INDEXED BY nodes_live_description, json_each(:terms) t
17689
+ WHERE n.valid_to IS NULL AND n.description LIKE '%' || t.value || '%'
17690
+ GROUP BY n.id
17691
+ ) SELECT ${TEXT_SEARCH_COLUMNS}, matches.matched FROM nodes
17692
+ JOIN matches USING (id) ORDER BY page_rank DESC, last_updated_at DESC`).all({ terms: JSON.stringify(terms) });
17693
+ return rows.map((row) => ({ node: textSearchNode(row), matched: row.matched }));
17694
+ }
17695
+ commentAnchors(commentIds) {
17696
+ const out = /* @__PURE__ */ new Map();
17697
+ if (commentIds.length === 0) return out;
17698
+ const rows = this.db.prepare(`WITH a AS MATERIALIZED (
17699
+ SELECT q.value AS cid, COALESCE(
17700
+ (SELECT to_id FROM edges WHERE from_id = q.value AND type IN ('EXPLAINS', 'ANNOTATES') AND valid_to IS NULL LIMIT 1),
17701
+ (SELECT from_id FROM edges WHERE to_id = q.value AND type = 'DOCUMENTED_BY' AND valid_to IS NULL LIMIT 1)
17702
+ ) AS aid FROM json_each(:ids) q
17703
+ ) SELECT a.cid AS cid, ${TEXT_SEARCH_COLUMNS} FROM a JOIN nodes ON nodes.id = a.aid AND nodes.valid_to IS NULL`).all({ ids: JSON.stringify(commentIds) });
17704
+ for (const row of rows) out.set(row.cid, textSearchNode(row));
17705
+ return out;
17706
+ }
17707
+ getTextSearchNode(id) {
17708
+ const row = this.db.prepare(`SELECT ${TEXT_SEARCH_COLUMNS} FROM nodes WHERE id = ? AND valid_to IS NULL`).get(id);
17709
+ return row ? textSearchNode(row) : null;
17710
+ }
17711
+ readPageRankScale() {
17712
+ const rows = this.db.prepare("SELECT page_rank FROM nodes WHERE valid_to IS NULL AND page_rank > 0").all();
17713
+ return pageRankScale(rows.map((row) => row.page_rank));
17714
+ }
17715
+ pageRankScale() {
17716
+ const value = Number(this.getMeta(PAGE_RANK_SCALE_KEY));
17717
+ if (Number.isFinite(value) && value > 0) return value;
17718
+ return this.initialPageRankScale ??= this.readPageRankScale();
17719
+ }
17720
+ refreshPageRankScale() {
17721
+ const scale = this.readPageRankScale();
17722
+ this.setMeta(PAGE_RANK_SCALE_KEY, String(scale));
17723
+ return scale;
17724
+ }
17260
17725
  typedEdgeStmt(sql) {
17261
17726
  let stmt = this.typedEdgeStmts.get(sql);
17262
17727
  if (!stmt) {
@@ -17491,6 +17956,19 @@ var SqliteGraphStore = class {
17491
17956
  ).get(attr, `%${marker}%`, limit);
17492
17957
  return { count: Number(r.count), total: Number(r.total) };
17493
17958
  }
17959
+ liveNodeIdsByCloudNodeId(cloudIds) {
17960
+ const out = /* @__PURE__ */ new Map();
17961
+ const CHUNK = 900;
17962
+ for (let i = 0; i < cloudIds.length; i += CHUNK) {
17963
+ const slice = cloudIds.slice(i, i + CHUNK);
17964
+ if (slice.length === 0) continue;
17965
+ const rows = this.db.prepare(
17966
+ `SELECT id, json_extract(attrs_json, '$.cloudNodeId') AS cloud FROM nodes INDEXED BY nodes_cloud_node_id WHERE json_extract(attrs_json, '$.cloudNodeId') IN (${slice.map(() => "?").join(",")}) AND valid_to IS NULL ORDER BY id`
17967
+ ).all(...slice);
17968
+ for (const r of rows) out.set(r.cloud, [...out.get(r.cloud) ?? [], r.id]);
17969
+ }
17970
+ return out;
17971
+ }
17494
17972
  liveNodeIds(ids) {
17495
17973
  const live = /* @__PURE__ */ new Set();
17496
17974
  const CHUNK = 900;
@@ -17552,7 +18030,10 @@ var SqliteGraphStore = class {
17552
18030
  if (relPaths.length === 0) return [];
17553
18031
  const placeholders = relPaths.map(() => "?").join(",");
17554
18032
  const rows = this.db.prepare(
17555
- `SELECT * FROM nodes WHERE valid_to IS NULL AND json_extract(attrs_json, '$.relPath') IN (${placeholders})`
18033
+ // INDEXED BY: unforced, the planner took nodes_live_page_rank and
18034
+ // json-parsed every live row — 11–25s per reindexed file pair on the
18035
+ // live store vs 7ms here (2026-10-08).
18036
+ `SELECT * FROM nodes INDEXED BY nodes_relpath WHERE valid_to IS NULL AND json_extract(attrs_json, '$.relPath') IN (${placeholders})`
17556
18037
  ).all(...relPaths);
17557
18038
  return rows.map(rowToNode);
17558
18039
  }
@@ -17802,183 +18283,6 @@ function percolate(l1, l2, opts) {
17802
18283
  init_src();
17803
18284
  init_src2();
17804
18285
 
17805
- // ../../packages/math/src/constants.ts
17806
- var NEMORI_DEDUP_COSINE = 0.88;
17807
-
17808
- // ../../packages/math/src/triage.ts
17809
- var TRIAGE_GLOBAL_BUCKET = "*";
17810
- function triageContextBuckets(ctx) {
17811
- const out = [];
17812
- for (const s of ctx.stack ?? []) {
17813
- const t = s.trim().toLowerCase();
17814
- if (t) out.push(`stack:${t}`);
17815
- }
17816
- const domains = Array.isArray(ctx.domain) ? ctx.domain : typeof ctx.domain === "string" ? [ctx.domain] : [];
17817
- for (const d of domains) {
17818
- const t = d.trim().toLowerCase();
17819
- if (t) out.push(`domain:${t}`);
17820
- }
17821
- return out;
17822
- }
17823
-
17824
- // ../../packages/math/src/leiden.ts
17825
- function buildGraph(nodeIds, adj) {
17826
- const nbr = /* @__PURE__ */ new Map();
17827
- const deg = /* @__PURE__ */ new Map();
17828
- const present = new Set(nodeIds);
17829
- for (const id of nodeIds) {
17830
- nbr.set(id, /* @__PURE__ */ new Map());
17831
- deg.set(id, 0);
17832
- }
17833
- let m2 = 0;
17834
- for (const id of nodeIds) {
17835
- for (const { to, weight } of adj.get(id) ?? []) {
17836
- if (!present.has(to)) continue;
17837
- const row = nbr.get(id);
17838
- row.set(to, (row.get(to) ?? 0) + weight);
17839
- deg.set(id, (deg.get(id) ?? 0) + weight);
17840
- m2 += weight;
17841
- }
17842
- }
17843
- return { nbr, deg, m2 };
17844
- }
17845
- function localMoving(nodeIds, g) {
17846
- const community = new Map(nodeIds.map((n) => [n, n]));
17847
- const comTot = new Map(nodeIds.map((n) => [n, g.deg.get(n) ?? 0]));
17848
- const order = [...nodeIds].sort();
17849
- const m2 = g.m2 > 0 ? g.m2 : 1;
17850
- let improved = true;
17851
- let iter = 0;
17852
- while (improved && iter < 100) {
17853
- improved = false;
17854
- iter++;
17855
- for (const node of order) {
17856
- const ci = community.get(node);
17857
- const ki = g.deg.get(node) ?? 0;
17858
- comTot.set(ci, (comTot.get(ci) ?? 0) - ki);
17859
- const wToComm = /* @__PURE__ */ new Map();
17860
- for (const [nb, w] of g.nbr.get(node) ?? []) {
17861
- if (nb === node) continue;
17862
- const c = community.get(nb);
17863
- wToComm.set(c, (wToComm.get(c) ?? 0) + w);
17864
- }
17865
- let bestC = ci;
17866
- let bestGain = (wToComm.get(ci) ?? 0) - (comTot.get(ci) ?? 0) * ki / m2;
17867
- for (const [c, kiIn] of [...wToComm.entries()].sort(
17868
- (a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0
17869
- )) {
17870
- const gain = kiIn - (comTot.get(c) ?? 0) * ki / m2;
17871
- if (gain > bestGain) {
17872
- bestGain = gain;
17873
- bestC = c;
17874
- }
17875
- }
17876
- community.set(node, bestC);
17877
- comTot.set(bestC, (comTot.get(bestC) ?? 0) + ki);
17878
- if (bestC !== ci) improved = true;
17879
- }
17880
- }
17881
- return community;
17882
- }
17883
- function refineConnectivity(community, g) {
17884
- const members = /* @__PURE__ */ new Map();
17885
- for (const [id, c] of community) {
17886
- const l = members.get(c) ?? [];
17887
- l.push(id);
17888
- members.set(c, l);
17889
- }
17890
- const refined = /* @__PURE__ */ new Map();
17891
- for (const [, group] of members) {
17892
- const inGroup = new Set(group);
17893
- const seen = /* @__PURE__ */ new Set();
17894
- for (const start of [...group].sort()) {
17895
- if (seen.has(start)) continue;
17896
- const comp = [];
17897
- const stack = [start];
17898
- seen.add(start);
17899
- while (stack.length) {
17900
- const cur = stack.pop();
17901
- comp.push(cur);
17902
- for (const [nb] of g.nbr.get(cur) ?? []) {
17903
- if (inGroup.has(nb) && !seen.has(nb)) {
17904
- seen.add(nb);
17905
- stack.push(nb);
17906
- }
17907
- }
17908
- }
17909
- const anchor = [...comp].sort()[0];
17910
- for (const id of comp) refined.set(id, anchor);
17911
- }
17912
- }
17913
- return refined;
17914
- }
17915
- function normalize(assignment) {
17916
- const groups = /* @__PURE__ */ new Map();
17917
- for (const [id, c] of assignment) {
17918
- const l = groups.get(c) ?? [];
17919
- l.push(id);
17920
- groups.set(c, l);
17921
- }
17922
- const community = /* @__PURE__ */ new Map();
17923
- for (const grp of groups.values()) {
17924
- const anchor = [...grp].sort()[0];
17925
- const cid = `community_${anchor}`;
17926
- for (const m of grp) community.set(m, cid);
17927
- }
17928
- return { community, count: groups.size, iterations: 1 };
17929
- }
17930
- function causalComponents(nodeIds, pairs) {
17931
- const parent = new Map(nodeIds.map((n) => [n, n]));
17932
- const find = (x) => {
17933
- let r = x;
17934
- while (parent.get(r) !== r) r = parent.get(r);
17935
- let c = x;
17936
- while (parent.get(c) !== r) {
17937
- const next = parent.get(c);
17938
- parent.set(c, r);
17939
- c = next;
17940
- }
17941
- return r;
17942
- };
17943
- for (const [a, b] of pairs) {
17944
- const ra = find(a);
17945
- const rb = find(b);
17946
- if (ra !== rb) parent.set(ra < rb ? rb : ra, ra < rb ? ra : rb);
17947
- }
17948
- return find;
17949
- }
17950
- function detectCommunitiesLeiden(input, opts = {}) {
17951
- const idSet = new Set(input.nodeIds);
17952
- const protect = [...opts.protect ?? []].filter(
17953
- ([a, b]) => a !== b && idSet.has(a) && idSet.has(b)
17954
- );
17955
- if (protect.length === 0) {
17956
- const g = buildGraph(input.nodeIds, input.adj);
17957
- return normalize(refineConnectivity(localMoving(input.nodeIds, g), g));
17958
- }
17959
- const rep = causalComponents(input.nodeIds, protect);
17960
- const superIds = [...new Set(input.nodeIds.map(rep))];
17961
- const superAdj = /* @__PURE__ */ new Map();
17962
- const acc = /* @__PURE__ */ new Map();
17963
- for (const id of input.nodeIds) {
17964
- const ra = rep(id);
17965
- for (const { to, weight } of input.adj.get(id) ?? []) {
17966
- if (!idSet.has(to)) continue;
17967
- const rb = rep(to);
17968
- if (ra === rb) continue;
17969
- const row = acc.get(ra) ?? /* @__PURE__ */ new Map();
17970
- row.set(rb, (row.get(rb) ?? 0) + weight);
17971
- acc.set(ra, row);
17972
- }
17973
- }
17974
- for (const [a, row] of acc) superAdj.set(a, [...row].map(([to, weight]) => ({ to, weight })));
17975
- const sg = buildGraph(superIds, superAdj);
17976
- const superComm = refineConnectivity(localMoving(superIds, sg), sg);
17977
- const assignment = /* @__PURE__ */ new Map();
17978
- for (const id of input.nodeIds) assignment.set(id, superComm.get(rep(id)) ?? rep(id));
17979
- return normalize(assignment);
17980
- }
17981
-
17982
18286
  // ../../packages/local-graph/src/triage.ts
17983
18287
  init_src();
17984
18288
  init_src();
@@ -19368,7 +19672,7 @@ var DOC_COMMENT_KINDS = /* @__PURE__ */ new Set(["docstring", "explanation"]);
19368
19672
  var MAX_DOC_CHARS = 600;
19369
19673
  function buildDocIndex(store) {
19370
19674
  const byName = /* @__PURE__ */ new Map();
19371
- for (const c of store.findNodesByLabel("Comment")) {
19675
+ for (const c of store.findNodeFieldsByLabel("Comment", ["attachedTo", "kind", "text"])) {
19372
19676
  const name = c.attrs["attachedTo"];
19373
19677
  const kind = c.attrs["kind"];
19374
19678
  if (typeof name !== "string" || !name) continue;
@@ -19384,7 +19688,7 @@ function buildSymbolLexicon(store) {
19384
19688
  const docs = buildDocIndex(store);
19385
19689
  const candidates = [];
19386
19690
  for (const [label, kind] of SYMBOL_LABELS) {
19387
- for (const n of store.findNodesByLabel(label)) {
19691
+ for (const n of store.findNodeFieldsByLabel(label, ["qname"])) {
19388
19692
  const qname = String(n.attrs["qname"] ?? "");
19389
19693
  const doc = (n.description ? docs.get(n.description) : void 0) ?? (qname ? docs.get(qname) : void 0);
19390
19694
  for (const name of [n.description, qname]) {
@@ -19490,7 +19794,7 @@ function buildFileLexicon(store) {
19490
19794
  const basenameCount = /* @__PURE__ */ new Map();
19491
19795
  const stemRelPaths = /* @__PURE__ */ new Map();
19492
19796
  const entries = [];
19493
- for (const n of store.findNodesByLabel("File")) {
19797
+ for (const n of store.findNodeFieldsByLabel("File", ["relPath"])) {
19494
19798
  const relPath = typeof n.attrs["relPath"] === "string" && n.attrs["relPath"].length > 0 ? n.attrs["relPath"] : n.description;
19495
19799
  if (!relPath) continue;
19496
19800
  const basename = relPath.split("/").pop();
@@ -19812,9 +20116,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
19812
20116
  collapsedEdges: 0
19813
20117
  };
19814
20118
  const sharedCauses = /* @__PURE__ */ new Map();
19815
- const causalQueue = store.findNodesByLabel("Problem").filter(
19816
- (n) => n.attrs["source"] !== "cloud" && n.attrs["resolvedAs"] !== PROBLEM_RESOLUTION.FALSE_POSITIVE && !ignored(n.description)
19817
- ).map((n) => n.id);
20119
+ const causalQueue = store.findNodeFieldsByLabel("Problem", ["source", "resolvedAs"]).filter((r) => r.attrs["source"] !== "cloud" && r.attrs["resolvedAs"] !== PROBLEM_RESOLUTION.FALSE_POSITIVE).map((r) => r.id).filter((id) => ignorePatterns.length === 0 || !ignored(store.getNode(id)?.description ?? ""));
19818
20120
  const causalSeen = new Set(causalQueue);
19819
20121
  for (let i = 0; i < causalQueue.length; i++) {
19820
20122
  const id = causalQueue[i];
@@ -19830,7 +20132,20 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
19830
20132
  for (const label of INSTANCE_LABELS) {
19831
20133
  const ref = [];
19832
20134
  includedByLabel.set(label, ref);
19833
- const population = label === "RootCause" && opts.sharedStore ? [...store.findNodesByLabel(label), ...sharedCauses.values()] : store.findNodesByLabel(label);
20135
+ const local = [];
20136
+ for (const row of store.findNodeFieldsByLabel(label, ["source", "privateFinding"])) {
20137
+ if (row.attrs["source"] !== "cloud") {
20138
+ const n = store.getNode(row.id);
20139
+ if (n) local.push(n);
20140
+ continue;
20141
+ }
20142
+ if (seen.has(row.id) || walked.has(row.id)) continue;
20143
+ walked.add(row.id);
20144
+ composition.candidates++;
20145
+ if ((row.attrs["privateFinding"] || label === "Vulnerability") && !opts.project) composition.privateTargetMissing++;
20146
+ else composition.cloudSourced++;
20147
+ }
20148
+ const population = label === "RootCause" && opts.sharedStore ? [...local, ...sharedCauses.values()] : local;
19834
20149
  for (const n of population) {
19835
20150
  if (seen.has(n.id)) continue;
19836
20151
  if (walked.has(n.id)) continue;