@inerrata-corporation/errata 2.0.8-dev.2914 → 2.0.8-dev.2916

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.
@@ -16137,6 +16137,12 @@ CREATE UNIQUE INDEX IF NOT EXISTS edges_triple ON edges(from_id, type, to_id);
16137
16137
  `;
16138
16138
  var TEMPORAL_INDEX_SQL = `
16139
16139
  CREATE INDEX IF NOT EXISTS nodes_live ON nodes(label, valid_to);
16140
+ -- Covering + partial index for labelFingerprint over the instance labels: the
16141
+ -- live-push probe runs it per workspace on a timer, and each hit used to read
16142
+ -- its table row. Partial, so it maintains a few thousand rows instead of adding
16143
+ -- a write to every node write. Its label list is FINGERPRINT_INDEXED_LABELS.
16144
+ DROP INDEX IF EXISTS nodes_live_fingerprint;
16145
+ CREATE INDEX IF NOT EXISTS nodes_instance_fingerprint ON nodes(label, last_updated_at, last_reinforced_at_seq, created_at_seq) WHERE valid_to IS NULL AND label IN ('Problem','Solution','RootCause');
16140
16146
  CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
16141
16147
  CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
16142
16148
  -- Expression index on the per-node relPath (stored in attrs_json) so the
@@ -16207,6 +16213,7 @@ function rowToEdge(r) {
16207
16213
  attrs: JSON.parse(r.attrs_json)
16208
16214
  };
16209
16215
  }
16216
+ var FINGERPRINT_INDEXED_LABELS = ["Problem", "Solution", "RootCause"];
16210
16217
  var SqliteGraphStore = class {
16211
16218
  db;
16212
16219
  // Typed edge lookups build their SQL from the number of types asked for, and
@@ -16683,6 +16690,32 @@ var SqliteGraphStore = class {
16683
16690
  attrs: JSON.parse(r.attrs_json)
16684
16691
  }));
16685
16692
  }
