@inerrata-corporation/errata 2.0.21-dev.3740 → 2.0.21-dev.3749

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.
@@ -14980,7 +14980,27 @@ function installEditPlaceholderRetirement(db) {
14980
14980
  });
14981
14981
  }
14982
14982
 
14983
+ // ../../packages/math/src/relevance.ts
14984
+ function pageRankScale(ranks) {
14985
+ const positive = [...ranks].filter((r) => Number.isFinite(r) && r > 0).sort((a, b) => a - b);
14986
+ return positive.length ? Math.max(1e-9, positive[Math.floor((positive.length - 1) * 0.95)]) : 1;
14987
+ }
14988
+
14983
14989
  // ../../packages/local-graph/src/store.ts
14990
+ var PAGE_RANK_SCALE_KEY = "pageRankScale.p95.v1";
14991
+ var TEXT_SEARCH_COLUMNS = `id, label, description, page_rank,
14992
+ json_object('test', json(CASE WHEN json_type(attrs_json, '$.test') = 'true' THEN 'true' ELSE 'false' END),
14993
+ 'relPath', json_extract(attrs_json, '$.relPath'),
14994
+ 'resolutionSuspect', json(CASE WHEN json_type(attrs_json, '$.resolutionSuspect') = 'true' THEN 'true' ELSE 'false' END),
14995
+ 'resolvedAt', json_extract(attrs_json, '$.resolvedAt'),
14996
+ 'refutedAt', json_extract(attrs_json, '$.refutedAt'),
14997
+ 'refuteReasons', json_extract(attrs_json, '$.refuteReasons'),
14998
+ 'reopenedFrom', json_extract(attrs_json, '$.reopenedFrom'),
14999
+ 'resolvedAs', json_extract(attrs_json, '$.resolvedAs')) AS search_attrs`;
15000
+ function textSearchNode(row) {
15001
+ const attrs = JSON.parse(row.search_attrs);
15002
+ return { id: row.id, label: row.label, description: row.description, pageRank: row.page_rank, attrs };
15003
+ }
14984
15004
  var LOCAL_RULE_OVERRIDES = {
14985
15005
  ANCHORED_AT: {
14986
15006
  from: EDGE_RULES["ANCHORED_AT"]?.from,
@@ -15124,6 +15144,9 @@ CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_E
15124
15144
  DROP INDEX IF EXISTS nodes_live_fingerprint;
15125
15145
  DROP INDEX IF EXISTS nodes_instance_fingerprint;
15126
15146
  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');
15147
+ -- Calibrate rank from a covering scalar index; the global page-rank index
15148
+ -- requires a table lookup per node to exclude frozen versions.
15149
+ CREATE INDEX IF NOT EXISTS nodes_live_page_rank ON nodes(valid_to, page_rank);
15127
15150
  CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
15128
15151
  CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
15129
15152
  -- Expression index on the per-node relPath (stored in attrs_json) so the
@@ -15222,6 +15245,7 @@ var SqliteGraphStore = class {
15222
15245
  * ALL upserts incl. code reindex) — this counter is the knowledge-arrival axis
15223
15246
  * Δseq is measured over, so code reindex churn must NOT advance it. */
15224
15247
  ingestSeq = 0;
15248
+ initialPageRankScale;
15225
15249
  constructor(opts) {
15226
15250
  this.db = openDatabase(opts.path);
15227
15251
  this.db.exec(SCHEMA_SQL);
@@ -15841,6 +15865,34 @@ var SqliteGraphStore = class {
15841
15865
  ).all(...terms.map((t) => `%${t}%`), Math.max(1, limit));
15842
15866
  return rows.map((r) => ({ node: rowToNode(r), matched: Number(r.matched) }));
15843
15867
  }
15868
+ searchTextCandidates(terms) {
15869
+ if (terms.length === 0) return [];
15870
+ const rows = this.db.prepare(`WITH matches AS MATERIALIZED (
15871
+ SELECT n.id, COUNT(*) AS matched FROM nodes n INDEXED BY nodes_live_description, json_each(:terms) t
15872
+ WHERE n.valid_to IS NULL AND n.description LIKE '%' || t.value || '%'
15873
+ GROUP BY n.id
15874
+ ) SELECT ${TEXT_SEARCH_COLUMNS}, matches.matched FROM nodes
15875
+ JOIN matches USING (id) ORDER BY page_rank DESC, last_updated_at DESC`).all({ terms: JSON.stringify(terms) });
15876
+ return rows.map((row) => ({ node: textSearchNode(row), matched: row.matched }));
15877
+ }
15878
+ getTextSearchNode(id) {
15879
+ const row = this.db.prepare(`SELECT ${TEXT_SEARCH_COLUMNS} FROM nodes WHERE id = ? AND valid_to IS NULL`).get(id);
15880
+ return row ? textSearchNode(row) : null;
15881
+ }
15882
+ readPageRankScale() {
15883
+ const rows = this.db.prepare("SELECT page_rank FROM nodes WHERE valid_to IS NULL AND page_rank > 0").all();
15884
+ return pageRankScale(rows.map((row) => row.page_rank));
15885
+ }
15886
+ pageRankScale() {
15887
+ const value = Number(this.getMeta(PAGE_RANK_SCALE_KEY));
15888
+ if (Number.isFinite(value) && value > 0) return value;
15889
+ return this.initialPageRankScale ??= this.readPageRankScale();
15890
+ }
15891
+ refreshPageRankScale() {
15892
+ const scale = this.readPageRankScale();
15893
+ this.setMeta(PAGE_RANK_SCALE_KEY, String(scale));
15894
+ return scale;
15895
+ }
15844
15896
  typedEdgeStmt(sql) {
15845
15897
  let stmt = this.typedEdgeStmts.get(sql);
15846
15898
  if (!stmt) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.21-dev.3740",
3
+ "version": "2.0.21-dev.3749",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {