@inerrata-corporation/errata 2.0.22-dev.4665 → 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) {
@@ -17911,6 +17924,9 @@ CREATE INDEX IF NOT EXISTS nodes_live_resolved ON nodes(label, valid_to) WHERE v
17911
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;
17912
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;
17913
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};
17914
17930
  CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
17915
17931
  -- Covering + partial index for labelFingerprint over the instance labels: the
17916
17932
  -- live-push probe runs it per workspace on a timer, and each hit used to read
@@ -18129,6 +18145,12 @@ var SqliteGraphStore = class {
18129
18145
  `),
18130
18146
  getNode: this.db.prepare("SELECT * FROM nodes WHERE id = ?"),
18131
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
+ ),
18132
18154
  // Single-column fast paths for the nightly pass — writing 100k pageRank
18133
18155
  // values through the 18-column COALESCE updateNode took ~30s; these are ~10x
18134
18156
  // cheaper (one bound column, no COALESCE evaluation per row).
@@ -18340,6 +18362,27 @@ var SqliteGraphStore = class {
18340
18362
  ORDER BY label, created_at, rowid`
18341
18363
  ).all(label);
18342
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
+ }
18343
18386
  liveCommentsAttachedTo(name) {
18344
18387
  return this.db.prepare(
18345
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
@@ -18479,6 +18522,19 @@ var SqliteGraphStore = class {
18479
18522
  attrs_json: JSON.stringify(node.attrs ?? {})
18480
18523
  });
18481
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
+ }
18482
18538
  updateNode(id, patch) {
18483
18539
  const hasEmbedding = patch.embedding !== void 0 && patch.embedding.length > 0;
18484
18540
  this.stmts.updateNode.run({
@@ -18584,6 +18640,21 @@ var SqliteGraphStore = class {
18584
18640
  attrs_json: JSON.stringify(edge.attrs ?? {})
18585
18641
  });
18586
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
+ }
18587
18658
  /** EDGE_RULES check against the stored endpoints' labels, with the LOCAL
18588
18659
  * overlay consulted first. Returns the reason string on a documented-
18589
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;
@@ -20660,6 +20741,7 @@ var init_store = __esm({
20660
20741
  "use strict";
20661
20742
  init_src2();
20662
20743
  init_src();
20744
+ init_wm_calibration();
20663
20745
  init_history_cache_floor();
20664
20746
  init_session_evidence();
20665
20747
  init_edit_placeholder_retirement();
@@ -20819,6 +20901,9 @@ CREATE INDEX IF NOT EXISTS nodes_live_resolved ON nodes(label, valid_to) WHERE v
20819
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;
20820
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;
20821
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};
20822
20907
  CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
20823
20908
  -- Covering + partial index for labelFingerprint over the instance labels: the
20824
20909
  -- live-push probe runs it per workspace on a timer, and each hit used to read
@@ -20980,6 +21065,12 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
20980
21065
  `),
20981
21066
  getNode: this.db.prepare("SELECT * FROM nodes WHERE id = ?"),
20982
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
+ ),
20983
21074
  // Single-column fast paths for the nightly pass — writing 100k pageRank
20984
21075
  // values through the 18-column COALESCE updateNode took ~30s; these are ~10x
20985
21076
  // cheaper (one bound column, no COALESCE evaluation per row).
@@ -21191,6 +21282,27 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21191
21282
  ORDER BY label, created_at, rowid`
21192
21283
  ).all(label);
21193
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
+ }
21194
21306
  liveCommentsAttachedTo(name2) {
21195
21307
  return this.db.prepare(
21196
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
@@ -21330,6 +21442,19 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21330
21442
  attrs_json: JSON.stringify(node2.attrs ?? {})
21331
21443
  });
21332
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
+ }
21333
21458
  updateNode(id, patch) {
21334
21459
  const hasEmbedding = patch.embedding !== void 0 && patch.embedding.length > 0;
21335
21460
  this.stmts.updateNode.run({
@@ -21435,6 +21560,21 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21435
21560
  attrs_json: JSON.stringify(edge2.attrs ?? {})
21436
21561
  });
21437
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
+ }
21438
21578
  /** EDGE_RULES check against the stored endpoints' labels, with the LOCAL
21439
21579
  * overlay consulted first. Returns the reason string on a documented-
21440
21580
  * forbidden combination, else null. Point lookups on the id PK — negligible
@@ -27438,82 +27578,6 @@ var init_mechanism_liveness = __esm({
27438
27578
  }
27439
27579
  });
27440
27580
 
27441
- // ../../packages/local-graph/src/wm-calibration.ts
27442
- function wmCalibrationCells(store) {
27443
- const cells = {
27444
- calibrationSchema: WM_CALIBRATION_SCHEMA,
27445
- shownNodes: 0,
27446
- shownImpressions: 0,
27447
- evictedNodes: 0,
27448
- evictedImpressions: 0,
27449
- reinforceShown: 0,
27450
- reinforceRecent: 0,
27451
- reinforceEvicted: 0,
27452
- reinforceUnshown: 0,
27453
- reinforceUnshownQueryOnly: 0,
27454
- reinforcedNodes: 0,
27455
- reinforcedShownNodes: 0
27456
- };
27457
- const newest = /* @__PURE__ */ new Map();
27458
- for (const label of WM_CALIBRATION_LABELS) {
27459
- for (const n of store.selectNodes({ labels: [label], attrs: WM_CALIBRATION_KEYS, includeClosed: true })) {
27460
- const live3 = n.validTo === null;
27461
- const prev = newest.get(n.logicalId);
27462
- if (prev && (prev.live || !live3 && prev.version >= n.version)) continue;
27463
- newest.set(n.logicalId, { version: n.version, live: live3, attrs: n.attrs });
27464
- }
27465
- }
27466
- for (const { attrs } of newest.values()) {
27467
- const shown = num(attrs["shownCount"]);
27468
- const evicted = num(attrs["evictedCount"]);
27469
- if (shown > 0) {
27470
- cells.shownNodes++;
27471
- cells.shownImpressions += shown;
27472
- }
27473
- if (evicted > 0) {
27474
- cells.evictedNodes++;
27475
- cells.evictedImpressions += evicted;
27476
- }
27477
- const rs = num(attrs["reinforceShownCount"]);
27478
- const rr = num(attrs["reinforceRecentCount"]);
27479
- const re = num(attrs["reinforceEvictedCount"]);
27480
- const ru = num(attrs["reinforceUnshownCount"]);
27481
- cells.reinforceShown += rs;
27482
- cells.reinforceRecent += rr;
27483
- cells.reinforceEvicted += re;
27484
- cells.reinforceUnshown += ru;
27485
- cells.reinforceUnshownQueryOnly += num(attrs["reinforceUnshownQueryOnlyCount"]);
27486
- if (rs + rr + re + ru > 0) cells.reinforcedNodes++;
27487
- if (rs > 0) cells.reinforcedShownNodes++;
27488
- }
27489
- return cells;
27490
- }
27491
- var WM_CALIBRATION_LABELS, WM_CALIBRATION_SCHEMA, num, WM_CALIBRATION_KEYS;
27492
- var init_wm_calibration = __esm({
27493
- "../../packages/local-graph/src/wm-calibration.ts"() {
27494
- "use strict";
27495
- WM_CALIBRATION_LABELS = [
27496
- "Problem",
27497
- "Solution",
27498
- "RootCause",
27499
- "Pattern",
27500
- "Technique",
27501
- "AntiPattern"
27502
- ];
27503
- WM_CALIBRATION_SCHEMA = 2;
27504
- num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
27505
- WM_CALIBRATION_KEYS = [
27506
- "shownCount",
27507
- "evictedCount",
27508
- "reinforceShownCount",
27509
- "reinforceRecentCount",
27510
- "reinforceEvictedCount",
27511
- "reinforceUnshownCount",
27512
- "reinforceUnshownQueryOnlyCount"
27513
- ];
27514
- }
27515
- });
27516
-
27517
27581
  // ../../packages/local-graph/src/index.ts
27518
27582
  var src_exports2 = {};
27519
27583
  __export(src_exports2, {
@@ -27560,8 +27624,10 @@ __export(src_exports2, {
27560
27624
  STALL_MIN_RUNS: () => STALL_MIN_RUNS,
27561
27625
  STALL_MIN_SPAN_MS: () => STALL_MIN_SPAN_MS,
27562
27626
  SqliteGraphStore: () => SqliteGraphStore,
27627
+ WM_CALIBRATION_KEYS: () => WM_CALIBRATION_KEYS,
27563
27628
  WM_CALIBRATION_LABELS: () => WM_CALIBRATION_LABELS,
27564
27629
  WM_CALIBRATION_SCHEMA: () => WM_CALIBRATION_SCHEMA,
27630
+ WM_COUNTED_PREDICATE: () => WM_COUNTED_PREDICATE,
27565
27631
  WORKSPACE_SCAN_CAP: () => WORKSPACE_SCAN_CAP,
27566
27632
  addDependency: () => addDependency,
27567
27633
  aggregateClaimConfidence: () => aggregateClaimConfidence,
@@ -27708,7 +27774,8 @@ __export(src_exports2, {
27708
27774
  upgradeAnchorHints: () => upgradeAnchorHints,
27709
27775
  upgradeAnchorHintsForFile: () => upgradeAnchorHintsForFile,
27710
27776
  walk: () => walk,
27711
- wmCalibrationCells: () => wmCalibrationCells
27777
+ wmCalibrationCells: () => wmCalibrationCells,
27778
+ wmCounterSql: () => wmCounterSql
27712
27779
  });
27713
27780
  var init_src5 = __esm({
27714
27781
  "../../packages/local-graph/src/index.ts"() {
@@ -38994,7 +39061,28 @@ async function incrementalReindex(store, rootPath, workspaceId2, changedAbsPaths
38994
39061
  store.closeNode(nid, t);
38995
39062
  }
38996
39063
  });
38997
- 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, {
38998
39086
  workspaceId: workspaceId2,
38999
39087
  rootPath,
39000
39088
  providers,
@@ -39012,14 +39100,14 @@ async function incrementalReindex(store, rootPath, workspaceId2, changedAbsPaths
39012
39100
  if (typeof rel !== "string" || !changedRelPaths.has(rel)) continue;
39013
39101
  if (versionedAndFile.has(n.label)) {
39014
39102
  ownedLive.push(n.id);
39015
- } else if (n.label === "Comment" && n.lastUpdatedAt < t) {
39103
+ } else if (n.label === "Comment" && n.lastUpdatedAt < t && !seenNodes.has(n.id)) {
39016
39104
  store.closeNode(n.id, t);
39017
39105
  }
39018
39106
  }
39019
39107
  for (const id of ownedLive) {
39020
39108
  for (const e of store.outEdges(id)) {
39021
39109
  if (e.type === "SUPERSEDED_BY") continue;
39022
- if (e.lastSeenAt < t) {
39110
+ if (e.lastSeenAt < t && !seenEdges.has(edgeId(e.from, e.type, e.to))) {
39023
39111
  store.closeEdge(e.id, t);
39024
39112
  delta.edgesRemoved++;
39025
39113
  } else if (e.createdAt >= t) {
@@ -87049,7 +87137,7 @@ function createWatchBreaker(deps) {
87049
87137
  }
87050
87138
 
87051
87139
  // src/engine.ts
87052
- var DAEMON_VERSION = true ? "2.0.22-dev.4665" : "2.0.0-alpha.0";
87140
+ var DAEMON_VERSION = true ? "2.0.22-dev.4669" : "2.0.0-alpha.0";
87053
87141
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
87054
87142
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
87055
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) {
@@ -16308,6 +16321,9 @@ CREATE INDEX IF NOT EXISTS nodes_live_resolved ON nodes(label, valid_to) WHERE v
16308
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;
16309
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;
16310
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};
16311
16327
  CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
16312
16328
  -- Covering + partial index for labelFingerprint over the instance labels: the
16313
16329
  -- live-push probe runs it per workspace on a timer, and each hit used to read
@@ -16526,6 +16542,12 @@ var SqliteGraphStore = class {
16526
16542
  `),
16527
16543
  getNode: this.db.prepare("SELECT * FROM nodes WHERE id = ?"),
16528
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
+ ),
16529
16551
  // Single-column fast paths for the nightly pass — writing 100k pageRank
16530
16552
  // values through the 18-column COALESCE updateNode took ~30s; these are ~10x
16531
16553
  // cheaper (one bound column, no COALESCE evaluation per row).
@@ -16737,6 +16759,27 @@ var SqliteGraphStore = class {
16737
16759
  ORDER BY label, created_at, rowid`
16738
16760
  ).all(label);
16739
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
+ }
16740
16783
  liveCommentsAttachedTo(name) {
16741
16784
  return this.db.prepare(
16742
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
@@ -16876,6 +16919,19 @@ var SqliteGraphStore = class {
16876
16919
  attrs_json: JSON.stringify(node.attrs ?? {})
16877
16920
  });
16878
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
+ }
16879
16935
  updateNode(id, patch) {
16880
16936
  const hasEmbedding = patch.embedding !== void 0 && patch.embedding.length > 0;
16881
16937
  this.stmts.updateNode.run({
@@ -16981,6 +17037,21 @@ var SqliteGraphStore = class {
16981
17037
  attrs_json: JSON.stringify(edge.attrs ?? {})
16982
17038
  });
16983
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
+ }
16984
17055
  /** EDGE_RULES check against the stored endpoints' labels, with the LOCAL
16985
17056
  * overlay consulted first. Returns the reason string on a documented-
16986
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.4665",
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) {
@@ -27187,6 +27283,9 @@ CREATE INDEX IF NOT EXISTS nodes_live_resolved ON nodes(label, valid_to) WHERE v
27187
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;
27188
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;
27189
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};
27190
27289
  CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
27191
27290
  -- Covering + partial index for labelFingerprint over the instance labels: the
27192
27291
  -- live-push probe runs it per workspace on a timer, and each hit used to read
@@ -27405,6 +27504,12 @@ var SqliteGraphStore = class {
27405
27504
  `),
27406
27505
  getNode: this.db.prepare("SELECT * FROM nodes WHERE id = ?"),
27407
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
+ ),
27408
27513
  // Single-column fast paths for the nightly pass — writing 100k pageRank
27409
27514
  // values through the 18-column COALESCE updateNode took ~30s; these are ~10x
27410
27515
  // cheaper (one bound column, no COALESCE evaluation per row).
@@ -27616,6 +27721,27 @@ var SqliteGraphStore = class {
27616
27721
  ORDER BY label, created_at, rowid`
27617
27722
  ).all(label);
27618
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
+ }
27619
27745
  liveCommentsAttachedTo(name2) {
27620
27746
  return this.db.prepare(
27621
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
@@ -27755,6 +27881,19 @@ var SqliteGraphStore = class {
27755
27881
  attrs_json: JSON.stringify(node.attrs ?? {})
27756
27882
  });
27757
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
+ }
27758
27897
  updateNode(id, patch) {
27759
27898
  const hasEmbedding = patch.embedding !== void 0 && patch.embedding.length > 0;
27760
27899
  this.stmts.updateNode.run({
@@ -27860,6 +27999,21 @@ var SqliteGraphStore = class {
27860
27999
  attrs_json: JSON.stringify(edge.attrs ?? {})
27861
28000
  });
27862
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
+ }
27863
28017
  /** EDGE_RULES check against the stored endpoints' labels, with the LOCAL
27864
28018
  * overlay consulted first. Returns the reason string on a documented-
27865
28019
  * forbidden combination, else null. Point lookups on the id PK — negligible
@@ -30415,76 +30569,6 @@ function minFractionOf(d) {
30415
30569
  return d.fedIncoming?.minFraction ?? d.fedEdges?.minFraction ?? d.fed?.minFraction ?? 0;
30416
30570
  }
30417
30571
 
30418
- // ../../packages/local-graph/src/wm-calibration.ts
30419
- var WM_CALIBRATION_LABELS = [
30420
- "Problem",
30421
- "Solution",
30422
- "RootCause",
30423
- "Pattern",
30424
- "Technique",
30425
- "AntiPattern"
30426
- ];
30427
- var WM_CALIBRATION_SCHEMA = 2;
30428
- var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
30429
- var WM_CALIBRATION_KEYS = [
30430
- "shownCount",
30431
- "evictedCount",
30432
- "reinforceShownCount",
30433
- "reinforceRecentCount",
30434
- "reinforceEvictedCount",
30435
- "reinforceUnshownCount",
30436
- "reinforceUnshownQueryOnlyCount"
30437
- ];
30438
- function wmCalibrationCells(store2) {
30439
- const cells = {
30440
- calibrationSchema: WM_CALIBRATION_SCHEMA,
30441
- shownNodes: 0,
30442
- shownImpressions: 0,
30443
- evictedNodes: 0,
30444
- evictedImpressions: 0,
30445
- reinforceShown: 0,
30446
- reinforceRecent: 0,
30447
- reinforceEvicted: 0,
30448
- reinforceUnshown: 0,
30449
- reinforceUnshownQueryOnly: 0,
30450
- reinforcedNodes: 0,
30451
- reinforcedShownNodes: 0
30452
- };
30453
- const newest = /* @__PURE__ */ new Map();
30454
- for (const label of WM_CALIBRATION_LABELS) {
30455
- for (const n of store2.selectNodes({ labels: [label], attrs: WM_CALIBRATION_KEYS, includeClosed: true })) {
30456
- const live = n.validTo === null;
30457
- const prev = newest.get(n.logicalId);
30458
- if (prev && (prev.live || !live && prev.version >= n.version)) continue;
30459
- newest.set(n.logicalId, { version: n.version, live, attrs: n.attrs });
30460
- }
30461
- }
30462
- for (const { attrs } of newest.values()) {
30463
- const shown = num(attrs["shownCount"]);
30464
- const evicted = num(attrs["evictedCount"]);
30465
- if (shown > 0) {
30466
- cells.shownNodes++;
30467
- cells.shownImpressions += shown;
30468
- }
30469
- if (evicted > 0) {
30470
- cells.evictedNodes++;
30471
- cells.evictedImpressions += evicted;
30472
- }
30473
- const rs = num(attrs["reinforceShownCount"]);
30474
- const rr = num(attrs["reinforceRecentCount"]);
30475
- const re = num(attrs["reinforceEvictedCount"]);
30476
- const ru = num(attrs["reinforceUnshownCount"]);
30477
- cells.reinforceShown += rs;
30478
- cells.reinforceRecent += rr;
30479
- cells.reinforceEvicted += re;
30480
- cells.reinforceUnshown += ru;
30481
- cells.reinforceUnshownQueryOnly += num(attrs["reinforceUnshownQueryOnlyCount"]);
30482
- if (rs + rr + re + ru > 0) cells.reinforcedNodes++;
30483
- if (rs > 0) cells.reinforcedShownNodes++;
30484
- }
30485
- return cells;
30486
- }
30487
-
30488
30572
  // ../../packages/generalizer/src/generalizer.ts
30489
30573
  init_src2();
30490
30574
  init_src();
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "errata-context",
3
- "version": "2.0.22-dev.4665",
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
- f1e400bb7113a7620b6d9642dba999330a5888059992763fc34dd7c802ff0843 .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