16693
+ *iterateNodeIdentitiesByLabel(label, pageSize = 2e3) {
16694
+ const page = this.db.prepare(
16695
+ "SELECT rowid AS rid, id, json_extract(attrs_json, '$.relPath') AS relPath, json_extract(attrs_json, '$.qname') AS qname FROM nodes WHERE rowid > ? AND label = ? AND valid_to IS NULL ORDER BY rowid LIMIT ?"
16696
+ );
16697
+ let after = 0;
16698
+ for (; ; ) {
16699
+ const rows = page.all(after, label, pageSize);
16700
+ for (const r of rows) {
16701
+ yield {
16702
+ id: r.id,
16703
+ relPath: typeof r.relPath === "string" ? r.relPath : null,
16704
+ qname: typeof r.qname === "string" ? r.qname : null
16705
+ };
16706
+ }
16707
+ if (rows.length < pageSize) return;
16708
+ after = rows[rows.length - 1].rid;
16709
+ }
16710
+ }
16711
+ recentNodeIds(label, limit) {
16712
+ const rows = this.db.prepare("SELECT id FROM nodes WHERE label = ? AND valid_to IS NULL ORDER BY last_updated_at DESC LIMIT ?").all(label, Math.max(0, limit));
16713
+ return rows.map((r) => r.id);
16714
+ }
16715
+ hasLiveNodes() {
16716
+ const r = this.db.prepare("SELECT EXISTS(SELECT 1 FROM nodes WHERE valid_to IS NULL) AS e").get();
16717
+ return r.e === 1;
16718
+ }
16686
16719
  findNodeIdentitiesByLabel(label) {
16687
16720
  const rows = this.stmts.identitiesByLabel.all(label);
16688
16721
  return rows.map((r) => ({
@@ -16830,6 +16863,15 @@ var SqliteGraphStore = class {
16830
16863
  }
16831
16864
  labelFingerprint(labels) {
16832
16865
  if (labels.length === 0) return "0";
16866
+ if (labels.length === FINGERPRINT_INDEXED_LABELS.length && FINGERPRINT_INDEXED_LABELS.every((l) => labels.includes(l))) {
16867
+ const r2 = this.db.prepare(
16868
+ `SELECT COUNT(*) AS n, MAX(last_updated_at) AS t,
16869
+ MAX(COALESCE(last_reinforced_at_seq, created_at_seq, 0)) AS s
16870
+ FROM nodes INDEXED BY nodes_instance_fingerprint
16871
+ WHERE valid_to IS NULL AND label IN ('Problem','Solution','RootCause')`
16872
+ ).get();
16873
+ return `${r2.n}:${r2.t ?? 0}:${r2.s ?? 0}`;
16874
+ }
16833
16875
  const r = this.db.prepare(
16834
16876
  `SELECT COUNT(*) AS n, MAX(last_updated_at) AS t,
16835
16877
  MAX(COALESCE(last_reinforced_at_seq, created_at_seq, 0)) AS s
package/errata.mjs CHANGED
@@ -17438,7 +17438,7 @@ function rowToEdge(r) {
17438
17438
  function openGraphStore(opts) {
17439
17439
  return new SqliteGraphStore(opts);
17440
17440
  }
17441
- var LOCAL_RULE_OVERRIDES, SCHEMA_VERSION, SCHEMA_SQL, TEMPORAL_INDEX_SQL, SqliteGraphStore;
17441
+ var LOCAL_RULE_OVERRIDES, SCHEMA_VERSION, SCHEMA_SQL, TEMPORAL_INDEX_SQL, FINGERPRINT_INDEXED_LABELS, SqliteGraphStore;
17442
17442
  var init_store = __esm({
17443
17443
  "../../packages/local-graph/src/store.ts"() {
17444
17444
  "use strict";
@@ -17559,6 +17559,12 @@ CREATE UNIQUE INDEX IF NOT EXISTS edges_triple ON edges(from_id, type, to_id);
17559
17559
  `;
17560
17560
  TEMPORAL_INDEX_SQL = `
17561
17561
  CREATE INDEX IF NOT EXISTS nodes_live ON nodes(label, valid_to);
17562
+ -- Covering + partial index for labelFingerprint over the instance labels: the
17563
+ -- live-push probe runs it per workspace on a timer, and each hit used to read
17564
+ -- its table row. Partial, so it maintains a few thousand rows instead of adding
17565
+ -- a write to every node write. Its label list is FINGERPRINT_INDEXED_LABELS.
17566
+ DROP INDEX IF EXISTS nodes_live_fingerprint;
17567
+ CREATE INDEX IF NOT EXISTS nodes_instance_fingerprint ON nodes(label, last_updated_at, last_reinforced_at_seq, created_at_seq) WHERE valid_to IS NULL AND label IN ('Problem','Solution','RootCause');
17562
17568
  CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
17563
17569
  CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
17564
17570
  -- Expression index on the per-node relPath (stored in attrs_json) so the
@@ -17572,6 +17578,7 @@ CREATE INDEX IF NOT EXISTS nodes_live_description ON nodes(description, label) W
17572
17578
  -- every edited file of every turn, and a scan parses every Problem's attrs.
17573
17579
  CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_json, '$.anchorHint.path')) WHERE valid_to IS NULL;
17574
17580
  `;
17581
+ FINGERPRINT_INDEXED_LABELS = ["Problem", "Solution", "RootCause"];
17575
17582
  SqliteGraphStore = class {
17576
17583
  db;
17577
17584
  // Typed edge lookups build their SQL from the number of types asked for, and
@@ -18048,6 +18055,32 @@ CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_js
18048
18055
  attrs: JSON.parse(r.attrs_json)
18049
18056
  }));
18050
18057
  }
18058
+ *iterateNodeIdentitiesByLabel(label, pageSize = 2e3) {
18059
+ const page = this.db.prepare(
18060
+ "SELECT rowid AS rid, id, json_extract(attrs_json, '$.relPath') AS relPath, json_extract(attrs_json, '$.qname') AS qname FROM nodes WHERE rowid > ? AND label = ? AND valid_to IS NULL ORDER BY rowid LIMIT ?"
18061
+ );
18062
+ let after = 0;
18063
+ for (; ; ) {
18064
+ const rows = page.all(after, label, pageSize);
18065
+ for (const r of rows) {
18066
+ yield {
18067
+ id: r.id,
18068
+ relPath: typeof r.relPath === "string" ? r.relPath : null,
18069
+ qname: typeof r.qname === "string" ? r.qname : null
18070
+ };
18071
+ }
18072
+ if (rows.length < pageSize) return;
18073
+ after = rows[rows.length - 1].rid;
18074
+ }
18075
+ }
18076
+ recentNodeIds(label, limit) {
18077
+ const rows = this.db.prepare("SELECT id FROM nodes WHERE label = ? AND valid_to IS NULL ORDER BY last_updated_at DESC LIMIT ?").all(label, Math.max(0, limit));
18078
+ return rows.map((r) => r.id);
18079
+ }
18080
+ hasLiveNodes() {
18081
+ const r = this.db.prepare("SELECT EXISTS(SELECT 1 FROM nodes WHERE valid_to IS NULL) AS e").get();
18082
+ return r.e === 1;
18083
+ }
18051
18084
  findNodeIdentitiesByLabel(label) {
18052
18085
  const rows = this.stmts.identitiesByLabel.all(label);
18053
18086
  return rows.map((r) => ({
@@ -18195,6 +18228,15 @@ CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_js
18195
18228
  }
18196
18229
  labelFingerprint(labels) {
18197
18230
  if (labels.length === 0) return "0";
18231
+ if (labels.length === FINGERPRINT_INDEXED_LABELS.length && FINGERPRINT_INDEXED_LABELS.every((l) => labels.includes(l))) {
18232
+ const r2 = this.db.prepare(
18233
+ `SELECT COUNT(*) AS n, MAX(last_updated_at) AS t,
18234
+ MAX(COALESCE(last_reinforced_at_seq, created_at_seq, 0)) AS s
18235
+ FROM nodes INDEXED BY nodes_instance_fingerprint
18236
+ WHERE valid_to IS NULL AND label IN ('Problem','Solution','RootCause')`
18237
+ ).get();
18238
+ return `${r2.n}:${r2.t ?? 0}:${r2.s ?? 0}`;
18239
+ }
18198
18240
  const r = this.db.prepare(
18199
18241
  `SELECT COUNT(*) AS n, MAX(last_updated_at) AS t,
18200
18242
  MAX(COALESCE(last_reinforced_at_seq, created_at_seq, 0)) AS s
@@ -23978,6 +24020,7 @@ __export(src_exports2, {
23978
24020
  CODE_REACH_EDGES: () => CODE_REACH_EDGES,
23979
24021
  CREDIT_FAMILY: () => CREDIT_FAMILY,
23980
24022
  DEFAULT_MIN_COVERAGE: () => DEFAULT_MIN_COVERAGE,
24023
+ FINGERPRINT_INDEXED_LABELS: () => FINGERPRINT_INDEXED_LABELS,
23981
24024
  FIX_CANDIDATE_ASK_LIMIT: () => FIX_CANDIDATE_ASK_LIMIT,
23982
24025
  FOLD_EMBED_AUTO_COSINE: () => FOLD_EMBED_AUTO_COSINE,
23983
24026
  FOLD_JUDGE_MIN_COSINE: () => FOLD_JUDGE_MIN_COSINE,
@@ -80224,7 +80267,7 @@ function createWatchBreaker(deps) {
80224
80267
  }
80225
80268
 
80226
80269
  // src/engine.ts
80227
- var DAEMON_VERSION = true ? "2.0.8-dev.2914" : "2.0.0-alpha.0";
80270
+ var DAEMON_VERSION = true ? "2.0.8-dev.2916" : "2.0.0-alpha.0";
80228
80271
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
80229
80272
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
80230
80273
  var GIT_OP_MUTE_MS = 4e3;
@@ -80791,7 +80834,7 @@ function createWorkspaceEngine(opts) {
80791
80834
  lastRemoteAt = now;
80792
80835
  try {
80793
80836
  const norm = normStackTokens;
80794
- const seed = store.findNodesByLabel("Problem").sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt).slice(0, 6).map((p) => p.id);
80837
+ const seed = store.recentNodeIds("Problem", 6);
80795
80838
  const q = {
80796
80839
  stack: outboundStack(),
80797
80840
  domains: norm(profile.domains),
@@ -84130,7 +84173,7 @@ function buildReverseSymbolMap(opts) {
84130
84173
  function* reverseSymbolEntries(opts) {
84131
84174
  const { store, salt, projectId } = opts;
84132
84175
  for (const kind of ANCHOR_SYMBOL_KINDS2) {
84133
- for (const n of store.findNodeIdentitiesByLabel(kind)) {
84176
+ for (const n of store.iterateNodeIdentitiesByLabel(kind)) {
84134
84177
  const { relPath, qname } = n;
84135
84178
  if (relPath === null || qname === null) continue;
84136
84179
  const sym = projectSymbolId(salt, projectId, relPath, qname, kind);
@@ -84138,7 +84181,7 @@ function* reverseSymbolEntries(opts) {
84138
84181
  yield [projectSaltedNodeId(sym, salt), n.id];
84139
84182
  }
84140
84183
  }
84141
- for (const n of store.findNodeIdentitiesByLabel("File")) {
84184
+ for (const n of store.iterateNodeIdentitiesByLabel("File")) {
84142
84185
  if (n.relPath === null) continue;
84143
84186
  const sym = projectSymbolId(salt, projectId, n.relPath, FILE_ANCHOR_QNAME, "File");
84144
84187
  yield [sym, n.id];
@@ -84178,7 +84221,7 @@ function buildOwnKnowledgeMap(opts) {
84178
84221
  function* ownKnowledgeEntries(opts) {
84179
84222
  const { store, salt } = opts;
84180
84223
  for (const kind of ANCHOR_SOURCE_KINDS) {
84181
- for (const n of store.findNodeIdentitiesByLabel(kind)) {
84224
+ for (const n of store.iterateNodeIdentitiesByLabel(kind)) {
84182
84225
  if (n.id.startsWith("projn_")) continue;
84183
84226
  yield [projectSaltedNodeId(n.id, salt), n.id];
84184
84227
  }
@@ -86489,7 +86532,7 @@ async function startMultiDaemon(opts = {}) {
86489
86532
  registerWorkspace(rec.engine.profile, rec.root);
86490
86533
  } catch {
86491
86534
  }
86492
- if (wakeIndexPlan(rec.engine.store.nodeCount(), opts.reindexOnStart !== false) === "full-index") {
86535
+ if (wakeIndexPlan(rec.engine.store.hasLiveNodes() ? 1 : 0, opts.reindexOnStart !== false) === "full-index") {
86493
86536
  void enqueueWakeIndex(async () => {
86494
86537
  const done = markPass(`wake-index:${rec.entry.name}`);
86495
86538
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.8-dev.2914",
3
+ "version": "2.0.8-dev.2916",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -24519,6 +24519,12 @@ CREATE UNIQUE INDEX IF NOT EXISTS edges_triple ON edges(from_id, type, to_id);
24519
24519
  `;
24520
24520
  var TEMPORAL_INDEX_SQL = `
24521
24521
  CREATE INDEX IF NOT EXISTS nodes_live ON nodes(label, valid_to);
24522
+ -- Covering + partial index for labelFingerprint over the instance labels: the
24523
+ -- live-push probe runs it per workspace on a timer, and each hit used to read
24524
+ -- its table row. Partial, so it maintains a few thousand rows instead of adding
24525
+ -- a write to every node write. Its label list is FINGERPRINT_INDEXED_LABELS.
24526
+ DROP INDEX IF EXISTS nodes_live_fingerprint;
24527
+ CREATE INDEX IF NOT EXISTS nodes_instance_fingerprint ON nodes(label, last_updated_at, last_reinforced_at_seq, created_at_seq) WHERE valid_to IS NULL AND label IN ('Problem','Solution','RootCause');
24522
24528
  CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
24523
24529
  CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
24524
24530
  -- Expression index on the per-node relPath (stored in attrs_json) so the
@@ -24589,6 +24595,7 @@ function rowToEdge(r) {
24589
24595
  attrs: JSON.parse(r.attrs_json)
24590
24596
  };
24591
24597
  }
24598
+ var FINGERPRINT_INDEXED_LABELS = ["Problem", "Solution", "RootCause"];
24592
24599
  var SqliteGraphStore = class {
24593
24600
  db;
24594
24601
  // Typed edge lookups build their SQL from the number of types asked for, and
@@ -25065,6 +25072,32 @@ var SqliteGraphStore = class {
25065
25072
  attrs: JSON.parse(r.attrs_json)
25066
25073
  }));
25067
25074
  }
25075
+ *iterateNodeIdentitiesByLabel(label, pageSize = 2e3) {
25076
+ const page = this.db.prepare(
25077
+ "SELECT rowid AS rid, id, json_extract(attrs_json, '$.relPath') AS relPath, json_extract(attrs_json, '$.qname') AS qname FROM nodes WHERE rowid > ? AND label = ? AND valid_to IS NULL ORDER BY rowid LIMIT ?"
25078
+ );
25079
+ let after = 0;
25080
+ for (; ; ) {
25081
+ const rows = page.all(after, label, pageSize);
25082
+ for (const r of rows) {
25083
+ yield {
25084
+ id: r.id,
25085
+ relPath: typeof r.relPath === "string" ? r.relPath : null,
25086
+ qname: typeof r.qname === "string" ? r.qname : null
25087
+ };
25088
+ }
25089
+ if (rows.length < pageSize) return;
25090
+ after = rows[rows.length - 1].rid;
25091
+ }
25092
+ }
25093
+ recentNodeIds(label, limit) {
25094
+ const rows = this.db.prepare("SELECT id FROM nodes WHERE label = ? AND valid_to IS NULL ORDER BY last_updated_at DESC LIMIT ?").all(label, Math.max(0, limit));
25095
+ return rows.map((r) => r.id);
25096
+ }
25097
+ hasLiveNodes() {
25098
+ const r = this.db.prepare("SELECT EXISTS(SELECT 1 FROM nodes WHERE valid_to IS NULL) AS e").get();
25099
+ return r.e === 1;
25100
+ }
25068
25101
  findNodeIdentitiesByLabel(label) {
25069
25102
  const rows = this.stmts.identitiesByLabel.all(label);
25070
25103
  return rows.map((r) => ({
@@ -25212,6 +25245,15 @@ var SqliteGraphStore = class {
25212
25245
  }
25213
25246
  labelFingerprint(labels) {
25214
25247
  if (labels.length === 0) return "0";
25248
+ if (labels.length === FINGERPRINT_INDEXED_LABELS.length && FINGERPRINT_INDEXED_LABELS.every((l) => labels.includes(l))) {
25249
+ const r2 = this.db.prepare(
25250
+ `SELECT COUNT(*) AS n, MAX(last_updated_at) AS t,
25251
+ MAX(COALESCE(last_reinforced_at_seq, created_at_seq, 0)) AS s
25252
+ FROM nodes INDEXED BY nodes_instance_fingerprint
25253
+ WHERE valid_to IS NULL AND label IN ('Problem','Solution','RootCause')`
25254
+ ).get();
25255
+ return `${r2.n}:${r2.t ?? 0}:${r2.s ?? 0}`;
25256
+ }
25215
25257
  const r = this.db.prepare(
25216
25258
  `SELECT COUNT(*) AS n, MAX(last_updated_at) AS t,
25217
25259
  MAX(COALESCE(last_reinforced_at_seq, created_at_seq, 0)) AS s