@inerrata-corporation/errata 2.0.22-dev.4660 → 2.0.22-dev.4669

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.
@@ -16825,6 +16825,19 @@ import { parentPort } from "node:worker_threads";
16825
16825
  init_src2();
16826
16826
  init_src();
16827
16827
 
16828
+ // ../../packages/local-graph/src/wm-calibration.ts
16829
+ var WM_CALIBRATION_KEYS = [
16830
+ "shownCount",
16831
+ "evictedCount",
16832
+ "reinforceShownCount",
16833
+ "reinforceRecentCount",
16834
+ "reinforceEvictedCount",
16835
+ "reinforceUnshownCount",
16836
+ "reinforceUnshownQueryOnlyCount"
16837
+ ];
16838
+ var WM_COUNTED_PREDICATE = WM_CALIBRATION_KEYS.map((k) => `json_type(attrs_json, '$.${k}') IS NOT NULL`).join(" OR ");
16839
+ var wmCounterSql = (k) => `CASE WHEN json_type(attrs_json, '$.${k}') IN ('integer', 'real') THEN json_extract(attrs_json, '$.${k}') END`;
16840
+
16828
16841
  // ../../packages/local-graph/src/history-cache-floor.ts
16829
16842
  var HISTORY_CACHE_FLOOR_KEY = "history-cache-withdrawal-floor-3";
16830
16843
  function installHistoryCacheFloor(db) {
@@ -17575,7 +17588,13 @@ var DEMAND_HOT = Math.log1p(10);
17575
17588
  var DEMAND_WARM = Math.log1p(5);
17576
17589
 
17577
17590
  // ../../packages/local-graph/src/node-select.ts
17578
- var SELECT_INDEXES = ["nodes_live_local_community", "nodes_live_fold_candidate"];
17591
+ var SELECT_INDEXES = [
17592
+ "nodes_live_local_community",
17593
+ "nodes_live_fold_candidate",
17594
+ "nodes_live_resolved",
17595
+ "nodes_live_fix_candidate",
17596
+ "nodes_live_anchor_hint"
17597
+ ];
17579
17598
  var EMBEDDING_DIM_SQL = "(CASE WHEN length(embedding_blob) >= 4 THEN length(embedding_blob) / 4 ELSE COALESCE(json_array_length(embedding_json), 0) END)";
17580
17599
  var KEY = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/;
17581
17600
  var COLUMNS = /* @__PURE__ */ new Set([
@@ -17602,7 +17621,7 @@ function column(c) {
17602
17621
  function compileNodeWhere(q) {
17603
17622
  if (q.labels.length === 0) throw new Error("selectNodes: no labels");
17604
17623
  const params = [...q.labels];
17605
- const byId = (q.where ?? []).some((w) => "idIn" in w);
17624
+ const byId = (q.where ?? []).some((w) => "idIn" in w || "idPrefix" in w && w.idPrefix.length > 0);
17606
17625
  const unindexed = byId ? "+" : "";
17607
17626
  const conds = [`${unindexed}label IN (${q.labels.map(() => "?").join(", ")})`];
17608
17627
  if (!q.includeClosed) conds.push(`${unindexed}valid_to IS NULL`);
@@ -17612,6 +17631,11 @@ function compileNodeWhere(q) {
17612
17631
  } else if ("idPrefix" in w) {
17613
17632
  conds.push("substr(id, 1, ?) = ?");
17614
17633
  params.push(w.idPrefix.length, w.idPrefix);
17634
+ if (w.idPrefix.length > 0) {
17635
+ const last = w.idPrefix.charCodeAt(w.idPrefix.length - 1);
17636
+ conds.push("id >= ? AND id < ?");
17637
+ params.push(w.idPrefix, w.idPrefix.slice(0, -1) + String.fromCharCode(last + 1));
17638
+ }
17615
17639
  } else if ("idIn" in w) {
17616
17640
  if (w.idIn.length === 0) conds.push("0");
17617
17641
  else {
@@ -17893,7 +17917,16 @@ CREATE INDEX IF NOT EXISTS nodes_live_local_community ON nodes(label, valid_to,
17893
17917
  -- Problems parked for the fold judge (read every settle boundary): ~1 in 5 open
17894
17918
  -- Problems; without it every live Problem was read to find them (56 MB).
17895
17919
  CREATE INDEX IF NOT EXISTS nodes_live_fold_candidate ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_type(attrs_json, '$.foldCandidate') IS NOT NULL;
17920
+ -- The semantic-maintenance sweeps' own filters (closed, asked, hinted): each read
17921
+ -- every Problem row (~30 MB on a 1.1 GB store) for the few hundred that match,
17922
+ -- several times a run, and a run follows most writes (2026-10-20).
17923
+ CREATE INDEX IF NOT EXISTS nodes_live_resolved ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.resolvedAt') IS NOT NULL;
17924
+ CREATE INDEX IF NOT EXISTS nodes_live_fix_candidate ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_type(attrs_json, '$.fixCandidateAt') IS NOT NULL;
17925
+ CREATE INDEX IF NOT EXISTS nodes_live_anchor_hint ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.anchorHint') IS NOT NULL;
17896
17926
  CREATE INDEX IF NOT EXISTS nodes_session_evidence ON nodes(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
17927
+ -- Rows carrying a calibration counter, any version: the logical nodes wm
17928
+ -- calibration can count (~3k), found without reading every citable row.
17929
+ CREATE INDEX IF NOT EXISTS nodes_wm_counted ON nodes(logical_id, version, valid_to, label, ${WM_CALIBRATION_KEYS.map(wmCounterSql).join(", ")}) WHERE ${WM_COUNTED_PREDICATE};
17897
17930
  CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
17898
17931
  -- Covering + partial index for labelFingerprint over the instance labels: the
17899
17932
  -- live-push probe runs it per workspace on a timer, and each hit used to read
@@ -18112,6 +18145,12 @@ var SqliteGraphStore = class {
18112
18145
  `),
18113
18146
  getNode: this.db.prepare("SELECT * FROM nodes WHERE id = ?"),
18114
18147
  labelOf: this.db.prepare("SELECT label FROM nodes WHERE id = ?"),
18148
+ nodeContent: this.db.prepare(
18149
+ "SELECT label, description, extraction_confidence, extraction_source, stability, valid_to, attrs_json FROM nodes WHERE id = ?"
18150
+ ),
18151
+ edgeContent: this.db.prepare(
18152
+ "SELECT confidence, valid_to, attrs_json FROM edges WHERE from_id = ? AND type = ? AND to_id = ?"
18153
+ ),
18115
18154
  // Single-column fast paths for the nightly pass — writing 100k pageRank
18116
18155
  // values through the 18-column COALESCE updateNode took ~30s; these are ~10x
18117
18156
  // cheaper (one bound column, no COALESCE evaluation per row).
@@ -18323,6 +18362,27 @@ var SqliteGraphStore = class {
18323
18362
  ORDER BY label, created_at, rowid`
18324
18363
  ).all(label);
18325
18364
  }
18365
+ wmCalibrationRows(labels) {
18366
+ const counted = this.db.prepare(
18367
+ `SELECT rowid AS rid, logical_id, version, valid_to, label, ${WM_CALIBRATION_KEYS.map((k, i) => `${wmCounterSql(k)} AS c${i}`).join(", ")}
18368
+ FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}`
18369
+ ).all();
18370
+ const others = this.db.prepare(
18371
+ `SELECT rowid AS rid, logical_id, version, valid_to, label FROM nodes WHERE rowid IN (
18372
+ SELECT n.rowid FROM nodes AS n INDEXED BY nodes_logical
18373
+ WHERE n.logical_id IN (SELECT logical_id FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE})
18374
+ EXCEPT SELECT rowid FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE})`
18375
+ ).all();
18376
+ const order = new Map(labels.map((l, i) => [l, i]));
18377
+ return [...counted, ...others].filter((r) => order.has(r.label)).sort((a, b) => order.get(a.label) - order.get(b.label) || a.rid - b.rid).map((r) => {
18378
+ const attrs = {};
18379
+ WM_CALIBRATION_KEYS.forEach((k, i) => {
18380
+ const v = r[`c${i}`];
18381
+ if (typeof v === "number") attrs[k] = v;
18382
+ });
18383
+ return { logicalId: r.logical_id, version: Number(r.version), validTo: r.valid_to ?? null, attrs };
18384
+ });
18385
+ }
18326
18386
  liveCommentsAttachedTo(name) {
18327
18387
  return this.db.prepare(
18328
18388
  `SELECT json_extract(attrs_json, '$.kind') AS kind, json_extract(attrs_json, '$.text') AS text, description FROM nodes INDEXED BY nodes_live_attached_to
@@ -18462,6 +18522,19 @@ var SqliteGraphStore = class {
18462
18522
  attrs_json: JSON.stringify(node.attrs ?? {})
18463
18523
  });
18464
18524
  }
18525
+ /** mergeNode, unless the live row already holds this content. Content is what
18526
+ * the columns a merge rewrites from the producer (label, description, attrs,
18527
+ * confidence, stability);
18528
+ * rank, community, landmark, surprise, hits, tier and the vector belong to
18529
+ * the passes that compute them, and a skipped write leaves them as they are
18530
+ * where a merge would have reset them to the producer's defaults. Returns
18531
+ * whether it wrote. */
18532
+ mergeNodeIfChanged(node) {
18533
+ const row = this.stmts.nodeContent.get(node.id);
18534
+ if (row && row.valid_to === null && row.label === node.label && row.description === node.description && row.extraction_confidence === node.extractionConfidence && row.stability === node.stability && row.attrs_json === JSON.stringify(node.attrs ?? {})) return false;
18535
+ this.mergeNode(node);
18536
+ return true;
18537
+ }
18465
18538
  updateNode(id, patch) {
18466
18539
  const hasEmbedding = patch.embedding !== void 0 && patch.embedding.length > 0;
18467
18540
  this.stmts.updateNode.run({
@@ -18567,6 +18640,21 @@ var SqliteGraphStore = class {
18567
18640
  attrs_json: JSON.stringify(edge.attrs ?? {})
18568
18641
  });
18569
18642
  }
18643
+ /** mergeEdge, unless the live edge already holds this content: same attrs, and
18644
+ * a confidence the upsert's MAX would keep. Its navigation posteriors and
18645
+ * last-seen stamp stay as they are where a merge would have reset them. The
18646
+ * ontology gate runs first either way, so refusals are recorded as before.
18647
+ * Returns whether it wrote (or refused). */
18648
+ mergeEdgeIfChanged(edge) {
18649
+ if (!this.edgeRuleViolation(edge)) {
18650
+ const row = this.stmts.edgeContent.get(edge.from, edge.type, edge.to);
18651
+ if (row && row.valid_to === null && row.confidence >= edge.confidence && row.attrs_json === JSON.stringify(edge.attrs ?? {})) {
18652
+ return false;
18653
+ }
18654
+ }
18655
+ this.mergeEdge(edge);
18656
+ return true;
18657
+ }
18570
18658
  /** EDGE_RULES check against the stored endpoints' labels, with the LOCAL
18571
18659
  * overlay consulted first. Returns the reason string on a documented-
18572
18660
  * forbidden combination, else null. Point lookups on the id PK — negligible
package/errata.mjs CHANGED
@@ -18851,6 +18851,87 @@ var init_src2 = __esm({
18851
18851
  }
18852
18852
  });
18853
18853
 
18854
+ // ../../packages/local-graph/src/wm-calibration.ts
18855
+ function wmCalibrationCells(store) {
18856
+ const cells = {
18857
+ calibrationSchema: WM_CALIBRATION_SCHEMA,
18858
+ shownNodes: 0,
18859
+ shownImpressions: 0,
18860
+ evictedNodes: 0,
18861
+ evictedImpressions: 0,
18862
+ reinforceShown: 0,
18863
+ reinforceRecent: 0,
18864
+ reinforceEvicted: 0,
18865
+ reinforceUnshown: 0,
18866
+ reinforceUnshownQueryOnly: 0,
18867
+ reinforcedNodes: 0,
18868
+ reinforcedShownNodes: 0
18869
+ };
18870
+ const newest = /* @__PURE__ */ new Map();
18871
+ const rows = store.wmCalibrationRows?.(WM_CALIBRATION_LABELS) ?? (function* () {
18872
+ for (const label of WM_CALIBRATION_LABELS) {
18873
+ yield* store.selectNodes({ labels: [label], attrs: WM_CALIBRATION_KEYS, includeClosed: true });
18874
+ }
18875
+ })();
18876
+ for (const n of rows) {
18877
+ const live3 = n.validTo === null;
18878
+ const prev = newest.get(n.logicalId);
18879
+ if (prev && (prev.live || !live3 && prev.version >= n.version)) continue;
18880
+ newest.set(n.logicalId, { version: n.version, live: live3, attrs: n.attrs });
18881
+ }
18882
+ for (const { attrs } of newest.values()) {
18883
+ const shown = num(attrs["shownCount"]);
18884
+ const evicted = num(attrs["evictedCount"]);
18885
+ if (shown > 0) {
18886
+ cells.shownNodes++;
18887
+ cells.shownImpressions += shown;
18888
+ }
18889
+ if (evicted > 0) {
18890
+ cells.evictedNodes++;
18891
+ cells.evictedImpressions += evicted;
18892
+ }
18893
+ const rs = num(attrs["reinforceShownCount"]);
18894
+ const rr = num(attrs["reinforceRecentCount"]);
18895
+ const re = num(attrs["reinforceEvictedCount"]);
18896
+ const ru = num(attrs["reinforceUnshownCount"]);
18897
+ cells.reinforceShown += rs;
18898
+ cells.reinforceRecent += rr;
18899
+ cells.reinforceEvicted += re;
18900
+ cells.reinforceUnshown += ru;
18901
+ cells.reinforceUnshownQueryOnly += num(attrs["reinforceUnshownQueryOnlyCount"]);
18902
+ if (rs + rr + re + ru > 0) cells.reinforcedNodes++;
18903
+ if (rs > 0) cells.reinforcedShownNodes++;
18904
+ }
18905
+ return cells;
18906
+ }
18907
+ var WM_CALIBRATION_LABELS, WM_CALIBRATION_SCHEMA, num, WM_CALIBRATION_KEYS, WM_COUNTED_PREDICATE, wmCounterSql;
18908
+ var init_wm_calibration = __esm({
18909
+ "../../packages/local-graph/src/wm-calibration.ts"() {
18910
+ "use strict";
18911
+ WM_CALIBRATION_LABELS = [
18912
+ "Problem",
18913
+ "Solution",
18914
+ "RootCause",
18915
+ "Pattern",
18916
+ "Technique",
18917
+ "AntiPattern"
18918
+ ];
18919
+ WM_CALIBRATION_SCHEMA = 2;
18920
+ num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
18921
+ WM_CALIBRATION_KEYS = [
18922
+ "shownCount",
18923
+ "evictedCount",
18924
+ "reinforceShownCount",
18925
+ "reinforceRecentCount",
18926
+ "reinforceEvictedCount",
18927
+ "reinforceUnshownCount",
18928
+ "reinforceUnshownQueryOnlyCount"
18929
+ ];
18930
+ WM_COUNTED_PREDICATE = WM_CALIBRATION_KEYS.map((k) => `json_type(attrs_json, '$.${k}') IS NOT NULL`).join(" OR ");
18931
+ wmCounterSql = (k) => `CASE WHEN json_type(attrs_json, '$.${k}') IN ('integer', 'real') THEN json_extract(attrs_json, '$.${k}') END`;
18932
+ }
18933
+ });
18934
+
18854
18935
  // ../../packages/local-graph/src/history-cache-floor.ts
18855
18936
  function installHistoryCacheFloor(db) {
18856
18937
  const key = HISTORY_CACHE_FLOOR_KEY;
@@ -20398,7 +20479,7 @@ function column(c) {
20398
20479
  function compileNodeWhere(q) {
20399
20480
  if (q.labels.length === 0) throw new Error("selectNodes: no labels");
20400
20481
  const params = [...q.labels];
20401
- const byId = (q.where ?? []).some((w) => "idIn" in w);
20482
+ const byId = (q.where ?? []).some((w) => "idIn" in w || "idPrefix" in w && w.idPrefix.length > 0);
20402
20483
  const unindexed = byId ? "+" : "";
20403
20484
  const conds = [`${unindexed}label IN (${q.labels.map(() => "?").join(", ")})`];
20404
20485
  if (!q.includeClosed) conds.push(`${unindexed}valid_to IS NULL`);
@@ -20408,6 +20489,11 @@ function compileNodeWhere(q) {
20408
20489
  } else if ("idPrefix" in w) {
20409
20490
  conds.push("substr(id, 1, ?) = ?");
20410
20491
  params.push(w.idPrefix.length, w.idPrefix);
20492
+ if (w.idPrefix.length > 0) {
20493
+ const last = w.idPrefix.charCodeAt(w.idPrefix.length - 1);
20494
+ conds.push("id >= ? AND id < ?");
20495
+ params.push(w.idPrefix, w.idPrefix.slice(0, -1) + String.fromCharCode(last + 1));
20496
+ }
20411
20497
  } else if ("idIn" in w) {
20412
20498
  if (w.idIn.length === 0) conds.push("0");
20413
20499
  else {
@@ -20530,7 +20616,13 @@ var SELECT_INDEXES, EMBEDDING_DIM_SQL, KEY, COLUMNS;
20530
20616
  var init_node_select = __esm({
20531
20617
  "../../packages/local-graph/src/node-select.ts"() {
20532
20618
  "use strict";
20533
- SELECT_INDEXES = ["nodes_live_local_community", "nodes_live_fold_candidate"];
20619
+ SELECT_INDEXES = [
20620
+ "nodes_live_local_community",
20621
+ "nodes_live_fold_candidate",
20622
+ "nodes_live_resolved",
20623
+ "nodes_live_fix_candidate",
20624
+ "nodes_live_anchor_hint"
20625
+ ];
20534
20626
  EMBEDDING_DIM_SQL = "(CASE WHEN length(embedding_blob) >= 4 THEN length(embedding_blob) / 4 ELSE COALESCE(json_array_length(embedding_json), 0) END)";
20535
20627
  KEY = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/;
20536
20628
  COLUMNS = /* @__PURE__ */ new Set([
@@ -20649,6 +20741,7 @@ var init_store = __esm({
20649
20741
  "use strict";
20650
20742
  init_src2();
20651
20743
  init_src();
20744
+ init_wm_calibration();
20652
20745
  init_history_cache_floor();
20653
20746
  init_session_evidence();
20654
20747
  init_edit_placeholder_retirement();
@@ -20801,7 +20894,16 @@ CREATE INDEX IF NOT EXISTS nodes_live_local_community ON nodes(label, valid_to,
20801
20894
  -- Problems parked for the fold judge (read every settle boundary): ~1 in 5 open
20802
20895
  -- Problems; without it every live Problem was read to find them (56 MB).
20803
20896
  CREATE INDEX IF NOT EXISTS nodes_live_fold_candidate ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_type(attrs_json, '$.foldCandidate') IS NOT NULL;
20897
+ -- The semantic-maintenance sweeps' own filters (closed, asked, hinted): each read
20898
+ -- every Problem row (~30 MB on a 1.1 GB store) for the few hundred that match,
20899
+ -- several times a run, and a run follows most writes (2026-10-20).
20900
+ CREATE INDEX IF NOT EXISTS nodes_live_resolved ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.resolvedAt') IS NOT NULL;
20901
+ CREATE INDEX IF NOT EXISTS nodes_live_fix_candidate ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_type(attrs_json, '$.fixCandidateAt') IS NOT NULL;
20902
+ CREATE INDEX IF NOT EXISTS nodes_live_anchor_hint ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.anchorHint') IS NOT NULL;
20804
20903
  CREATE INDEX IF NOT EXISTS nodes_session_evidence ON nodes(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
20904
+ -- Rows carrying a calibration counter, any version: the logical nodes wm
20905
+ -- calibration can count (~3k), found without reading every citable row.
20906
+ CREATE INDEX IF NOT EXISTS nodes_wm_counted ON nodes(logical_id, version, valid_to, label, ${WM_CALIBRATION_KEYS.map(wmCounterSql).join(", ")}) WHERE ${WM_COUNTED_PREDICATE};
20805
20907
  CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
20806
20908
  -- Covering + partial index for labelFingerprint over the instance labels: the
20807
20909
  -- live-push probe runs it per workspace on a timer, and each hit used to read
@@ -20963,6 +21065,12 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
20963
21065
  `),
20964
21066
  getNode: this.db.prepare("SELECT * FROM nodes WHERE id = ?"),
20965
21067
  labelOf: this.db.prepare("SELECT label FROM nodes WHERE id = ?"),
21068
+ nodeContent: this.db.prepare(
21069
+ "SELECT label, description, extraction_confidence, extraction_source, stability, valid_to, attrs_json FROM nodes WHERE id = ?"
21070
+ ),
21071
+ edgeContent: this.db.prepare(
21072
+ "SELECT confidence, valid_to, attrs_json FROM edges WHERE from_id = ? AND type = ? AND to_id = ?"
21073
+ ),
20966
21074
  // Single-column fast paths for the nightly pass — writing 100k pageRank
20967
21075
  // values through the 18-column COALESCE updateNode took ~30s; these are ~10x
20968
21076
  // cheaper (one bound column, no COALESCE evaluation per row).
@@ -21174,6 +21282,27 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21174
21282
  ORDER BY label, created_at, rowid`
21175
21283
  ).all(label);
21176
21284
  }
21285
+ wmCalibrationRows(labels) {
21286
+ const counted = this.db.prepare(
21287
+ `SELECT rowid AS rid, logical_id, version, valid_to, label, ${WM_CALIBRATION_KEYS.map((k, i2) => `${wmCounterSql(k)} AS c${i2}`).join(", ")}
21288
+ FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}`
21289
+ ).all();
21290
+ const others = this.db.prepare(
21291
+ `SELECT rowid AS rid, logical_id, version, valid_to, label FROM nodes WHERE rowid IN (
21292
+ SELECT n.rowid FROM nodes AS n INDEXED BY nodes_logical
21293
+ WHERE n.logical_id IN (SELECT logical_id FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE})
21294
+ EXCEPT SELECT rowid FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE})`
21295
+ ).all();
21296
+ const order = new Map(labels.map((l, i2) => [l, i2]));
21297
+ return [...counted, ...others].filter((r) => order.has(r.label)).sort((a, b) => order.get(a.label) - order.get(b.label) || a.rid - b.rid).map((r) => {
21298
+ const attrs = {};
21299
+ WM_CALIBRATION_KEYS.forEach((k, i2) => {
21300
+ const v = r[`c${i2}`];
21301
+ if (typeof v === "number") attrs[k] = v;
21302
+ });
21303
+ return { logicalId: r.logical_id, version: Number(r.version), validTo: r.valid_to ?? null, attrs };
21304
+ });
21305
+ }
21177
21306
  liveCommentsAttachedTo(name2) {
21178
21307
  return this.db.prepare(
21179
21308
  `SELECT json_extract(attrs_json, '$.kind') AS kind, json_extract(attrs_json, '$.text') AS text, description FROM nodes INDEXED BY nodes_live_attached_to
@@ -21313,6 +21442,19 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21313
21442
  attrs_json: JSON.stringify(node2.attrs ?? {})
21314
21443
  });
21315
21444
  }
21445
+ /** mergeNode, unless the live row already holds this content. Content is what
21446
+ * the columns a merge rewrites from the producer (label, description, attrs,
21447
+ * confidence, stability);
21448
+ * rank, community, landmark, surprise, hits, tier and the vector belong to
21449
+ * the passes that compute them, and a skipped write leaves them as they are
21450
+ * where a merge would have reset them to the producer's defaults. Returns
21451
+ * whether it wrote. */
21452
+ mergeNodeIfChanged(node2) {
21453
+ const row = this.stmts.nodeContent.get(node2.id);
21454
+ if (row && row.valid_to === null && row.label === node2.label && row.description === node2.description && row.extraction_confidence === node2.extractionConfidence && row.stability === node2.stability && row.attrs_json === JSON.stringify(node2.attrs ?? {})) return false;
21455
+ this.mergeNode(node2);
21456
+ return true;
21457
+ }
21316
21458
  updateNode(id, patch) {
21317
21459
  const hasEmbedding = patch.embedding !== void 0 && patch.embedding.length > 0;
21318
21460
  this.stmts.updateNode.run({
@@ -21418,6 +21560,21 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21418
21560
  attrs_json: JSON.stringify(edge2.attrs ?? {})
21419
21561
  });
21420
21562
  }
21563
+ /** mergeEdge, unless the live edge already holds this content: same attrs, and
21564
+ * a confidence the upsert's MAX would keep. Its navigation posteriors and
21565
+ * last-seen stamp stay as they are where a merge would have reset them. The
21566
+ * ontology gate runs first either way, so refusals are recorded as before.
21567
+ * Returns whether it wrote (or refused). */
21568
+ mergeEdgeIfChanged(edge2) {
21569
+ if (!this.edgeRuleViolation(edge2)) {
21570
+ const row = this.stmts.edgeContent.get(edge2.from, edge2.type, edge2.to);
21571
+ if (row && row.valid_to === null && row.confidence >= edge2.confidence && row.attrs_json === JSON.stringify(edge2.attrs ?? {})) {
21572
+ return false;
21573
+ }
21574
+ }
21575
+ this.mergeEdge(edge2);
21576
+ return true;
21577
+ }
21421
21578
  /** EDGE_RULES check against the stored endpoints' labels, with the LOCAL
21422
21579
  * overlay consulted first. Returns the reason string on a documented-
21423
21580
  * forbidden combination, else null. Point lookups on the id PK — negligible
@@ -24534,7 +24691,8 @@ function upgradeAnchorHints(store, workspaceId2, ts) {
24534
24691
  const hinted = store.selectNodes({
24535
24692
  labels: ["Problem"],
24536
24693
  attrs: ["resolvedAt", "anchorHint"],
24537
- where: [{ attr: "anchorHint", notNullish: true }]
24694
+ where: [{ attr: "anchorHint", notNullish: true }],
24695
+ indexedBy: "nodes_live_anchor_hint"
24538
24696
  });
24539
24697
  for (const row of hinted) {
24540
24698
  if (row.attrs["resolvedAt"]) continue;
@@ -24617,7 +24775,8 @@ function reopenAutoClosedProblems(store, t) {
24617
24775
  const closed = store.selectNodes({
24618
24776
  labels: ["Problem"],
24619
24777
  attrs: ["resolvedAt", "resolvedAs", "mergedInto", "resolutionWitnessed", "retiredEditPlaceholder", "resolutionSuspect"],
24620
- where: [{ attr: "resolvedAt", notNullish: true }, { attr: "resolvedAs", nullish: true }, { attr: "mergedInto", absent: true }]
24778
+ where: [{ attr: "resolvedAt", notNullish: true }, { attr: "resolvedAs", nullish: true }, { attr: "mergedInto", absent: true }],
24779
+ indexedBy: "nodes_live_resolved"
24621
24780
  });
24622
24781
  for (const p of closed) {
24623
24782
  if (p.attrs["resolvedAt"] == null) continue;
@@ -24735,7 +24894,7 @@ function markFixCandidates(store, t) {
24735
24894
  }
24736
24895
  function clearSettledFixCandidates(store, t) {
24737
24896
  const report = { answered: 0, ignored: 0, unsubstantiated: 0, standing: 0 };
24738
- const asked = store.selectNodes({ labels: ["Problem"], where: [{ attr: "fixCandidateAt", present: true }] });
24897
+ const asked = store.selectNodes({ labels: ["Problem"], where: [{ attr: "fixCandidateAt", present: true }], indexedBy: "nodes_live_fix_candidate" });
24739
24898
  for (const row of asked) {
24740
24899
  const p = store.getNode(row.id);
24741
24900
  if (!p) continue;
@@ -27419,82 +27578,6 @@ var init_mechanism_liveness = __esm({
27419
27578
  }
27420
27579
  });
27421
27580
 
27422
- // ../../packages/local-graph/src/wm-calibration.ts
27423
- function wmCalibrationCells(store) {
27424
- const cells = {
27425
- calibrationSchema: WM_CALIBRATION_SCHEMA,
27426
- shownNodes: 0,
27427
- shownImpressions: 0,
27428
- evictedNodes: 0,
27429
- evictedImpressions: 0,
27430
- reinforceShown: 0,
27431
- reinforceRecent: 0,
27432
- reinforceEvicted: 0,
27433
- reinforceUnshown: 0,
27434
- reinforceUnshownQueryOnly: 0,
27435
- reinforcedNodes: 0,
27436
- reinforcedShownNodes: 0
27437
- };
27438
- const newest = /* @__PURE__ */ new Map();
27439
- for (const label of WM_CALIBRATION_LABELS) {
27440
- for (const n of store.selectNodes({ labels: [label], attrs: WM_CALIBRATION_KEYS, includeClosed: true })) {
27441
- const live3 = n.validTo === null;
27442
- const prev = newest.get(n.logicalId);
27443
- if (prev && (prev.live || !live3 && prev.version >= n.version)) continue;
27444
- newest.set(n.logicalId, { version: n.version, live: live3, attrs: n.attrs });
27445
- }
27446
- }
27447
- for (const { attrs } of newest.values()) {
27448
- const shown = num(attrs["shownCount"]);
27449
- const evicted = num(attrs["evictedCount"]);
27450
- if (shown > 0) {
27451
- cells.shownNodes++;
27452
- cells.shownImpressions += shown;
27453
- }
27454
- if (evicted > 0) {
27455
- cells.evictedNodes++;
27456
- cells.evictedImpressions += evicted;
27457
- }
27458
- const rs = num(attrs["reinforceShownCount"]);
27459
- const rr = num(attrs["reinforceRecentCount"]);
27460
- const re = num(attrs["reinforceEvictedCount"]);
27461
- const ru = num(attrs["reinforceUnshownCount"]);
27462
- cells.reinforceShown += rs;
27463
- cells.reinforceRecent += rr;
27464
- cells.reinforceEvicted += re;
27465
- cells.reinforceUnshown += ru;
27466
- cells.reinforceUnshownQueryOnly += num(attrs["reinforceUnshownQueryOnlyCount"]);
27467
- if (rs + rr + re + ru > 0) cells.reinforcedNodes++;
27468
- if (rs > 0) cells.reinforcedShownNodes++;
27469
- }
27470
- return cells;
27471
- }
27472
- var WM_CALIBRATION_LABELS, WM_CALIBRATION_SCHEMA, num, WM_CALIBRATION_KEYS;
27473
- var init_wm_calibration = __esm({
27474
- "../../packages/local-graph/src/wm-calibration.ts"() {
27475
- "use strict";
27476
- WM_CALIBRATION_LABELS = [
27477
- "Problem",
27478
- "Solution",
27479
- "RootCause",
27480
- "Pattern",
27481
- "Technique",
27482
- "AntiPattern"
27483
- ];
27484
- WM_CALIBRATION_SCHEMA = 2;
27485
- num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
27486
- WM_CALIBRATION_KEYS = [
27487
- "shownCount",
27488
- "evictedCount",
27489
- "reinforceShownCount",
27490
- "reinforceRecentCount",
27491
- "reinforceEvictedCount",
27492
- "reinforceUnshownCount",
27493
- "reinforceUnshownQueryOnlyCount"
27494
- ];
27495
- }
27496
- });
27497
-
27498
27581
  // ../../packages/local-graph/src/index.ts
27499
27582
  var src_exports2 = {};
27500
27583
  __export(src_exports2, {
@@ -27541,8 +27624,10 @@ __export(src_exports2, {
27541
27624
  STALL_MIN_RUNS: () => STALL_MIN_RUNS,
27542
27625
  STALL_MIN_SPAN_MS: () => STALL_MIN_SPAN_MS,
27543
27626
  SqliteGraphStore: () => SqliteGraphStore,
27627
+ WM_CALIBRATION_KEYS: () => WM_CALIBRATION_KEYS,
27544
27628
  WM_CALIBRATION_LABELS: () => WM_CALIBRATION_LABELS,
27545
27629
  WM_CALIBRATION_SCHEMA: () => WM_CALIBRATION_SCHEMA,
27630
+ WM_COUNTED_PREDICATE: () => WM_COUNTED_PREDICATE,
27546
27631
  WORKSPACE_SCAN_CAP: () => WORKSPACE_SCAN_CAP,
27547
27632
  addDependency: () => addDependency,
27548
27633
  aggregateClaimConfidence: () => aggregateClaimConfidence,
@@ -27689,7 +27774,8 @@ __export(src_exports2, {
27689
27774
  upgradeAnchorHints: () => upgradeAnchorHints,
27690
27775
  upgradeAnchorHintsForFile: () => upgradeAnchorHintsForFile,
27691
27776
  walk: () => walk,
27692
- wmCalibrationCells: () => wmCalibrationCells
27777
+ wmCalibrationCells: () => wmCalibrationCells,
27778
+ wmCounterSql: () => wmCounterSql
27693
27779
  });
27694
27780
  var init_src5 = __esm({
27695
27781
  "../../packages/local-graph/src/index.ts"() {
@@ -38975,7 +39061,28 @@ async function incrementalReindex(store, rootPath, workspaceId2, changedAbsPaths
38975
39061
  store.closeNode(nid, t);
38976
39062
  }
38977
39063
  });
38978
- const r = await runIndexer(store, {
39064
+ const seenNodes = /* @__PURE__ */ new Set();
39065
+ const seenEdges = /* @__PURE__ */ new Set();
39066
+ const writeStore = store.mergeNodeIfChanged && store.mergeEdgeIfChanged ? new Proxy(store, {
39067
+ get(target, prop) {
39068
+ if (prop === "mergeNode") {
39069
+ return (n) => {
39070
+ seenNodes.add(n.id);
39071
+ if (n.label === "File" || n.label === "Folder") target.mergeNode(n);
39072
+ else target.mergeNodeIfChanged(n);
39073
+ };
39074
+ }
39075
+ if (prop === "mergeEdge") {
39076
+ return (e) => {
39077
+ seenEdges.add(edgeId(e.from, e.type, e.to));
39078
+ target.mergeEdgeIfChanged(e);
39079
+ };
39080
+ }
39081
+ const v = Reflect.get(target, prop, target);
39082
+ return typeof v === "function" ? v.bind(target) : v;
39083
+ }
39084
+ }) : store;
39085
+ const r = await runIndexer(writeStore, {
38979
39086
  workspaceId: workspaceId2,
38980
39087
  rootPath,
38981
39088
  providers,
@@ -38993,14 +39100,14 @@ async function incrementalReindex(store, rootPath, workspaceId2, changedAbsPaths
38993
39100
  if (typeof rel !== "string" || !changedRelPaths.has(rel)) continue;
38994
39101
  if (versionedAndFile.has(n.label)) {
38995
39102
  ownedLive.push(n.id);
38996
- } else if (n.label === "Comment" && n.lastUpdatedAt < t) {
39103
+ } else if (n.label === "Comment" && n.lastUpdatedAt < t && !seenNodes.has(n.id)) {
38997
39104
  store.closeNode(n.id, t);
38998
39105
  }
38999
39106
  }
39000
39107
  for (const id of ownedLive) {
39001
39108
  for (const e of store.outEdges(id)) {
39002
39109
  if (e.type === "SUPERSEDED_BY") continue;
39003
- if (e.lastSeenAt < t) {
39110
+ if (e.lastSeenAt < t && !seenEdges.has(edgeId(e.from, e.type, e.to))) {
39004
39111
  store.closeEdge(e.id, t);
39005
39112
  delta.edgesRemoved++;
39006
39113
  } else if (e.createdAt >= t) {
@@ -87030,7 +87137,7 @@ function createWatchBreaker(deps) {
87030
87137
  }
87031
87138
 
87032
87139
  // src/engine.ts
87033
- var DAEMON_VERSION = true ? "2.0.22-dev.4660" : "2.0.0-alpha.0";
87140
+ var DAEMON_VERSION = true ? "2.0.22-dev.4669" : "2.0.0-alpha.0";
87034
87141
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
87035
87142
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
87036
87143
  var GIT_OP_MUTE_MS = 4e3;
@@ -15402,6 +15402,19 @@ var LEADING_VERB = new RegExp(String.raw`^\s*(${VERB_WORDS_SOURCE})\s*:`, "i");
15402
15402
  var ANY_VERB_WORD = new RegExp(String.raw`\b(?:${VERB_WORDS_SOURCE})\s*:`, "gi");
15403
15403
  var QUOTED_TAG_OPENER = new RegExp(String.raw`\(\s*(?:(${VERB_WORDS_SOURCE})\s*:|(?=\[))`, "gi");
15404
15404
 
15405
+ // ../../packages/local-graph/src/wm-calibration.ts
15406
+ var WM_CALIBRATION_KEYS = [
15407
+ "shownCount",
15408
+ "evictedCount",
15409
+ "reinforceShownCount",
15410
+ "reinforceRecentCount",
15411
+ "reinforceEvictedCount",
15412
+ "reinforceUnshownCount",
15413
+ "reinforceUnshownQueryOnlyCount"
15414
+ ];
15415
+ var WM_COUNTED_PREDICATE = WM_CALIBRATION_KEYS.map((k) => `json_type(attrs_json, '$.${k}') IS NOT NULL`).join(" OR ");
15416
+ var wmCounterSql = (k) => `CASE WHEN json_type(attrs_json, '$.${k}') IN ('integer', 'real') THEN json_extract(attrs_json, '$.${k}') END`;
15417
+
15405
15418
  // ../../packages/local-graph/src/history-cache-floor.ts
15406
15419
  var HISTORY_CACHE_FLOOR_KEY = "history-cache-withdrawal-floor-3";
15407
15420
  function installHistoryCacheFloor(db) {
@@ -15972,7 +15985,13 @@ var DEMAND_HOT = Math.log1p(10);
15972
15985
  var DEMAND_WARM = Math.log1p(5);
15973
15986
 
15974
15987
  // ../../packages/local-graph/src/node-select.ts
15975
- var SELECT_INDEXES = ["nodes_live_local_community", "nodes_live_fold_candidate"];
15988
+ var SELECT_INDEXES = [
15989
+ "nodes_live_local_community",
15990
+ "nodes_live_fold_candidate",
15991
+ "nodes_live_resolved",
15992
+ "nodes_live_fix_candidate",
15993
+ "nodes_live_anchor_hint"
15994
+ ];
15976
15995
  var EMBEDDING_DIM_SQL = "(CASE WHEN length(embedding_blob) >= 4 THEN length(embedding_blob) / 4 ELSE COALESCE(json_array_length(embedding_json), 0) END)";
15977
15996
  var KEY = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/;
15978
15997
  var COLUMNS = /* @__PURE__ */ new Set([
@@ -15999,7 +16018,7 @@ function column(c) {
15999
16018
  function compileNodeWhere(q) {
16000
16019
  if (q.labels.length === 0) throw new Error("selectNodes: no labels");
16001
16020
  const params = [...q.labels];
16002
- const byId = (q.where ?? []).some((w) => "idIn" in w);
16021
+ const byId = (q.where ?? []).some((w) => "idIn" in w || "idPrefix" in w && w.idPrefix.length > 0);
16003
16022
  const unindexed = byId ? "+" : "";
16004
16023
  const conds = [`${unindexed}label IN (${q.labels.map(() => "?").join(", ")})`];
16005
16024
  if (!q.includeClosed) conds.push(`${unindexed}valid_to IS NULL`);
@@ -16009,6 +16028,11 @@ function compileNodeWhere(q) {
16009
16028
  } else if ("idPrefix" in w) {
16010
16029
  conds.push("substr(id, 1, ?) = ?");
16011
16030
  params.push(w.idPrefix.length, w.idPrefix);
16031
+ if (w.idPrefix.length > 0) {
16032
+ const last = w.idPrefix.charCodeAt(w.idPrefix.length - 1);
16033
+ conds.push("id >= ? AND id < ?");
16034
+ params.push(w.idPrefix, w.idPrefix.slice(0, -1) + String.fromCharCode(last + 1));
16035
+ }
16012
16036
  } else if ("idIn" in w) {
16013
16037
  if (w.idIn.length === 0) conds.push("0");
16014
16038
  else {
@@ -16290,7 +16314,16 @@ CREATE INDEX IF NOT EXISTS nodes_live_local_community ON nodes(label, valid_to,
16290
16314
  -- Problems parked for the fold judge (read every settle boundary): ~1 in 5 open
16291
16315
  -- Problems; without it every live Problem was read to find them (56 MB).
16292
16316
  CREATE INDEX IF NOT EXISTS nodes_live_fold_candidate ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_type(attrs_json, '$.foldCandidate') IS NOT NULL;
16317
+ -- The semantic-maintenance sweeps' own filters (closed, asked, hinted): each read
16318
+ -- every Problem row (~30 MB on a 1.1 GB store) for the few hundred that match,
16319
+ -- several times a run, and a run follows most writes (2026-10-20).
16320
+ CREATE INDEX IF NOT EXISTS nodes_live_resolved ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.resolvedAt') IS NOT NULL;
16321
+ CREATE INDEX IF NOT EXISTS nodes_live_fix_candidate ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_type(attrs_json, '$.fixCandidateAt') IS NOT NULL;
16322
+ CREATE INDEX IF NOT EXISTS nodes_live_anchor_hint ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.anchorHint') IS NOT NULL;
16293
16323
  CREATE INDEX IF NOT EXISTS nodes_session_evidence ON nodes(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
16324
+ -- Rows carrying a calibration counter, any version: the logical nodes wm
16325
+ -- calibration can count (~3k), found without reading every citable row.
16326
+ CREATE INDEX IF NOT EXISTS nodes_wm_counted ON nodes(logical_id, version, valid_to, label, ${WM_CALIBRATION_KEYS.map(wmCounterSql).join(", ")}) WHERE ${WM_COUNTED_PREDICATE};
16294
16327
  CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
16295
16328
  -- Covering + partial index for labelFingerprint over the instance labels: the
16296
16329
  -- live-push probe runs it per workspace on a timer, and each hit used to read
@@ -16509,6 +16542,12 @@ var SqliteGraphStore = class {
16509
16542
  `),
16510
16543
  getNode: this.db.prepare("SELECT * FROM nodes WHERE id = ?"),
16511
16544
  labelOf: this.db.prepare("SELECT label FROM nodes WHERE id = ?"),
16545
+ nodeContent: this.db.prepare(
16546
+ "SELECT label, description, extraction_confidence, extraction_source, stability, valid_to, attrs_json FROM nodes WHERE id = ?"
16547
+ ),
16548
+ edgeContent: this.db.prepare(
16549
+ "SELECT confidence, valid_to, attrs_json FROM edges WHERE from_id = ? AND type = ? AND to_id = ?"
16550
+ ),
16512
16551
  // Single-column fast paths for the nightly pass — writing 100k pageRank
16513
16552
  // values through the 18-column COALESCE updateNode took ~30s; these are ~10x
16514
16553
  // cheaper (one bound column, no COALESCE evaluation per row).
@@ -16720,6 +16759,27 @@ var SqliteGraphStore = class {
16720
16759
  ORDER BY label, created_at, rowid`
16721
16760
  ).all(label);
16722
16761
  }
16762
+ wmCalibrationRows(labels) {
16763
+ const counted = this.db.prepare(
16764
+ `SELECT rowid AS rid, logical_id, version, valid_to, label, ${WM_CALIBRATION_KEYS.map((k, i) => `${wmCounterSql(k)} AS c${i}`).join(", ")}
16765
+ FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}`
16766
+ ).all();
16767
+ const others = this.db.prepare(
16768
+ `SELECT rowid AS rid, logical_id, version, valid_to, label FROM nodes WHERE rowid IN (
16769
+ SELECT n.rowid FROM nodes AS n INDEXED BY nodes_logical
16770
+ WHERE n.logical_id IN (SELECT logical_id FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE})
16771
+ EXCEPT SELECT rowid FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE})`
16772
+ ).all();
16773
+ const order = new Map(labels.map((l, i) => [l, i]));
16774
+ return [...counted, ...others].filter((r) => order.has(r.label)).sort((a, b) => order.get(a.label) - order.get(b.label) || a.rid - b.rid).map((r) => {
16775
+ const attrs = {};
16776
+ WM_CALIBRATION_KEYS.forEach((k, i) => {
16777
+ const v = r[`c${i}`];
16778
+ if (typeof v === "number") attrs[k] = v;
16779
+ });
16780
+ return { logicalId: r.logical_id, version: Number(r.version), validTo: r.valid_to ?? null, attrs };
16781
+ });
16782
+ }
16723
16783
  liveCommentsAttachedTo(name) {
16724
16784
  return this.db.prepare(
16725
16785
  `SELECT json_extract(attrs_json, '$.kind') AS kind, json_extract(attrs_json, '$.text') AS text, description FROM nodes INDEXED BY nodes_live_attached_to
@@ -16859,6 +16919,19 @@ var SqliteGraphStore = class {
16859
16919
  attrs_json: JSON.stringify(node.attrs ?? {})
16860
16920
  });
16861
16921
  }
16922
+ /** mergeNode, unless the live row already holds this content. Content is what
16923
+ * the columns a merge rewrites from the producer (label, description, attrs,
16924
+ * confidence, stability);
16925
+ * rank, community, landmark, surprise, hits, tier and the vector belong to
16926
+ * the passes that compute them, and a skipped write leaves them as they are
16927
+ * where a merge would have reset them to the producer's defaults. Returns
16928
+ * whether it wrote. */
16929
+ mergeNodeIfChanged(node) {
16930
+ const row = this.stmts.nodeContent.get(node.id);
16931
+ if (row && row.valid_to === null && row.label === node.label && row.description === node.description && row.extraction_confidence === node.extractionConfidence && row.stability === node.stability && row.attrs_json === JSON.stringify(node.attrs ?? {})) return false;
16932
+ this.mergeNode(node);
16933
+ return true;
16934
+ }
16862
16935
  updateNode(id, patch) {
16863
16936
  const hasEmbedding = patch.embedding !== void 0 && patch.embedding.length > 0;
16864
16937
  this.stmts.updateNode.run({
@@ -16964,6 +17037,21 @@ var SqliteGraphStore = class {
16964
17037
  attrs_json: JSON.stringify(edge.attrs ?? {})
16965
17038
  });
16966
17039
  }
17040
+ /** mergeEdge, unless the live edge already holds this content: same attrs, and
17041
+ * a confidence the upsert's MAX would keep. Its navigation posteriors and
17042
+ * last-seen stamp stay as they are where a merge would have reset them. The
17043
+ * ontology gate runs first either way, so refusals are recorded as before.
17044
+ * Returns whether it wrote (or refused). */
17045
+ mergeEdgeIfChanged(edge) {
17046
+ if (!this.edgeRuleViolation(edge)) {
17047
+ const row = this.stmts.edgeContent.get(edge.from, edge.type, edge.to);
17048
+ if (row && row.valid_to === null && row.confidence >= edge.confidence && row.attrs_json === JSON.stringify(edge.attrs ?? {})) {
17049
+ return false;
17050
+ }
17051
+ }
17052
+ this.mergeEdge(edge);
17053
+ return true;
17054
+ }
16967
17055
  /** EDGE_RULES check against the stored endpoints' labels, with the LOCAL
16968
17056
  * overlay consulted first. Returns the reason string on a documented-
16969
17057
  * forbidden combination, else null. Point lookups on the id PK — negligible
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.22-dev.4660",
3
+ "version": "2.0.22-dev.4669",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -17629,7 +17629,28 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
17629
17629
  store2.closeNode(nid, t);
17630
17630
  }
17631
17631
  });
17632
- const r = await runIndexer(store2, {
17632
+ const seenNodes = /* @__PURE__ */ new Set();
17633
+ const seenEdges = /* @__PURE__ */ new Set();
17634
+ const writeStore = store2.mergeNodeIfChanged && store2.mergeEdgeIfChanged ? new Proxy(store2, {
17635
+ get(target, prop) {
17636
+ if (prop === "mergeNode") {
17637
+ return (n) => {
17638
+ seenNodes.add(n.id);
17639
+ if (n.label === "File" || n.label === "Folder") target.mergeNode(n);
17640
+ else target.mergeNodeIfChanged(n);
17641
+ };
17642
+ }
17643
+ if (prop === "mergeEdge") {
17644
+ return (e) => {
17645
+ seenEdges.add(edgeId(e.from, e.type, e.to));
17646
+ target.mergeEdgeIfChanged(e);
17647
+ };
17648
+ }
17649
+ const v = Reflect.get(target, prop, target);
17650
+ return typeof v === "function" ? v.bind(target) : v;
17651
+ }
17652
+ }) : store2;
17653
+ const r = await runIndexer(writeStore, {
17633
17654
  workspaceId,
17634
17655
  rootPath,
17635
17656
  providers,
@@ -17647,14 +17668,14 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
17647
17668
  if (typeof rel !== "string" || !changedRelPaths.has(rel)) continue;
17648
17669
  if (versionedAndFile.has(n.label)) {
17649
17670
  ownedLive.push(n.id);
17650
- } else if (n.label === "Comment" && n.lastUpdatedAt < t) {
17671
+ } else if (n.label === "Comment" && n.lastUpdatedAt < t && !seenNodes.has(n.id)) {
17651
17672
  store2.closeNode(n.id, t);
17652
17673
  }
17653
17674
  }
17654
17675
  for (const id of ownedLive) {
17655
17676
  for (const e of store2.outEdges(id)) {
17656
17677
  if (e.type === "SUPERSEDED_BY") continue;
17657
- if (e.lastSeenAt < t) {
17678
+ if (e.lastSeenAt < t && !seenEdges.has(edgeId(e.from, e.type, e.to))) {
17658
17679
  store2.closeEdge(e.id, t);
17659
17680
  delta.edgesRemoved++;
17660
17681
  } else if (e.createdAt >= t) {
@@ -25842,6 +25863,81 @@ import { parentPort, workerData } from "node:worker_threads";
25842
25863
  init_src2();
25843
25864
  init_src();
25844
25865
 
25866
+ // ../../packages/local-graph/src/wm-calibration.ts
25867
+ var WM_CALIBRATION_LABELS = [
25868
+ "Problem",
25869
+ "Solution",
25870
+ "RootCause",
25871
+ "Pattern",
25872
+ "Technique",
25873
+ "AntiPattern"
25874
+ ];
25875
+ var WM_CALIBRATION_SCHEMA = 2;
25876
+ var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
25877
+ var WM_CALIBRATION_KEYS = [
25878
+ "shownCount",
25879
+ "evictedCount",
25880
+ "reinforceShownCount",
25881
+ "reinforceRecentCount",
25882
+ "reinforceEvictedCount",
25883
+ "reinforceUnshownCount",
25884
+ "reinforceUnshownQueryOnlyCount"
25885
+ ];
25886
+ var WM_COUNTED_PREDICATE = WM_CALIBRATION_KEYS.map((k) => `json_type(attrs_json, '$.${k}') IS NOT NULL`).join(" OR ");
25887
+ var wmCounterSql = (k) => `CASE WHEN json_type(attrs_json, '$.${k}') IN ('integer', 'real') THEN json_extract(attrs_json, '$.${k}') END`;
25888
+ function wmCalibrationCells(store2) {
25889
+ const cells = {
25890
+ calibrationSchema: WM_CALIBRATION_SCHEMA,
25891
+ shownNodes: 0,
25892
+ shownImpressions: 0,
25893
+ evictedNodes: 0,
25894
+ evictedImpressions: 0,
25895
+ reinforceShown: 0,
25896
+ reinforceRecent: 0,
25897
+ reinforceEvicted: 0,
25898
+ reinforceUnshown: 0,
25899
+ reinforceUnshownQueryOnly: 0,
25900
+ reinforcedNodes: 0,
25901
+ reinforcedShownNodes: 0
25902
+ };
25903
+ const newest = /* @__PURE__ */ new Map();
25904
+ const rows = store2.wmCalibrationRows?.(WM_CALIBRATION_LABELS) ?? (function* () {
25905
+ for (const label of WM_CALIBRATION_LABELS) {
25906
+ yield* store2.selectNodes({ labels: [label], attrs: WM_CALIBRATION_KEYS, includeClosed: true });
25907
+ }
25908
+ })();
25909
+ for (const n of rows) {
25910
+ const live = n.validTo === null;
25911
+ const prev = newest.get(n.logicalId);
25912
+ if (prev && (prev.live || !live && prev.version >= n.version)) continue;
25913
+ newest.set(n.logicalId, { version: n.version, live, attrs: n.attrs });
25914
+ }
25915
+ for (const { attrs } of newest.values()) {
25916
+ const shown = num(attrs["shownCount"]);
25917
+ const evicted = num(attrs["evictedCount"]);
25918
+ if (shown > 0) {
25919
+ cells.shownNodes++;
25920
+ cells.shownImpressions += shown;
25921
+ }
25922
+ if (evicted > 0) {
25923
+ cells.evictedNodes++;
25924
+ cells.evictedImpressions += evicted;
25925
+ }
25926
+ const rs = num(attrs["reinforceShownCount"]);
25927
+ const rr = num(attrs["reinforceRecentCount"]);
25928
+ const re = num(attrs["reinforceEvictedCount"]);
25929
+ const ru = num(attrs["reinforceUnshownCount"]);
25930
+ cells.reinforceShown += rs;
25931
+ cells.reinforceRecent += rr;
25932
+ cells.reinforceEvicted += re;
25933
+ cells.reinforceUnshown += ru;
25934
+ cells.reinforceUnshownQueryOnly += num(attrs["reinforceUnshownQueryOnlyCount"]);
25935
+ if (rs + rr + re + ru > 0) cells.reinforcedNodes++;
25936
+ if (rs > 0) cells.reinforcedShownNodes++;
25937
+ }
25938
+ return cells;
25939
+ }
25940
+
25845
25941
  // ../../packages/local-graph/src/history-cache-floor.ts
25846
25942
  var HISTORY_CACHE_FLOOR_KEY = "history-cache-withdrawal-floor-3";
25847
25943
  function installHistoryCacheFloor(db) {
@@ -26851,7 +26947,13 @@ function pagerankLocal(input) {
26851
26947
  }
26852
26948
 
26853
26949
  // ../../packages/local-graph/src/node-select.ts
26854
- var SELECT_INDEXES = ["nodes_live_local_community", "nodes_live_fold_candidate"];
26950
+ var SELECT_INDEXES = [
26951
+ "nodes_live_local_community",
26952
+ "nodes_live_fold_candidate",
26953
+ "nodes_live_resolved",
26954
+ "nodes_live_fix_candidate",
26955
+ "nodes_live_anchor_hint"
26956
+ ];
26855
26957
  var EMBEDDING_DIM_SQL = "(CASE WHEN length(embedding_blob) >= 4 THEN length(embedding_blob) / 4 ELSE COALESCE(json_array_length(embedding_json), 0) END)";
26856
26958
  var KEY = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/;
26857
26959
  var COLUMNS = /* @__PURE__ */ new Set([
@@ -26878,7 +26980,7 @@ function column(c) {
26878
26980
  function compileNodeWhere(q) {
26879
26981
  if (q.labels.length === 0) throw new Error("selectNodes: no labels");
26880
26982
  const params = [...q.labels];
26881
- const byId = (q.where ?? []).some((w) => "idIn" in w);
26983
+ const byId = (q.where ?? []).some((w) => "idIn" in w || "idPrefix" in w && w.idPrefix.length > 0);
26882
26984
  const unindexed = byId ? "+" : "";
26883
26985
  const conds = [`${unindexed}label IN (${q.labels.map(() => "?").join(", ")})`];
26884
26986
  if (!q.includeClosed) conds.push(`${unindexed}valid_to IS NULL`);
@@ -26888,6 +26990,11 @@ function compileNodeWhere(q) {
26888
26990
  } else if ("idPrefix" in w) {
26889
26991
  conds.push("substr(id, 1, ?) = ?");
26890
26992
  params.push(w.idPrefix.length, w.idPrefix);
26993
+ if (w.idPrefix.length > 0) {
26994
+ const last = w.idPrefix.charCodeAt(w.idPrefix.length - 1);
26995
+ conds.push("id >= ? AND id < ?");
26996
+ params.push(w.idPrefix, w.idPrefix.slice(0, -1) + String.fromCharCode(last + 1));
26997
+ }
26891
26998
  } else if ("idIn" in w) {
26892
26999
  if (w.idIn.length === 0) conds.push("0");
26893
27000
  else {
@@ -27169,7 +27276,16 @@ CREATE INDEX IF NOT EXISTS nodes_live_local_community ON nodes(label, valid_to,
27169
27276
  -- Problems parked for the fold judge (read every settle boundary): ~1 in 5 open
27170
27277
  -- Problems; without it every live Problem was read to find them (56 MB).
27171
27278
  CREATE INDEX IF NOT EXISTS nodes_live_fold_candidate ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_type(attrs_json, '$.foldCandidate') IS NOT NULL;
27279
+ -- The semantic-maintenance sweeps' own filters (closed, asked, hinted): each read
27280
+ -- every Problem row (~30 MB on a 1.1 GB store) for the few hundred that match,
27281
+ -- several times a run, and a run follows most writes (2026-10-20).
27282
+ CREATE INDEX IF NOT EXISTS nodes_live_resolved ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.resolvedAt') IS NOT NULL;
27283
+ CREATE INDEX IF NOT EXISTS nodes_live_fix_candidate ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_type(attrs_json, '$.fixCandidateAt') IS NOT NULL;
27284
+ CREATE INDEX IF NOT EXISTS nodes_live_anchor_hint ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.anchorHint') IS NOT NULL;
27172
27285
  CREATE INDEX IF NOT EXISTS nodes_session_evidence ON nodes(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
27286
+ -- Rows carrying a calibration counter, any version: the logical nodes wm
27287
+ -- calibration can count (~3k), found without reading every citable row.
27288
+ CREATE INDEX IF NOT EXISTS nodes_wm_counted ON nodes(logical_id, version, valid_to, label, ${WM_CALIBRATION_KEYS.map(wmCounterSql).join(", ")}) WHERE ${WM_COUNTED_PREDICATE};
27173
27289
  CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
27174
27290
  -- Covering + partial index for labelFingerprint over the instance labels: the
27175
27291
  -- live-push probe runs it per workspace on a timer, and each hit used to read
@@ -27388,6 +27504,12 @@ var SqliteGraphStore = class {
27388
27504
  `),
27389
27505
  getNode: this.db.prepare("SELECT * FROM nodes WHERE id = ?"),
27390
27506
  labelOf: this.db.prepare("SELECT label FROM nodes WHERE id = ?"),
27507
+ nodeContent: this.db.prepare(
27508
+ "SELECT label, description, extraction_confidence, extraction_source, stability, valid_to, attrs_json FROM nodes WHERE id = ?"
27509
+ ),
27510
+ edgeContent: this.db.prepare(
27511
+ "SELECT confidence, valid_to, attrs_json FROM edges WHERE from_id = ? AND type = ? AND to_id = ?"
27512
+ ),
27391
27513
  // Single-column fast paths for the nightly pass — writing 100k pageRank
27392
27514
  // values through the 18-column COALESCE updateNode took ~30s; these are ~10x
27393
27515
  // cheaper (one bound column, no COALESCE evaluation per row).
@@ -27599,6 +27721,27 @@ var SqliteGraphStore = class {
27599
27721
  ORDER BY label, created_at, rowid`
27600
27722
  ).all(label);
27601
27723
  }
27724
+ wmCalibrationRows(labels) {
27725
+ const counted = this.db.prepare(
27726
+ `SELECT rowid AS rid, logical_id, version, valid_to, label, ${WM_CALIBRATION_KEYS.map((k, i2) => `${wmCounterSql(k)} AS c${i2}`).join(", ")}
27727
+ FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}`
27728
+ ).all();
27729
+ const others = this.db.prepare(
27730
+ `SELECT rowid AS rid, logical_id, version, valid_to, label FROM nodes WHERE rowid IN (
27731
+ SELECT n.rowid FROM nodes AS n INDEXED BY nodes_logical
27732
+ WHERE n.logical_id IN (SELECT logical_id FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE})
27733
+ EXCEPT SELECT rowid FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE})`
27734
+ ).all();
27735
+ const order = new Map(labels.map((l, i2) => [l, i2]));
27736
+ return [...counted, ...others].filter((r) => order.has(r.label)).sort((a, b) => order.get(a.label) - order.get(b.label) || a.rid - b.rid).map((r) => {
27737
+ const attrs = {};
27738
+ WM_CALIBRATION_KEYS.forEach((k, i2) => {
27739
+ const v = r[`c${i2}`];
27740
+ if (typeof v === "number") attrs[k] = v;
27741
+ });
27742
+ return { logicalId: r.logical_id, version: Number(r.version), validTo: r.valid_to ?? null, attrs };
27743
+ });
27744
+ }
27602
27745
  liveCommentsAttachedTo(name2) {
27603
27746
  return this.db.prepare(
27604
27747
  `SELECT json_extract(attrs_json, '$.kind') AS kind, json_extract(attrs_json, '$.text') AS text, description FROM nodes INDEXED BY nodes_live_attached_to
@@ -27738,6 +27881,19 @@ var SqliteGraphStore = class {
27738
27881
  attrs_json: JSON.stringify(node.attrs ?? {})
27739
27882
  });
27740
27883
  }
27884
+ /** mergeNode, unless the live row already holds this content. Content is what
27885
+ * the columns a merge rewrites from the producer (label, description, attrs,
27886
+ * confidence, stability);
27887
+ * rank, community, landmark, surprise, hits, tier and the vector belong to
27888
+ * the passes that compute them, and a skipped write leaves them as they are
27889
+ * where a merge would have reset them to the producer's defaults. Returns
27890
+ * whether it wrote. */
27891
+ mergeNodeIfChanged(node) {
27892
+ const row = this.stmts.nodeContent.get(node.id);
27893
+ if (row && row.valid_to === null && row.label === node.label && row.description === node.description && row.extraction_confidence === node.extractionConfidence && row.stability === node.stability && row.attrs_json === JSON.stringify(node.attrs ?? {})) return false;
27894
+ this.mergeNode(node);
27895
+ return true;
27896
+ }
27741
27897
  updateNode(id, patch) {
27742
27898
  const hasEmbedding = patch.embedding !== void 0 && patch.embedding.length > 0;
27743
27899
  this.stmts.updateNode.run({
@@ -27843,6 +27999,21 @@ var SqliteGraphStore = class {
27843
27999
  attrs_json: JSON.stringify(edge.attrs ?? {})
27844
28000
  });
27845
28001
  }
28002
+ /** mergeEdge, unless the live edge already holds this content: same attrs, and
28003
+ * a confidence the upsert's MAX would keep. Its navigation posteriors and
28004
+ * last-seen stamp stay as they are where a merge would have reset them. The
28005
+ * ontology gate runs first either way, so refusals are recorded as before.
28006
+ * Returns whether it wrote (or refused). */
28007
+ mergeEdgeIfChanged(edge) {
28008
+ if (!this.edgeRuleViolation(edge)) {
28009
+ const row = this.stmts.edgeContent.get(edge.from, edge.type, edge.to);
28010
+ if (row && row.valid_to === null && row.confidence >= edge.confidence && row.attrs_json === JSON.stringify(edge.attrs ?? {})) {
28011
+ return false;
28012
+ }
28013
+ }
28014
+ this.mergeEdge(edge);
28015
+ return true;
28016
+ }
27846
28017
  /** EDGE_RULES check against the stored endpoints' labels, with the LOCAL
27847
28018
  * overlay consulted first. Returns the reason string on a documented-
27848
28019
  * forbidden combination, else null. Point lookups on the id PK — negligible
@@ -29387,7 +29558,8 @@ function upgradeAnchorHints(store2, workspaceId, ts) {
29387
29558
  const hinted = store2.selectNodes({
29388
29559
  labels: ["Problem"],
29389
29560
  attrs: ["resolvedAt", "anchorHint"],
29390
- where: [{ attr: "anchorHint", notNullish: true }]
29561
+ where: [{ attr: "anchorHint", notNullish: true }],
29562
+ indexedBy: "nodes_live_anchor_hint"
29391
29563
  });
29392
29564
  for (const row of hinted) {
29393
29565
  if (row.attrs["resolvedAt"]) continue;
@@ -29418,7 +29590,8 @@ function reopenAutoClosedProblems(store2, t) {
29418
29590
  const closed = store2.selectNodes({
29419
29591
  labels: ["Problem"],
29420
29592
  attrs: ["resolvedAt", "resolvedAs", "mergedInto", "resolutionWitnessed", "retiredEditPlaceholder", "resolutionSuspect"],
29421
- where: [{ attr: "resolvedAt", notNullish: true }, { attr: "resolvedAs", nullish: true }, { attr: "mergedInto", absent: true }]
29593
+ where: [{ attr: "resolvedAt", notNullish: true }, { attr: "resolvedAs", nullish: true }, { attr: "mergedInto", absent: true }],
29594
+ indexedBy: "nodes_live_resolved"
29422
29595
  });
29423
29596
  for (const p of closed) {
29424
29597
  if (p.attrs["resolvedAt"] == null) continue;
@@ -29507,7 +29680,7 @@ function markFixCandidates(store2, t) {
29507
29680
  var FIX_CANDIDATE_ASK_LIMIT = 5;
29508
29681
  function clearSettledFixCandidates(store2, t) {
29509
29682
  const report = { answered: 0, ignored: 0, unsubstantiated: 0, standing: 0 };
29510
- const asked = store2.selectNodes({ labels: ["Problem"], where: [{ attr: "fixCandidateAt", present: true }] });
29683
+ const asked = store2.selectNodes({ labels: ["Problem"], where: [{ attr: "fixCandidateAt", present: true }], indexedBy: "nodes_live_fix_candidate" });
29511
29684
  for (const row of asked) {
29512
29685
  const p = store2.getNode(row.id);
29513
29686
  if (!p) continue;
@@ -30396,76 +30569,6 @@ function minFractionOf(d) {
30396
30569
  return d.fedIncoming?.minFraction ?? d.fedEdges?.minFraction ?? d.fed?.minFraction ?? 0;
30397
30570
  }
30398
30571
 
30399
- // ../../packages/local-graph/src/wm-calibration.ts
30400
- var WM_CALIBRATION_LABELS = [
30401
- "Problem",
30402
- "Solution",
30403
- "RootCause",
30404
- "Pattern",
30405
- "Technique",
30406
- "AntiPattern"
30407
- ];
30408
- var WM_CALIBRATION_SCHEMA = 2;
30409
- var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
30410
- var WM_CALIBRATION_KEYS = [
30411
- "shownCount",
30412
- "evictedCount",
30413
- "reinforceShownCount",
30414
- "reinforceRecentCount",
30415
- "reinforceEvictedCount",
30416
- "reinforceUnshownCount",
30417
- "reinforceUnshownQueryOnlyCount"
30418
- ];
30419
- function wmCalibrationCells(store2) {
30420
- const cells = {
30421
- calibrationSchema: WM_CALIBRATION_SCHEMA,
30422
- shownNodes: 0,
30423
- shownImpressions: 0,
30424
- evictedNodes: 0,
30425
- evictedImpressions: 0,
30426
- reinforceShown: 0,
30427
- reinforceRecent: 0,
30428
- reinforceEvicted: 0,
30429
- reinforceUnshown: 0,
30430
- reinforceUnshownQueryOnly: 0,
30431
- reinforcedNodes: 0,
30432
- reinforcedShownNodes: 0
30433
- };
30434
- const newest = /* @__PURE__ */ new Map();
30435
- for (const label of WM_CALIBRATION_LABELS) {
30436
- for (const n of store2.selectNodes({ labels: [label], attrs: WM_CALIBRATION_KEYS, includeClosed: true })) {
30437
- const live = n.validTo === null;
30438
- const prev = newest.get(n.logicalId);
30439
- if (prev && (prev.live || !live && prev.version >= n.version)) continue;
30440
- newest.set(n.logicalId, { version: n.version, live, attrs: n.attrs });
30441
- }
30442
- }
30443
- for (const { attrs } of newest.values()) {
30444
- const shown = num(attrs["shownCount"]);
30445
- const evicted = num(attrs["evictedCount"]);
30446
- if (shown > 0) {
30447
- cells.shownNodes++;
30448
- cells.shownImpressions += shown;
30449
- }
30450
- if (evicted > 0) {
30451
- cells.evictedNodes++;
30452
- cells.evictedImpressions += evicted;
30453
- }
30454
- const rs = num(attrs["reinforceShownCount"]);
30455
- const rr = num(attrs["reinforceRecentCount"]);
30456
- const re = num(attrs["reinforceEvictedCount"]);
30457
- const ru = num(attrs["reinforceUnshownCount"]);
30458
- cells.reinforceShown += rs;
30459
- cells.reinforceRecent += rr;
30460
- cells.reinforceEvicted += re;
30461
- cells.reinforceUnshown += ru;
30462
- cells.reinforceUnshownQueryOnly += num(attrs["reinforceUnshownQueryOnlyCount"]);
30463
- if (rs + rr + re + ru > 0) cells.reinforcedNodes++;
30464
- if (rs > 0) cells.reinforcedShownNodes++;
30465
- }
30466
- return cells;
30467
- }
30468
-
30469
30572
  // ../../packages/generalizer/src/generalizer.ts
30470
30573
  init_src2();
30471
30574
  init_src();
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "errata-context",
3
- "version": "2.0.22-dev.4660",
3
+ "version": "2.0.22-dev.4669",
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
- 3bcb422706463f9d29ee3751dd1036046ce56211386ec8d38b1cbe53b26ac809 .codex-plugin/plugin.json
1
+ 235e69b11fb75b5854bbe0e3e025fdad29c29d8b5b34159bd8270bfb8b235b0b .codex-plugin/plugin.json
2
2
  a7bebae9fb01c838d6c75fc784129e28be571233fa66ff5ee8039b574f35ccf7 assets/icon.png
3
3
  fc38b64e4df1e0b72c35764bef6c4c05430d97c9009ad0ecc8a9e75b4deab232 configure-local.mjs
4
4
  73dc6028b3b070257c7c31f252d11e55a9e096f4aed0f187f10cb21716e1e6d7 dist/client.js