@inerrata-corporation/errata 2.0.21-dev.3745 → 2.0.21-dev.3763

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.
@@ -14651,6 +14651,9 @@ var CastaliaIngestPayloadSchema = external_exports.object({
14651
14651
  symbolSummaries: SymbolSummarySidecarSchema.optional()
14652
14652
  });
14653
14653
 
14654
+ // ../../packages/shared/src/history-read.ts
14655
+ var CLOUD_ATTR_DUPLICATE_KEYS = ["attrsJson", "embedding"];
14656
+
14654
14657
  // ../../packages/shared/src/history-revision.ts
14655
14658
  var Sha = external_exports.string().regex(/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/);
14656
14659
  var HistoryRevisionRequestSchema = external_exports.strictObject({
@@ -14980,7 +14983,59 @@ function installEditPlaceholderRetirement(db) {
14980
14983
  });
14981
14984
  }
14982
14985
 
14986
+ // ../../packages/local-graph/src/cloud-attr-compaction.ts
14987
+ var CLOUD_ATTR_COMPACTION_KEY = "cloud-attr-compaction-1";
14988
+ var DUPES = CLOUD_ATTR_DUPLICATE_KEYS.map((k) => `'${k}'`).join(",");
14989
+ function compacted(row) {
14990
+ const packed = `json_extract(${row}.attrs_json, '$.attrsJson')`;
14991
+ const nested = `CASE WHEN json_type(${row}.attrs_json, '$.attrsJson') = 'text' AND json_valid(${packed})
14992
+ AND json_type(${packed}) = 'object' THEN ${packed} ELSE '{}' END`;
14993
+ const value = `CASE WHEN type IN ('array','object') THEN json(value) WHEN type IN ('true','false') THEN json(type) ELSE value END`;
14994
+ return `(SELECT json_group_object(key, ${value}) FROM (
14995
+ SELECT key, value, type, min(pri) FROM (
14996
+ SELECT key, value, type, 0 AS pri FROM json_each(${row}.attrs_json)
14997
+ UNION ALL
14998
+ SELECT key, value, type, 1 AS pri FROM json_each(${nested})
14999
+ ) WHERE key NOT IN (${DUPES}) GROUP BY key))`;
15000
+ }
15001
+ function dirty(row) {
15002
+ const has = CLOUD_ATTR_DUPLICATE_KEYS.map((k) => `json_type(${row}.attrs_json, '$.${k}') IS NOT NULL`).join(" OR ");
15003
+ return `json_extract(${row}.attrs_json, '$.source') = 'cloud' AND (${has})`;
15004
+ }
15005
+ function installCloudAttrCompaction(db) {
15006
+ if (db.prepare("SELECT value FROM store_meta WHERE key = ?").get(CLOUD_ATTR_COMPACTION_KEY)) return;
15007
+ db.transaction(() => {
15008
+ for (const operation of ["INSERT", "UPDATE"]) {
15009
+ db.exec(`CREATE TRIGGER IF NOT EXISTS cloud_attr_compact_${operation.toLowerCase()}
15010
+ AFTER ${operation} ON nodes WHEN ${dirty("NEW")}
15011
+ BEGIN UPDATE nodes SET attrs_json = ${compacted("nodes")} WHERE id = NEW.id; END;`);
15012
+ }
15013
+ db.exec(`UPDATE nodes SET attrs_json = ${compacted("nodes")} WHERE ${dirty("nodes")}`);
15014
+ db.prepare("INSERT INTO store_meta(key,value) VALUES (?,?)").run(CLOUD_ATTR_COMPACTION_KEY, "installed");
15015
+ });
15016
+ }
15017
+
15018
+ // ../../packages/math/src/relevance.ts
15019
+ function pageRankScale(ranks) {
15020
+ const positive = [...ranks].filter((r) => Number.isFinite(r) && r > 0).sort((a, b) => a - b);
15021
+ return positive.length ? Math.max(1e-9, positive[Math.floor((positive.length - 1) * 0.95)]) : 1;
15022
+ }
15023
+
14983
15024
  // ../../packages/local-graph/src/store.ts
15025
+ var PAGE_RANK_SCALE_KEY = "pageRankScale.p95.v1";
15026
+ var TEXT_SEARCH_COLUMNS = `id, label, description, page_rank,
15027
+ json_object('test', json(CASE WHEN json_type(attrs_json, '$.test') = 'true' THEN 'true' ELSE 'false' END),
15028
+ 'relPath', json_extract(attrs_json, '$.relPath'),
15029
+ 'resolutionSuspect', json(CASE WHEN json_type(attrs_json, '$.resolutionSuspect') = 'true' THEN 'true' ELSE 'false' END),
15030
+ 'resolvedAt', json_extract(attrs_json, '$.resolvedAt'),
15031
+ 'refutedAt', json_extract(attrs_json, '$.refutedAt'),
15032
+ 'refuteReasons', json_extract(attrs_json, '$.refuteReasons'),
15033
+ 'reopenedFrom', json_extract(attrs_json, '$.reopenedFrom'),
15034
+ 'resolvedAs', json_extract(attrs_json, '$.resolvedAs')) AS search_attrs`;
15035
+ function textSearchNode(row) {
15036
+ const attrs = JSON.parse(row.search_attrs);
15037
+ return { id: row.id, label: row.label, description: row.description, pageRank: row.page_rank, attrs };
15038
+ }
14984
15039
  var LOCAL_RULE_OVERRIDES = {
14985
15040
  ANCHORED_AT: {
14986
15041
  from: EDGE_RULES["ANCHORED_AT"]?.from,
@@ -15124,6 +15179,9 @@ CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_E
15124
15179
  DROP INDEX IF EXISTS nodes_live_fingerprint;
15125
15180
  DROP INDEX IF EXISTS nodes_instance_fingerprint;
15126
15181
  CREATE INDEX IF NOT EXISTS nodes_instance_fingerprint_v2 ON nodes(label, last_updated_at, last_reinforced_at_seq, created_at_seq) WHERE valid_to IS NULL AND label IN ('Problem','Vulnerability','Solution','RootCause');
15182
+ -- Calibrate rank from a covering scalar index; the global page-rank index
15183
+ -- requires a table lookup per node to exclude frozen versions.
15184
+ CREATE INDEX IF NOT EXISTS nodes_live_page_rank ON nodes(valid_to, page_rank);
15127
15185
  CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
15128
15186
  CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
15129
15187
  -- Expression index on the per-node relPath (stored in attrs_json) so the
@@ -15136,6 +15194,12 @@ CREATE INDEX IF NOT EXISTS nodes_live_description ON nodes(description, label) W
15136
15194
  -- Problems by the file their anchor hint names: the harvest upgrades hints for
15137
15195
  -- every edited file of every turn, and a scan parses every Problem's attrs.
15138
15196
  CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_json, '$.anchorHint.path')) WHERE valid_to IS NULL;
15197
+ -- Problems by the command / checker / file that opened them. The generalizer
15198
+ -- looked these up per shell or tool EVENT by hydrating every Problem: ~12s of
15199
+ -- every minute of daemon main-thread time on a 3.4k-Problem store (2026-10-05).
15200
+ CREATE INDEX IF NOT EXISTS nodes_command_signature ON nodes(json_extract(attrs_json, '$.commandSignature')) WHERE valid_to IS NULL;
15201
+ CREATE INDEX IF NOT EXISTS nodes_diag_command ON nodes(json_extract(attrs_json, '$.diagCommand')) WHERE valid_to IS NULL;
15202
+ CREATE INDEX IF NOT EXISTS nodes_lsp_rel_path ON nodes(json_extract(attrs_json, '$.lspRelPath')) WHERE valid_to IS NULL;
15139
15203
  -- Nodes still waiting on a root-cause ask. The MCP server checks this on EVERY
15140
15204
  -- tool response (the enrichment piggyback), so it must not parse every
15141
15205
  -- Solution's and Problem's attrs per call. Not partial: the Problem pull reads
@@ -15222,6 +15286,7 @@ var SqliteGraphStore = class {
15222
15286
  * ALL upserts incl. code reindex) — this counter is the knowledge-arrival axis
15223
15287
  * Δseq is measured over, so code reindex churn must NOT advance it. */
15224
15288
  ingestSeq = 0;
15289
+ initialPageRankScale;
15225
15290
  constructor(opts) {
15226
15291
  this.db = openDatabase(opts.path);
15227
15292
  this.db.exec(SCHEMA_SQL);
@@ -15238,6 +15303,7 @@ var SqliteGraphStore = class {
15238
15303
  this.db.exec(TEMPORAL_INDEX_SQL);
15239
15304
  installHistoryCacheFloor(this.db);
15240
15305
  installEditPlaceholderRetirement(this.db);
15306
+ installCloudAttrCompaction(this.db);
15241
15307
  if (!this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(SESSION_REPAIR_KEY)) {
15242
15308
  this.db.transaction(() => {
15243
15309
  this.repairSessionAliases();
@@ -15841,6 +15907,34 @@ var SqliteGraphStore = class {
15841
15907
  ).all(...terms.map((t) => `%${t}%`), Math.max(1, limit));
15842
15908
  return rows.map((r) => ({ node: rowToNode(r), matched: Number(r.matched) }));
15843
15909
  }
15910
+ searchTextCandidates(terms) {
15911
+ if (terms.length === 0) return [];
15912
+ const rows = this.db.prepare(`WITH matches AS MATERIALIZED (
15913
+ SELECT n.id, COUNT(*) AS matched FROM nodes n INDEXED BY nodes_live_description, json_each(:terms) t
15914
+ WHERE n.valid_to IS NULL AND n.description LIKE '%' || t.value || '%'
15915
+ GROUP BY n.id
15916
+ ) SELECT ${TEXT_SEARCH_COLUMNS}, matches.matched FROM nodes
15917
+ JOIN matches USING (id) ORDER BY page_rank DESC, last_updated_at DESC`).all({ terms: JSON.stringify(terms) });
15918
+ return rows.map((row) => ({ node: textSearchNode(row), matched: row.matched }));
15919
+ }
15920
+ getTextSearchNode(id) {
15921
+ const row = this.db.prepare(`SELECT ${TEXT_SEARCH_COLUMNS} FROM nodes WHERE id = ? AND valid_to IS NULL`).get(id);
15922
+ return row ? textSearchNode(row) : null;
15923
+ }
15924
+ readPageRankScale() {
15925
+ const rows = this.db.prepare("SELECT page_rank FROM nodes WHERE valid_to IS NULL AND page_rank > 0").all();
15926
+ return pageRankScale(rows.map((row) => row.page_rank));
15927
+ }
15928
+ pageRankScale() {
15929
+ const value = Number(this.getMeta(PAGE_RANK_SCALE_KEY));
15930
+ if (Number.isFinite(value) && value > 0) return value;
15931
+ return this.initialPageRankScale ??= this.readPageRankScale();
15932
+ }
15933
+ refreshPageRankScale() {
15934
+ const scale = this.readPageRankScale();
15935
+ this.setMeta(PAGE_RANK_SCALE_KEY, String(scale));
15936
+ return scale;
15937
+ }
15844
15938
  typedEdgeStmt(sql) {
15845
15939
  let stmt = this.typedEdgeStmts.get(sql);
15846
15940
  if (!stmt) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.21-dev.3745",
3
+ "version": "2.0.21-dev.3763",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {