@inerrata-corporation/errata 2.0.21-dev.3904 → 2.0.21-dev.3929

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.
@@ -16810,6 +16810,10 @@ CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_js
16810
16810
  CREATE INDEX IF NOT EXISTS nodes_command_signature ON nodes(json_extract(attrs_json, '$.commandSignature')) WHERE valid_to IS NULL;
16811
16811
  CREATE INDEX IF NOT EXISTS nodes_diag_command ON nodes(json_extract(attrs_json, '$.diagCommand')) WHERE valid_to IS NULL;
16812
16812
  CREATE INDEX IF NOT EXISTS nodes_lsp_rel_path ON nodes(json_extract(attrs_json, '$.lspRelPath')) WHERE valid_to IS NULL;
16813
+ -- Local nodes by the cloud twin their upload receipt recorded. Every burst /
16814
+ -- search fusion folds the Network copy of the caller's own knowledge back onto
16815
+ -- the local node; unindexed, the reverse lookup was a ~300ms attrs scan per call.
16816
+ CREATE INDEX IF NOT EXISTS nodes_cloud_node_id ON nodes(json_extract(attrs_json, '$.cloudNodeId')) WHERE valid_to IS NULL;
16813
16817
  -- Nodes still waiting on a root-cause ask. The MCP server checks this on EVERY
16814
16818
  -- tool response (the enrichment piggyback), so it must not parse every
16815
16819
  -- Solution's and Problem's attrs per call. Not partial: the Problem pull reads
@@ -17779,6 +17783,19 @@ var SqliteGraphStore = class {
17779
17783
  ).get(attr, `%${marker}%`, limit);
17780
17784
  return { count: Number(r.count), total: Number(r.total) };
17781
17785
  }
17786
+ liveNodeIdsByCloudNodeId(cloudIds) {
17787
+ const out = /* @__PURE__ */ new Map();
17788
+ const CHUNK = 900;
17789
+ for (let i = 0; i < cloudIds.length; i += CHUNK) {
17790
+ const slice = cloudIds.slice(i, i + CHUNK);
17791
+ if (slice.length === 0) continue;
17792
+ const rows = this.db.prepare(
17793
+ `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`
17794
+ ).all(...slice);
17795
+ for (const r of rows) out.set(r.cloud, [...out.get(r.cloud) ?? [], r.id]);
17796
+ }
17797
+ return out;
17798
+ }
17782
17799
  liveNodeIds(ids) {
17783
17800
  const live = /* @__PURE__ */ new Set();
17784
17801
  const CHUNK = 900;
@@ -17840,7 +17857,10 @@ var SqliteGraphStore = class {
17840
17857
  if (relPaths.length === 0) return [];
17841
17858
  const placeholders = relPaths.map(() => "?").join(",");
17842
17859
  const rows = this.db.prepare(
17843
- `SELECT * FROM nodes WHERE valid_to IS NULL AND json_extract(attrs_json, '$.relPath') IN (${placeholders})`
17860
+ // INDEXED BY: unforced, the planner took nodes_live_page_rank and
17861
+ // json-parsed every live row — 11–25s per reindexed file pair on the
17862
+ // live store vs 7ms here (2026-10-08).
17863
+ `SELECT * FROM nodes INDEXED BY nodes_relpath WHERE valid_to IS NULL AND json_extract(attrs_json, '$.relPath') IN (${placeholders})`
17844
17864
  ).all(...relPaths);
17845
17865
  return rows.map(rowToNode);
17846
17866
  }
package/errata.mjs CHANGED
@@ -19568,6 +19568,10 @@ CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_js
19568
19568
  CREATE INDEX IF NOT EXISTS nodes_command_signature ON nodes(json_extract(attrs_json, '$.commandSignature')) WHERE valid_to IS NULL;
19569
19569
  CREATE INDEX IF NOT EXISTS nodes_diag_command ON nodes(json_extract(attrs_json, '$.diagCommand')) WHERE valid_to IS NULL;
19570
19570
  CREATE INDEX IF NOT EXISTS nodes_lsp_rel_path ON nodes(json_extract(attrs_json, '$.lspRelPath')) WHERE valid_to IS NULL;
19571
+ -- Local nodes by the cloud twin their upload receipt recorded. Every burst /
19572
+ -- search fusion folds the Network copy of the caller's own knowledge back onto
19573
+ -- the local node; unindexed, the reverse lookup was a ~300ms attrs scan per call.
19574
+ CREATE INDEX IF NOT EXISTS nodes_cloud_node_id ON nodes(json_extract(attrs_json, '$.cloudNodeId')) WHERE valid_to IS NULL;
19571
19575
  -- Nodes still waiting on a root-cause ask. The MCP server checks this on EVERY
19572
19576
  -- tool response (the enrichment piggyback), so it must not parse every
19573
19577
  -- Solution's and Problem's attrs per call. Not partial: the Problem pull reads
@@ -20480,6 +20484,19 @@ CREATE INDEX IF NOT EXISTS nodes_enrichment_pending ON nodes(json_extract(attrs_
20480
20484
  ).get(attr, `%${marker}%`, limit);
20481
20485
  return { count: Number(r.count), total: Number(r.total) };
20482
20486
  }
20487
+ liveNodeIdsByCloudNodeId(cloudIds) {
20488
+ const out2 = /* @__PURE__ */ new Map();
20489
+ const CHUNK = 900;
20490
+ for (let i2 = 0; i2 < cloudIds.length; i2 += CHUNK) {
20491
+ const slice = cloudIds.slice(i2, i2 + CHUNK);
20492
+ if (slice.length === 0) continue;
20493
+ const rows = this.db.prepare(
20494
+ `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`
20495
+ ).all(...slice);
20496
+ for (const r of rows) out2.set(r.cloud, [...out2.get(r.cloud) ?? [], r.id]);
20497
+ }
20498
+ return out2;
20499
+ }
20483
20500
  liveNodeIds(ids) {
20484
20501
  const live3 = /* @__PURE__ */ new Set();
20485
20502
  const CHUNK = 900;
@@ -20541,7 +20558,10 @@ CREATE INDEX IF NOT EXISTS nodes_enrichment_pending ON nodes(json_extract(attrs_
20541
20558
  if (relPaths.length === 0) return [];
20542
20559
  const placeholders = relPaths.map(() => "?").join(",");
20543
20560
  const rows = this.db.prepare(
20544
- `SELECT * FROM nodes WHERE valid_to IS NULL AND json_extract(attrs_json, '$.relPath') IN (${placeholders})`
20561
+ // INDEXED BY: unforced, the planner took nodes_live_page_rank and
20562
+ // json-parsed every live row — 11–25s per reindexed file pair on the
20563
+ // live store vs 7ms here (2026-10-08).
20564
+ `SELECT * FROM nodes INDEXED BY nodes_relpath WHERE valid_to IS NULL AND json_extract(attrs_json, '$.relPath') IN (${placeholders})`
20545
20565
  ).all(...relPaths);
20546
20566
  return rows.map(rowToNode);
20547
20567
  }
@@ -26722,6 +26742,30 @@ function sidecarForNodes(sidecar, nodes) {
26722
26742
  }
26723
26743
  return count > 0 ? filtered : void 0;
26724
26744
  }
26745
+ function wireVia(value) {
26746
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
26747
+ const v = value;
26748
+ const { edgeType, parentId, forward } = v;
26749
+ if (typeof edgeType !== "string" || !edgeType || typeof parentId !== "string" || !parentId) return null;
26750
+ if (typeof forward !== "boolean") return null;
26751
+ return { edgeType, parentId, forward };
26752
+ }
26753
+ function searchVia(nodes) {
26754
+ const out2 = {};
26755
+ let any2 = false;
26756
+ for (const n of nodes) {
26757
+ const via = wireVia(n.attrs?.["via"]);
26758
+ if (!via || out2[n.canonicalId]) continue;
26759
+ out2[n.canonicalId] = via;
26760
+ any2 = true;
26761
+ }
26762
+ return any2 ? out2 : void 0;
26763
+ }
26764
+ function withoutVia(attrs) {
26765
+ if (!("via" in attrs)) return attrs;
26766
+ const { via: _via, ...rest2 } = attrs;
26767
+ return rest2;
26768
+ }
26725
26769
  function toWirePayload(batch, runId) {
26726
26770
  const droppedNodeIds = new Set(
26727
26771
  batch.nodes.filter((n) => !isCloudWritableNodeLabel(n.label)).map((n) => n.id)
@@ -27401,6 +27445,7 @@ var init_client = __esm({
27401
27445
  if (q.scope) qs.set("scope", q.scope);
27402
27446
  if (q.community) qs.set("community", "true");
27403
27447
  if (q.bridges) qs.set("bridges", "true");
27448
+ if (q.via) qs.set("via", "true");
27404
27449
  const publicCtx = process.env["ERRATA_PRIMING_PUBLIC_CONTEXT"];
27405
27450
  const usePublicCtx = Boolean(publicCtx && publicCtx !== "0" && q.channel === "priming");
27406
27451
  const UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -27415,10 +27460,12 @@ var init_client = __esm({
27415
27460
  } : void 0
27416
27461
  );
27417
27462
  const { nodes, edges } = this.toCastalia(res);
27463
+ const via = searchVia(res.nodes);
27418
27464
  return {
27419
27465
  nodes,
27420
27466
  edges,
27421
27467
  generatedAt: res.generatedAt,
27468
+ ...via ? { via } : {},
27422
27469
  ...res.voidSignal ? { voidSignal: res.voidSignal } : {},
27423
27470
  ...res.seeds ? { seeds: res.seeds } : {}
27424
27471
  };
@@ -27448,7 +27495,7 @@ var init_client = __esm({
27448
27495
  isLandmark: false,
27449
27496
  community: null,
27450
27497
  stability: "stable",
27451
- attrs: { ...localCloudAttrs(n.attrs), source: "cloud", cloudNodeId: n.nodeId }
27498
+ attrs: { ...withoutVia(localCloudAttrs(n.attrs)), source: "cloud", cloudNodeId: n.nodeId }
27452
27499
  }));
27453
27500
  const edges = res.edges.map((e) => ({
27454
27501
  id: digest({ from: e.fromCanonicalId, type: e.type, to: e.toCanonicalId }),
@@ -34955,33 +35002,90 @@ function collectiveScore(hit, cap) {
34955
35002
  function mergeDualBurst(local, cloud, opts = {}) {
34956
35003
  const boost = opts.corroborationBoost ?? 0.15;
34957
35004
  const cap = opts.collectiveScoreCap ?? 0.5;
34958
- const cloudById = new Map(cloud.map((c) => [c.id, c]));
34959
- const localIds = new Set(local.map((l) => l.id));
35005
+ const twins = opts.twins;
35006
+ const cloudIdsOf = (id) => twins?.cloudIdsOf(id) ?? [];
35007
+ const seedSet = /* @__PURE__ */ new Set();
35008
+ if (opts.seedId) {
35009
+ seedSet.add(opts.seedId);
35010
+ for (const c of cloudIdsOf(opts.seedId)) seedSet.add(c);
35011
+ }
35012
+ const isSeedTwin = (id) => seedSet.has(id) || opts.seedId != null && twins?.localOf(id)?.id === opts.seedId;
34960
35013
  const nodes = [];
35014
+ const byId = /* @__PURE__ */ new Map();
35015
+ const owner = /* @__PURE__ */ new Map();
34961
35016
  let corroboratedCount = 0;
34962
- for (const l of local) {
34963
- const twin = cloudById.get(l.id);
34964
- const corroborated = twin != null;
34965
- if (corroborated) corroboratedCount++;
34966
- nodes.push({
35017
+ let collectiveCount = 0;
35018
+ const keptLocal = local.filter((l) => !isSeedTwin(l.id)).sort((a, b) => b.score - a.score);
35019
+ const recordedBy = /* @__PURE__ */ new Map();
35020
+ for (const l of keptLocal) {
35021
+ for (const c of cloudIdsOf(l.id)) if (c !== l.id && !recordedBy.has(c)) recordedBy.set(c, l.id);
35022
+ }
35023
+ const copies = [];
35024
+ for (const l of keptLocal) {
35025
+ if (recordedBy.has(l.id)) {
35026
+ copies.push(l);
35027
+ continue;
35028
+ }
35029
+ const n = {
34967
35030
  id: l.id,
34968
35031
  label: l.node.label,
34969
35032
  name: l.node.description,
34970
- score: corroborated ? l.score * (1 + boost) : l.score,
35033
+ score: l.score,
34971
35034
  hops: l.hops,
34972
- provenance: corroborated ? "corroborated" : "local",
34973
- ...twin?.usageCount != null ? { usageCount: twin.usageCount } : {},
34974
- ...twin?.rel ? { rel: twin.rel } : {},
34975
- ...twin?.community ? { community: twin.community } : {},
35035
+ provenance: "local",
34976
35036
  why: l.components,
34977
35037
  ...l.via ? { via: l.via } : {}
34978
- });
35038
+ };
35039
+ nodes.push(n);
35040
+ byId.set(n.id, n);
35041
+ for (const c of cloudIdsOf(l.id)) if (!owner.has(c)) owner.set(c, n);
35042
+ }
35043
+ for (const l of copies) {
35044
+ const host = byId.get(recordedBy.get(l.id));
35045
+ if (host) addTwin(host, l.id);
34979
35046
  }
34980
- let collectiveCount = 0;
34981
35047
  for (const c of cloud) {
34982
- if (localIds.has(c.id)) continue;
35048
+ if (isSeedTwin(c.id)) continue;
35049
+ const recorded = owner.get(c.id);
35050
+ if (recorded) {
35051
+ foldSelfUpload(recorded, c);
35052
+ continue;
35053
+ }
35054
+ const exact = byId.get(c.id);
35055
+ if (exact && exact.provenance !== "collective") {
35056
+ absorb(exact, c);
35057
+ continue;
35058
+ }
35059
+ const mine = twins?.localOf(c.id);
35060
+ if (mine && !isSeedTwin(mine.id)) {
35061
+ const existing = byId.get(mine.id);
35062
+ if (existing) {
35063
+ if (existing.provenance === "collective") {
35064
+ existing.provenance = "local";
35065
+ collectiveCount--;
35066
+ }
35067
+ owner.set(c.id, existing);
35068
+ absorb(existing, c);
35069
+ continue;
35070
+ }
35071
+ const n2 = {
35072
+ id: mine.id,
35073
+ label: mine.label,
35074
+ name: mine.description,
35075
+ score: collectiveScore(c, cap),
35076
+ hops: 1,
35077
+ provenance: "local"
35078
+ };
35079
+ nodes.push(n2);
35080
+ byId.set(n2.id, n2);
35081
+ for (const cid of cloudIdsOf(mine.id)) if (!owner.has(cid)) owner.set(cid, n2);
35082
+ owner.set(c.id, n2);
35083
+ absorb(n2, c);
35084
+ continue;
35085
+ }
35086
+ if (exact) continue;
34983
35087
  collectiveCount++;
34984
- nodes.push({
35088
+ const n = {
34985
35089
  id: c.id,
34986
35090
  label: c.label,
34987
35091
  name: c.description,
@@ -34991,11 +35095,55 @@ function mergeDualBurst(local, cloud, opts = {}) {
34991
35095
  provenance: "collective",
34992
35096
  ...c.usageCount != null ? { usageCount: c.usageCount } : {},
34993
35097
  ...c.rel ? { rel: c.rel } : {},
34994
- ...c.community ? { community: c.community } : {}
34995
- });
35098
+ ...c.community ? { community: c.community } : {},
35099
+ ...c.via ? { via: c.via } : {}
35100
+ };
35101
+ nodes.push(n);
35102
+ byId.set(n.id, n);
35103
+ }
35104
+ for (const n of nodes) {
35105
+ if (!n.via) continue;
35106
+ const parent = canonicalId(n.via.parentId);
35107
+ if (parent === n.id) delete n.via;
35108
+ else if (parent !== n.via.parentId) n.via = { ...n.via, parentId: parent };
34996
35109
  }
34997
35110
  nodes.sort((a, b) => b.score - a.score);
34998
35111
  return { nodes, collectiveCount, corroboratedCount };
35112
+ function canonicalId(id) {
35113
+ if (opts.seedId && isSeedTwin(id)) return opts.seedId;
35114
+ return owner.get(id)?.id ?? recordedBy.get(id) ?? twins?.localOf(id)?.id ?? id;
35115
+ }
35116
+ function absorb(n, c) {
35117
+ if (c.id === n.id && cloudIdsOf(n.id).length === 0) corroborate(n, c);
35118
+ else foldSelfUpload(n, c);
35119
+ }
35120
+ function corroborate(n, c) {
35121
+ if (n.provenance !== "corroborated") {
35122
+ corroboratedCount++;
35123
+ n.provenance = "corroborated";
35124
+ n.score = n.score * (1 + boost);
35125
+ delete n.uploaded;
35126
+ }
35127
+ attachCloudFields(n, c);
35128
+ }
35129
+ function foldSelfUpload(n, c) {
35130
+ addTwin(n, c.id);
35131
+ if ((c.usageCount ?? 0) >= 2) return corroborate(n, c);
35132
+ if (n.provenance === "local") n.uploaded = true;
35133
+ attachCloudFields(n, c);
35134
+ }
35135
+ function attachCloudFields(n, c) {
35136
+ if (c.usageCount != null && (n.usageCount ?? -1) < c.usageCount) n.usageCount = c.usageCount;
35137
+ if (c.rel && !n.rel) n.rel = c.rel;
35138
+ if (c.community && !n.community) n.community = c.community;
35139
+ const v = c.via;
35140
+ if (v && !n.via) n.via = v;
35141
+ }
35142
+ }
35143
+ function addTwin(n, id) {
35144
+ if (id === n.id) return;
35145
+ const t = n.twins ?? (n.twins = []);
35146
+ if (!t.includes(id)) t.push(id);
34999
35147
  }
35000
35148
  function packageNeighborhoodToCloudHits(pn) {
35001
35149
  const mk = (rel) => (r) => ({
@@ -35030,13 +35178,15 @@ function searchResultToCloudHits(result) {
35030
35178
  const cloudScore = !Number.isNaN(s) && maxScore > 0 ? s / maxScore : n > 0 ? (n - i2) / n : 0;
35031
35179
  const communityId = node2.attrs?.["community"];
35032
35180
  const communityLabel = node2.attrs?.["communityLabel"];
35181
+ const via = result.via?.[node2.id];
35033
35182
  return {
35034
35183
  id: node2.id,
35035
35184
  label: node2.label,
35036
35185
  description: node2.description,
35037
35186
  cloudScore,
35038
35187
  ...typeof usage === "number" ? { usageCount: usage } : {},
35039
- ...typeof communityId === "number" ? { community: { id: communityId, label: typeof communityLabel === "string" ? communityLabel : null } } : {}
35188
+ ...typeof communityId === "number" ? { community: { id: communityId, label: typeof communityLabel === "string" ? communityLabel : null } } : {},
35189
+ ...via ? { via } : {}
35040
35190
  };
35041
35191
  });
35042
35192
  }
@@ -35048,6 +35198,69 @@ var init_dual_burst = __esm({
35048
35198
  }
35049
35199
  });
35050
35200
 
35201
+ // src/twin-index.ts
35202
+ function recordedCloudIds(n) {
35203
+ if (n.attrs["source"] === "cloud") return [];
35204
+ const ids = [];
35205
+ const add = (v) => {
35206
+ if (typeof v === "string" && v.length > 0 && v !== n.id && !ids.includes(v)) ids.push(v);
35207
+ };
35208
+ add(n.attrs["cloudNodeId"]);
35209
+ const receipts = n.attrs["instanceReceipts"];
35210
+ if (receipts && typeof receipts === "object" && !Array.isArray(receipts)) {
35211
+ for (const r of Object.values(receipts)) {
35212
+ if (r && typeof r === "object") add(r["cloudNodeId"]);
35213
+ }
35214
+ }
35215
+ return ids;
35216
+ }
35217
+ function localTwinOf(store, cloudId) {
35218
+ return localTwinsOf(store, [cloudId]).get(cloudId) ?? null;
35219
+ }
35220
+ function localTwinsOf(store, cloudIds) {
35221
+ const out2 = /* @__PURE__ */ new Map();
35222
+ if (cloudIds.length === 0) return out2;
35223
+ for (const [cloudId, localIds] of store.liveNodeIdsByCloudNodeId(cloudIds)) {
35224
+ for (const id of localIds) {
35225
+ if (id === cloudId) continue;
35226
+ const n = store.getNode(id);
35227
+ if (!n || n.attrs["source"] === "cloud") continue;
35228
+ out2.set(cloudId, n);
35229
+ break;
35230
+ }
35231
+ }
35232
+ const rest2 = cloudIds.filter((id) => !out2.has(id));
35233
+ for (const id of rest2.length ? store.liveNodeIds(rest2) : []) {
35234
+ const n = store.getNode(id);
35235
+ if (n && recordedCloudIds(n).length > 0) out2.set(id, n);
35236
+ }
35237
+ return out2;
35238
+ }
35239
+ function storeTwinIndex(store, cloudIds) {
35240
+ const reverse = localTwinsOf(store, [...new Set(cloudIds)]);
35241
+ const forward = /* @__PURE__ */ new Map();
35242
+ return {
35243
+ cloudIdsOf(localId) {
35244
+ let ids = forward.get(localId);
35245
+ if (!ids) {
35246
+ const n = store.getNode(localId);
35247
+ ids = n ? recordedCloudIds(n) : [];
35248
+ forward.set(localId, ids);
35249
+ }
35250
+ return ids;
35251
+ },
35252
+ localOf(cloudId) {
35253
+ const n = reverse.get(cloudId);
35254
+ return n ? { id: n.id, label: n.label, description: n.description } : void 0;
35255
+ }
35256
+ };
35257
+ }
35258
+ var init_twin_index = __esm({
35259
+ "src/twin-index.ts"() {
35260
+ "use strict";
35261
+ }
35262
+ });
35263
+
35051
35264
  // src/dual-augment.ts
35052
35265
  function isHardBridge(n) {
35053
35266
  return n.label === "Package" || n.label === "Language" || n.label === "Claim" && Number(n.attrs["abstractionLevel"] ?? 0) >= 2;
@@ -35068,6 +35281,8 @@ async function dualAugment(opts) {
35068
35281
  const n = seedNode && nid === seedNode.id ? seedNode : store.getNode(nid);
35069
35282
  return !!n && isHardBridge(n);
35070
35283
  });
35284
+ const seedTwinIds = seedNode ? recordedCloudIds(seedNode) : [];
35285
+ const cloudSeeds = [.../* @__PURE__ */ new Set([...seedTwinIds, ...hardIds])];
35071
35286
  const softText = queryText ?? (seedNode && BRIDGE_LABELS.has(seedNode.label) ? seedNode.description : void 0);
35072
35287
  const softQ = softText ? await generalizeSoft?.(softText) ?? generalizeSymbols(softText, lexiconsFor(store).symbols, 1) : void 0;
35073
35288
  if (seedNode?.label === "Package") {
@@ -35081,9 +35296,9 @@ async function dualAugment(opts) {
35081
35296
  })
35082
35297
  );
35083
35298
  }
35084
- if (hardIds.length > 0 || softQ) {
35299
+ if (cloudSeeds.length > 0 || softQ) {
35085
35300
  calls.push(
35086
- cloud.search({ stack: [], domains: [], kinds: [], seed: hardIds, ...softQ ? { q: softQ } : {}, limit, community: true }).then((res) => {
35301
+ cloud.search({ stack: [], domains: [], kinds: [], seed: cloudSeeds, ...softQ ? { q: softQ } : {}, limit, community: true, via: true }).then((res) => {
35087
35302
  cloudHits.push(...searchResultToCloudHits(res));
35088
35303
  })
35089
35304
  );
@@ -35104,9 +35319,18 @@ async function dualAugment(opts) {
35104
35319
  return { status: "offline" };
35105
35320
  }
35106
35321
  if (cloudHits.length === 0) return { status: "empty" };
35322
+ const deduped = dedupCloudHits(cloudHits);
35107
35323
  return {
35108
35324
  status: "merged",
35109
- merged: mergeDualBurst(localHits, dedupCloudHits(cloudHits)),
35325
+ merged: mergeDualBurst(localHits, deduped, {
35326
+ // A hit's `via` parent is resolved too, so an edge from a folded twin
35327
+ // can be re-pointed at the entity it folded into.
35328
+ twins: storeTwinIndex(store, deduped.flatMap((c) => {
35329
+ const parent = c.via?.parentId;
35330
+ return parent ? [c.id, parent] : [c.id];
35331
+ })),
35332
+ ...seedNode ? { seedId: seedNode.id } : {}
35333
+ }),
35110
35334
  seedProvenance,
35111
35335
  seedUsageCount
35112
35336
  };
@@ -35143,6 +35367,7 @@ async function collectiveDrilldown(cloud, seedId, limit, opts = {}) {
35143
35367
  seed: [seedId],
35144
35368
  limit,
35145
35369
  community: true,
35370
+ via: true,
35146
35371
  ...opts.bridges ? { bridges: true } : {},
35147
35372
  ...opts.scope ? { scope: opts.scope } : {}
35148
35373
  });
@@ -35171,6 +35396,7 @@ async function collectiveDrilldown(cloud, seedId, limit, opts = {}) {
35171
35396
  const score2 = n.attrs["score"];
35172
35397
  const uncertainty = n.attrs["uncertainty"];
35173
35398
  const community = attrsCommunity(n.attrs);
35399
+ const via = res.via?.[n.id];
35174
35400
  return {
35175
35401
  id: n.id,
35176
35402
  label: n.label,
@@ -35179,7 +35405,8 @@ async function collectiveDrilldown(cloud, seedId, limit, opts = {}) {
35179
35405
  ...typeof uncertainty === "number" ? { uncertainty: Number(uncertainty.toFixed(4)) } : {},
35180
35406
  ...community ? { community } : {},
35181
35407
  hops: 1,
35182
- provenance: "collective"
35408
+ provenance: "collective",
35409
+ ...via ? { via } : {}
35183
35410
  };
35184
35411
  }),
35185
35412
  edges: res.edges.map((e) => ({ from: e.from, to: e.to, type: e.type })),
@@ -35258,7 +35485,9 @@ function formatDualNodes(merged) {
35258
35485
  trust: n.why.trust
35259
35486
  }
35260
35487
  } : {},
35261
- ...n.via ? { via: n.via } : {}
35488
+ ...n.via ? { via: n.via } : {},
35489
+ ...n.twins?.length ? { twins: n.twins } : {},
35490
+ ...n.uploaded ? { uploaded: true } : {}
35262
35491
  }));
35263
35492
  }
35264
35493
  var BRIDGE_LABELS, CANONICAL_KNOWLEDGE_ID, UUID_ID;
@@ -35268,6 +35497,7 @@ var init_dual_augment = __esm({
35268
35497
  init_src2();
35269
35498
  init_generalize_graph();
35270
35499
  init_dual_burst();
35500
+ init_twin_index();
35271
35501
  BRIDGE_LABELS = new Set(SEMANTIC_NODE_LABELS);
35272
35502
  CANONICAL_KNOWLEDGE_ID = /^[a-z][a-z0-9]*_[0-9a-f]{8,}$/;
35273
35503
  UUID_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -44560,6 +44790,10 @@ function collectiveSeedMiss(args2, outcome) {
44560
44790
  function resolveSymbol2(store, args2) {
44561
44791
  const id = args2["nodeId"];
44562
44792
  if (typeof id === "string" && store.getNode(id)) return id;
44793
+ if (typeof id === "string" && hasCloudNodeShape(id)) {
44794
+ const twin = localTwinOf(store, id);
44795
+ if (twin) return twin.id;
44796
+ }
44563
44797
  const qname = args2["qname"];
44564
44798
  if (typeof qname === "string") {
44565
44799
  const hits = store.searchNodes({ descriptionLike: qname, limit: 5 });
@@ -44569,6 +44803,14 @@ function resolveSymbol2(store, args2) {
44569
44803
  }
44570
44804
  return null;
44571
44805
  }
44806
+ function seedTwinFields(args2, seed) {
44807
+ const twins = seed ? recordedCloudIds(seed) : [];
44808
+ const asked = args2["nodeId"];
44809
+ return {
44810
+ ...twins.length ? { seedTwins: twins } : {},
44811
+ ...typeof asked === "string" && seed && asked !== seed.id ? { requestedSeed: asked } : {}
44812
+ };
44813
+ }
44572
44814
  function isSynthesizedScope(name2) {
44573
44815
  return name2.includes(":") || name2.startsWith("anon@");
44574
44816
  }
@@ -44750,6 +44992,8 @@ var init_mcp = __esm({
44750
44992
  init_identity2();
44751
44993
  init_registry();
44752
44994
  init_dual_augment();
44995
+ init_dual_burst();
44996
+ init_twin_index();
44753
44997
  init_src5();
44754
44998
  init_src2();
44755
44999
  init_src8();
@@ -44915,11 +45159,10 @@ var init_mcp = __esm({
44915
45159
  node: { id: cn.seed, provenance: "collective" },
44916
45160
  collectiveReachable: true,
44917
45161
  collectiveStatus: collectiveSeedStatus(cn),
44918
- // Seed-touching edge types aren't on the wire (the server's seed
44919
- // burst excludes the seed itself, and edges to nodes outside the
44920
- // result set are dropped), so the collective fallback reports the
44921
- // 1-hop neighborhood + its internal topology instead of the local
44922
- // contract's typed out/in lists.
45162
+ // The collective fallback reports the 1-hop neighborhood + its
45163
+ // internal topology instead of the local contract's typed out/in
45164
+ // lists; the seed-touching edge rides on each node's `via` (the
45165
+ // server's seed burst excludes the seed itself, so `edges` can't).
44923
45166
  nearby: cn.nodes,
44924
45167
  edges: cn.edges,
44925
45168
  ...collectiveCommunityFields(cn)
@@ -45033,6 +45276,7 @@ var init_mcp = __esm({
45033
45276
  if (dual.status === "merged") {
45034
45277
  return {
45035
45278
  seed: id,
45279
+ ...seedTwinFields(args2, seedNode),
45036
45280
  seedProvenance: dual.seedProvenance,
45037
45281
  ...dual.seedUsageCount != null ? { seedUsageCount: dual.seedUsageCount } : {},
45038
45282
  collectiveReachable: true,
@@ -45047,25 +45291,17 @@ var init_mcp = __esm({
45047
45291
  }
45048
45292
  return {
45049
45293
  seed: id,
45294
+ ...seedTwinFields(args2, seedNode),
45050
45295
  // reached-but-empty counts as reachable; offline/no-portal/local-only do not.
45051
45296
  collectiveReachable: collectiveStatus === "empty",
45052
45297
  collectiveStatus,
45053
45298
  ...federationHint(collectiveStatus) ? { collectiveHint: federationHint(collectiveStatus) } : {},
45054
- count: ranked.length,
45055
- results: ranked.map((r) => ({
45056
- id: r.id,
45057
- label: r.node.label,
45058
- name: r.node.description,
45059
- score: Number(r.score.toFixed(4)),
45060
- hops: r.hops,
45061
- provenance: "local",
45062
- why: {
45063
- semantic: Number(r.components.semantic.toFixed(3)),
45064
- centrality: Number(r.components.centrality.toFixed(3)),
45065
- trust: r.components.trust
45066
- },
45067
- ...r.via ? { via: r.via } : {}
45068
- }))
45299
+ // Same twin fold as the merged path: a hydrated in-store copy of a hit
45300
+ // (or of the seed) is not a second entity.
45301
+ ...(() => {
45302
+ const localOnly = mergeDualBurst(localHits, [], { twins: storeTwinIndex(store, []), seedId: id });
45303
+ return { count: localOnly.nodes.length, results: formatDualNodes(localOnly) };
45304
+ })()
45069
45305
  };
45070
45306
  }
45071
45307
  },
@@ -45183,7 +45419,8 @@ var init_mcp = __esm({
45183
45419
  hops: n.hops,
45184
45420
  relevance: n.score,
45185
45421
  provenance: n.provenance,
45186
- ...n.community ? { community: n.community } : {}
45422
+ ...n.community ? { community: n.community } : {},
45423
+ ...n.via ? { via: n.via } : {}
45187
45424
  })),
45188
45425
  edges: cn.edges,
45189
45426
  ...collectiveCommunityFields(cn)
@@ -83179,7 +83416,7 @@ function createWatchBreaker(deps) {
83179
83416
  }
83180
83417
 
83181
83418
  // src/engine.ts
83182
- var DAEMON_VERSION = true ? "2.0.21-dev.3904" : "2.0.0-alpha.0";
83419
+ var DAEMON_VERSION = true ? "2.0.21-dev.3929" : "2.0.0-alpha.0";
83183
83420
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
83184
83421
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
83185
83422
  var GIT_OP_MUTE_MS = 4e3;
@@ -15204,6 +15204,10 @@ CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_js
15204
15204
  CREATE INDEX IF NOT EXISTS nodes_command_signature ON nodes(json_extract(attrs_json, '$.commandSignature')) WHERE valid_to IS NULL;
15205
15205
  CREATE INDEX IF NOT EXISTS nodes_diag_command ON nodes(json_extract(attrs_json, '$.diagCommand')) WHERE valid_to IS NULL;
15206
15206
  CREATE INDEX IF NOT EXISTS nodes_lsp_rel_path ON nodes(json_extract(attrs_json, '$.lspRelPath')) WHERE valid_to IS NULL;
15207
+ -- Local nodes by the cloud twin their upload receipt recorded. Every burst /
15208
+ -- search fusion folds the Network copy of the caller's own knowledge back onto
15209
+ -- the local node; unindexed, the reverse lookup was a ~300ms attrs scan per call.
15210
+ CREATE INDEX IF NOT EXISTS nodes_cloud_node_id ON nodes(json_extract(attrs_json, '$.cloudNodeId')) WHERE valid_to IS NULL;
15207
15211
  -- Nodes still waiting on a root-cause ask. The MCP server checks this on EVERY
15208
15212
  -- tool response (the enrichment piggyback), so it must not parse every
15209
15213
  -- Solution's and Problem's attrs per call. Not partial: the Problem pull reads
@@ -16173,6 +16177,19 @@ var SqliteGraphStore = class {
16173
16177
  ).get(attr, `%${marker}%`, limit);
16174
16178
  return { count: Number(r.count), total: Number(r.total) };
16175
16179
  }
16180
+ liveNodeIdsByCloudNodeId(cloudIds) {
16181
+ const out = /* @__PURE__ */ new Map();
16182
+ const CHUNK = 900;
16183
+ for (let i = 0; i < cloudIds.length; i += CHUNK) {
16184
+ const slice = cloudIds.slice(i, i + CHUNK);
16185
+ if (slice.length === 0) continue;
16186
+ const rows = this.db.prepare(
16187
+ `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`
16188
+ ).all(...slice);
16189
+ for (const r of rows) out.set(r.cloud, [...out.get(r.cloud) ?? [], r.id]);
16190
+ }
16191
+ return out;
16192
+ }
16176
16193
  liveNodeIds(ids) {
16177
16194
  const live = /* @__PURE__ */ new Set();
16178
16195
  const CHUNK = 900;
@@ -16234,7 +16251,10 @@ var SqliteGraphStore = class {
16234
16251
  if (relPaths.length === 0) return [];
16235
16252
  const placeholders = relPaths.map(() => "?").join(",");
16236
16253
  const rows = this.db.prepare(
16237
- `SELECT * FROM nodes WHERE valid_to IS NULL AND json_extract(attrs_json, '$.relPath') IN (${placeholders})`
16254
+ // INDEXED BY: unforced, the planner took nodes_live_page_rank and
16255
+ // json-parsed every live row — 11–25s per reindexed file pair on the
16256
+ // live store vs 7ms here (2026-10-08).
16257
+ `SELECT * FROM nodes INDEXED BY nodes_relpath WHERE valid_to IS NULL AND json_extract(attrs_json, '$.relPath') IN (${placeholders})`
16238
16258
  ).all(...relPaths);
16239
16259
  return rows.map(rowToNode);
16240
16260
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.21-dev.3904",
3
+ "version": "2.0.21-dev.3929",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -26063,6 +26063,10 @@ CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_js
26063
26063
  CREATE INDEX IF NOT EXISTS nodes_command_signature ON nodes(json_extract(attrs_json, '$.commandSignature')) WHERE valid_to IS NULL;
26064
26064
  CREATE INDEX IF NOT EXISTS nodes_diag_command ON nodes(json_extract(attrs_json, '$.diagCommand')) WHERE valid_to IS NULL;
26065
26065
  CREATE INDEX IF NOT EXISTS nodes_lsp_rel_path ON nodes(json_extract(attrs_json, '$.lspRelPath')) WHERE valid_to IS NULL;
26066
+ -- Local nodes by the cloud twin their upload receipt recorded. Every burst /
26067
+ -- search fusion folds the Network copy of the caller's own knowledge back onto
26068
+ -- the local node; unindexed, the reverse lookup was a ~300ms attrs scan per call.
26069
+ CREATE INDEX IF NOT EXISTS nodes_cloud_node_id ON nodes(json_extract(attrs_json, '$.cloudNodeId')) WHERE valid_to IS NULL;
26066
26070
  -- Nodes still waiting on a root-cause ask. The MCP server checks this on EVERY
26067
26071
  -- tool response (the enrichment piggyback), so it must not parse every
26068
26072
  -- Solution's and Problem's attrs per call. Not partial: the Problem pull reads
@@ -27032,6 +27036,19 @@ var SqliteGraphStore = class {
27032
27036
  ).get(attr, `%${marker}%`, limit);
27033
27037
  return { count: Number(r.count), total: Number(r.total) };
27034
27038
  }
27039
+ liveNodeIdsByCloudNodeId(cloudIds) {
27040
+ const out2 = /* @__PURE__ */ new Map();
27041
+ const CHUNK = 900;
27042
+ for (let i2 = 0; i2 < cloudIds.length; i2 += CHUNK) {
27043
+ const slice = cloudIds.slice(i2, i2 + CHUNK);
27044
+ if (slice.length === 0) continue;
27045
+ const rows = this.db.prepare(
27046
+ `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`
27047
+ ).all(...slice);
27048
+ for (const r of rows) out2.set(r.cloud, [...out2.get(r.cloud) ?? [], r.id]);
27049
+ }
27050
+ return out2;
27051
+ }
27035
27052
  liveNodeIds(ids) {
27036
27053
  const live = /* @__PURE__ */ new Set();
27037
27054
  const CHUNK = 900;
@@ -27093,7 +27110,10 @@ var SqliteGraphStore = class {
27093
27110
  if (relPaths.length === 0) return [];
27094
27111
  const placeholders = relPaths.map(() => "?").join(",");
27095
27112
  const rows = this.db.prepare(
27096
- `SELECT * FROM nodes WHERE valid_to IS NULL AND json_extract(attrs_json, '$.relPath') IN (${placeholders})`
27113
+ // INDEXED BY: unforced, the planner took nodes_live_page_rank and
27114
+ // json-parsed every live row — 11–25s per reindexed file pair on the
27115
+ // live store vs 7ms here (2026-10-08).
27116
+ `SELECT * FROM nodes INDEXED BY nodes_relpath WHERE valid_to IS NULL AND json_extract(attrs_json, '$.relPath') IN (${placeholders})`
27097
27117
  ).all(...relPaths);
27098
27118
  return rows.map(rowToNode);
27099
27119
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "errata-context",
3
- "version": "2.0.21-dev.3904",
3
+ "version": "2.0.21-dev.3929",
4
4
  "description": "A minimal 3D view of loaded Errata priors and recent graph bursts.",
5
5
  "author": {
6
6
  "name": "inErrata"
@@ -1,4 +1,4 @@
1
- 80437cb509261f3ab983d93240320f8ef99241c2b0709ccca75d3052035b27a7 .codex-plugin/plugin.json
1
+ cd043345b6db3c0984e689e096b49f0444e7a6ba7b1fc92ffd02db6ac7300e69 .codex-plugin/plugin.json
2
2
  a7bebae9fb01c838d6c75fc784129e28be571233fa66ff5ee8039b574f35ccf7 assets/icon.png
3
3
  fc38b64e4df1e0b72c35764bef6c4c05430d97c9009ad0ecc8a9e75b4deab232 configure-local.mjs
4
4
  73dc6028b3b070257c7c31f252d11e55a9e096f4aed0f187f10cb21716e1e6d7 dist/client.js