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

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.
package/pass-worker.mjs CHANGED
@@ -17450,10 +17450,20 @@ function isReExportBarrel(relPath) {
17450
17450
  function graphExportMap(store2, workspaceId, relPath) {
17451
17451
  const out2 = /* @__PURE__ */ new Map();
17452
17452
  const fileId = fileNodeId(workspaceId, relPath);
17453
- if (!store2.getNode(fileId)) return out2;
17454
- for (const e of store2.outEdges(fileId, ["DEFINES"])) {
17455
- const n = store2.getNode(e.to);
17456
- if (!n || !EXPORTABLE_LABELS.has(n.label)) continue;
17453
+ if (store2.nodeLabels([fileId]).size === 0) return out2;
17454
+ const defines = store2.outEdges(fileId, ["DEFINES"]);
17455
+ const rows = /* @__PURE__ */ new Map();
17456
+ for (let i2 = 0; i2 < defines.length; i2 += 500) {
17457
+ for (const r of store2.selectNodes({
17458
+ labels: [...EXPORTABLE_LABELS],
17459
+ attrs: ["workspaceId", "name"],
17460
+ where: [{ idIn: defines.slice(i2, i2 + 500).map((e) => e.to) }],
17461
+ includeClosed: true
17462
+ })) rows.set(r.id, r);
17463
+ }
17464
+ for (const e of defines) {
17465
+ const n = rows.get(e.to);
17466
+ if (!n) continue;
17457
17467
  if (n.attrs["workspaceId"] !== workspaceId) continue;
17458
17468
  const name2 = n.attrs["name"] ?? n.description;
17459
17469
  if (!name2 || name2.startsWith("<") || name2.includes(":")) continue;
@@ -25938,6 +25948,35 @@ function wmCalibrationCells(store2) {
25938
25948
  return cells;
25939
25949
  }
25940
25950
 
25951
+ // ../../packages/local-graph/src/exposure-attrs.ts
25952
+ var EXPOSURE_ATTRS = [
25953
+ "shownCount",
25954
+ "evictedCount",
25955
+ "firstShownAt",
25956
+ "firstEvictedAt",
25957
+ "firstShownAtSeq",
25958
+ "lastShownAtSeq",
25959
+ "lastEvictedAtSeq",
25960
+ "shownSinceTouch",
25961
+ "shownSinceTouchAt",
25962
+ "exposureLog",
25963
+ "exposureLogStart",
25964
+ "exposureLogSince",
25965
+ "exposureFirst",
25966
+ "exposureTrimmed"
25967
+ ];
25968
+ var EXPOSURE_SCOPE = "exposure";
25969
+ var EXPOSURE_KEYS = new Set(EXPOSURE_ATTRS);
25970
+ var isExposureAttr = (key) => EXPOSURE_KEYS.has(key);
25971
+ function pickExposure(attrs) {
25972
+ const out2 = {};
25973
+ for (const k of EXPOSURE_ATTRS) if (attrs[k] !== void 0) out2[k] = attrs[k];
25974
+ return out2;
25975
+ }
25976
+ function exposureDocSql(key) {
25977
+ return `coalesce((SELECT x.attrs_json FROM node_exposure x WHERE x.id = nodes.id AND json_type(x.attrs_json, '$.${key}') IS NOT NULL), attrs_json)`;
25978
+ }
25979
+
25941
25980
  // ../../packages/local-graph/src/history-cache-floor.ts
25942
25981
  var HISTORY_CACHE_FLOOR_KEY = "history-cache-withdrawal-floor-3";
25943
25982
  function installHistoryCacheFloor(db) {
@@ -26222,25 +26261,6 @@ function textIndexReady(db) {
26222
26261
  return db.prepare("SELECT value FROM store_meta WHERE key = ?").get(TEXT_INDEX_KEY)?.["value"] === "installed";
26223
26262
  }
26224
26263
 
26225
- // ../../packages/local-graph/src/exposure-attrs.ts
26226
- var EXPOSURE_ATTRS = [
26227
- "shownCount",
26228
- "evictedCount",
26229
- "firstShownAt",
26230
- "firstEvictedAt",
26231
- "firstShownAtSeq",
26232
- "lastShownAtSeq",
26233
- "lastEvictedAtSeq",
26234
- "shownSinceTouch",
26235
- "shownSinceTouchAt",
26236
- "exposureLog",
26237
- "exposureLogStart",
26238
- "exposureLogSince",
26239
- "exposureFirst",
26240
- "exposureTrimmed"
26241
- ];
26242
- var EXPOSURE_SCOPE = "exposure";
26243
-
26244
26264
  // ../../packages/local-graph/src/change-feed.ts
26245
26265
  var CHANGE_FEED_KEY = "change-feed";
26246
26266
  var ANY_CHANGE = "*";
@@ -26304,6 +26324,16 @@ function readChangedNodeIds(db, sinceSeq, labels) {
26304
26324
  const rows = db.prepare(`SELECT DISTINCT id FROM change_log WHERE change_seq > ? AND kind = 'node' AND label IN (${labels.map(() => "?").join(",")})`).all(sinceSeq, ...labels);
26305
26325
  return rows.map((r) => r.id);
26306
26326
  }
26327
+ function readChangedAmong(db, sinceSeq, ids) {
26328
+ if (!changeLogCovers(db, sinceSeq)) return null;
26329
+ const out2 = [];
26330
+ for (let i2 = 0; i2 < ids.length; i2 += 500) {
26331
+ const chunk = ids.slice(i2, i2 + 500);
26332
+ const rows = db.prepare(`SELECT DISTINCT id FROM change_log WHERE change_seq > ? AND kind = 'node' AND id IN (${chunk.map(() => "?").join(",")})`).all(sinceSeq, ...chunk);
26333
+ for (const r of rows) out2.push(r.id);
26334
+ }
26335
+ return out2;
26336
+ }
26307
26337
  function readLineageChangedFiles(db, sinceSeq) {
26308
26338
  if (!changeLogCovers(db, sinceSeq)) return null;
26309
26339
  const rows = db.prepare(LINEAGE_CHANGED_FILES_SQL).all({ s: sinceSeq });
@@ -27037,8 +27067,11 @@ function compileNodeWhere(q) {
27037
27067
  }
27038
27068
  return { where: conds.join(" AND "), params };
27039
27069
  }
27040
- function attrValueSql(p) {
27041
- return `CASE json_type(attrs_json, ${p}) WHEN 'integer' THEN CAST(json_extract(attrs_json, ${p}) AS REAL) ELSE json_extract(attrs_json, ${p}) END`;
27070
+ function attrValueSql(p, doc = "attrs_json") {
27071
+ return `CASE json_type(${doc}, ${p}) WHEN 'integer' THEN CAST(json_extract(${doc}, ${p}) AS REAL) ELSE json_extract(${doc}, ${p}) END`;
27072
+ }
27073
+ function attrDocSql(key) {
27074
+ return isExposureAttr(key) ? exposureDocSql(key) : "attrs_json";
27042
27075
  }
27043
27076
  function nodeSelectFrom(q) {
27044
27077
  if (q.indexedBy) return `nodes INDEXED BY ${selectIndex(q.indexedBy)}`;
@@ -27051,7 +27084,7 @@ function nodeSelectFrom(q) {
27051
27084
  function compileNodeSelect(q) {
27052
27085
  const { where, params } = compileNodeWhere(q);
27053
27086
  const attrs = q.attrs ?? [];
27054
- const attrCols = attrs.map((k, i2) => `, ${attrValueSql(path(k))} AS a${i2}, json_type(attrs_json, ${path(k)}) AS t${i2}`).join("");
27087
+ const attrCols = attrs.map((k, i2) => `, ${attrValueSql(path(k), attrDocSql(k))} AS a${i2}, json_type(${attrDocSql(k)}, ${path(k)}) AS t${i2}`).join("");
27055
27088
  let order;
27056
27089
  if (q.orderBy && "column" in q.orderBy) {
27057
27090
  order = `${column(q.orderBy.column)} ${q.orderBy.desc ? "DESC" : "ASC"}, rowid`;
@@ -27168,6 +27201,7 @@ function localEdgeViolation(fromLabel, type, toLabel) {
27168
27201
  }
27169
27202
  var SCHEMA_VERSION = 6;
27170
27203
  var SESSION_REPAIR_KEY = "session-evidence-v1";
27204
+ var EXPOSURE_SIDE_KEY = "exposure-side-rows-1";
27171
27205
  var SESSION_EVIDENCE_PREDICATE = SESSION_EVIDENCE_FIELDS.map((field) => `instr(attrs_json, '${JSON.stringify(field)}') > 0`).join(" OR ");
27172
27206
  var SCHEMA_SQL = `
27173
27207
  CREATE TABLE IF NOT EXISTS schema_version (
@@ -27231,8 +27265,13 @@ CREATE TABLE IF NOT EXISTS nodes (
27231
27265
  );
27232
27266
 
27233
27267
  CREATE INDEX IF NOT EXISTS nodes_label ON nodes(label);
27234
- CREATE INDEX IF NOT EXISTS nodes_memory_tier ON nodes(memory_tier);
27235
- CREATE INDEX IF NOT EXISTS nodes_page_rank ON nodes(page_rank DESC);
27268
+ -- Dropped: no query plans through them (every prepared statement across the
27269
+ -- local-graph, indexer, context-writer, generalizer and daemon suites was
27270
+ -- planned with and without them, 2026-10-21), and each write that touches
27271
+ -- memory_tier / page_rank (every node upsert, every rescore) paid to maintain
27272
+ -- them. nodes_live_page_rank serves the live page-rank orderings.
27273
+ DROP INDEX IF EXISTS nodes_memory_tier;
27274
+ DROP INDEX IF EXISTS nodes_page_rank;
27236
27275
 
27237
27276
  CREATE TABLE IF NOT EXISTS edges (
27238
27277
  id TEXT PRIMARY KEY,
@@ -27254,7 +27293,10 @@ CREATE TABLE IF NOT EXISTS edges (
27254
27293
  FOREIGN KEY (to_id) REFERENCES nodes(id) ON DELETE CASCADE
27255
27294
  );
27256
27295
 
27257
- CREATE INDEX IF NOT EXISTS edges_from ON edges(from_id, type);
27296
+ -- Dropped: a prefix of edges_triple (from_id, type, to_id), which every
27297
+ -- from-side lookup and foreign-key check already plans through; it cost 47 MB
27298
+ -- and an index write on every edge write.
27299
+ DROP INDEX IF EXISTS edges_from;
27258
27300
  CREATE INDEX IF NOT EXISTS edges_to ON edges(to_id, type);
27259
27301
  CREATE INDEX IF NOT EXISTS edges_type ON edges(type);
27260
27302
  CREATE UNIQUE INDEX IF NOT EXISTS edges_triple ON edges(from_id, type, to_id);
@@ -27276,6 +27318,12 @@ CREATE INDEX IF NOT EXISTS nodes_live_local_community ON nodes(label, valid_to,
27276
27318
  -- Problems parked for the fold judge (read every settle boundary): ~1 in 5 open
27277
27319
  -- Problems; without it every live Problem was read to find them (56 MB).
27278
27320
  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;
27321
+ -- Render impressions, one row per node id (exposure-attrs.ts): a render bumps
27322
+ -- ~40 of these, which in the node row rewrote each whole row and its indexes.
27323
+ CREATE TABLE IF NOT EXISTS node_exposure (
27324
+ id TEXT PRIMARY KEY,
27325
+ attrs_json TEXT NOT NULL DEFAULT '{}'
27326
+ ) WITHOUT ROWID;
27279
27327
  -- The semantic-maintenance sweeps' own filters (closed, asked, hinted): each read
27280
27328
  -- every Problem row (~30 MB on a 1.1 GB store) for the few hundred that match,
27281
27329
  -- several times a run, and a run follows most writes (2026-10-20).
@@ -27439,6 +27487,7 @@ var SqliteGraphStore = class {
27439
27487
  constructor(opts) {
27440
27488
  this.db = openDatabase(opts.path);
27441
27489
  this.db.exec("PRAGMA cache_size = -32768");
27490
+ this.db.exec("PRAGMA mmap_size = 2147483648");
27442
27491
  this.db.exec(SCHEMA_SQL);
27443
27492
  const versionRow = this.db.prepare("SELECT version FROM schema_version LIMIT 1").get();
27444
27493
  if (!versionRow) {
@@ -27502,7 +27551,10 @@ var SqliteGraphStore = class {
27502
27551
  last_reinforced_at_seq = excluded.last_reinforced_at_seq,
27503
27552
  attrs_json = excluded.attrs_json
27504
27553
  `),
27505
- getNode: this.db.prepare("SELECT * FROM nodes WHERE id = ?"),
27554
+ getNode: this.db.prepare("SELECT nodes.*, (SELECT x.attrs_json FROM node_exposure x WHERE x.id = nodes.id) AS exposure_json FROM nodes WHERE id = ?"),
27555
+ exposureOf: this.db.prepare("SELECT attrs_json FROM node_exposure WHERE id = ?"),
27556
+ writeExposure: this.db.prepare("INSERT INTO node_exposure(id, attrs_json) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET attrs_json = excluded.attrs_json"),
27557
+ lastUpdatedOf: this.db.prepare("SELECT last_updated_at AS t, attrs_json FROM nodes WHERE id = ?"),
27506
27558
  labelOf: this.db.prepare("SELECT label FROM nodes WHERE id = ?"),
27507
27559
  nodeContent: this.db.prepare(
27508
27560
  "SELECT label, description, extraction_confidence, extraction_source, stability, valid_to, attrs_json FROM nodes WHERE id = ?"
@@ -27666,6 +27718,7 @@ var SqliteGraphStore = class {
27666
27718
  "INSERT INTO store_meta (key, value) VALUES ('ingest_seq', :v) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
27667
27719
  )
27668
27720
  };
27721
+ this.moveExposureToSideRows();
27669
27722
  const seqRow = this.db.prepare("SELECT value FROM store_meta WHERE key = 'ingest_seq'").get();
27670
27723
  this.ingestSeq = seqRow ? Number(seqRow.value) : 0;
27671
27724
  }
@@ -27726,21 +27779,35 @@ var SqliteGraphStore = class {
27726
27779
  `SELECT rowid AS rid, logical_id, version, valid_to, label, ${WM_CALIBRATION_KEYS.map((k, i2) => `${wmCounterSql(k)} AS c${i2}`).join(", ")}
27727
27780
  FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}`
27728
27781
  ).all();
27782
+ const exposed = this.db.prepare(
27783
+ `SELECT n.rowid AS rid, n.logical_id, n.version, n.valid_to, n.label, x.attrs_json AS exposure
27784
+ FROM node_exposure x JOIN nodes n ON n.id = x.id`
27785
+ ).all();
27729
27786
  const others = this.db.prepare(
27730
27787
  `SELECT rowid AS rid, logical_id, version, valid_to, label FROM nodes WHERE rowid IN (
27731
27788
  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})`
27789
+ WHERE n.logical_id IN (SELECT logical_id FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}
27790
+ UNION SELECT n2.logical_id FROM node_exposure x2 JOIN nodes n2 ON n2.id = x2.id)
27791
+ EXCEPT SELECT rowid FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}
27792
+ EXCEPT SELECT n3.rowid FROM node_exposure x3 JOIN nodes n3 ON n3.id = x3.id)`
27734
27793
  ).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) => {
27794
+ const byRid = /* @__PURE__ */ new Map();
27795
+ for (const r of [...counted, ...others]) {
27737
27796
  const attrs = {};
27738
27797
  WM_CALIBRATION_KEYS.forEach((k, i2) => {
27739
27798
  const v = r[`c${i2}`];
27740
27799
  if (typeof v === "number") attrs[k] = v;
27741
27800
  });
27742
- return { logicalId: r.logical_id, version: Number(r.version), validTo: r.valid_to ?? null, attrs };
27743
- });
27801
+ byRid.set(r.rid, { row: r, attrs });
27802
+ }
27803
+ for (const r of exposed) {
27804
+ const side = JSON.parse(r.exposure);
27805
+ const entry = byRid.get(r.rid) ?? { row: r, attrs: {} };
27806
+ for (const k of WM_CALIBRATION_KEYS) if (typeof side[k] === "number" && Number.isFinite(side[k])) entry.attrs[k] = side[k];
27807
+ byRid.set(r.rid, entry);
27808
+ }
27809
+ const order = new Map(labels.map((l, i2) => [l, i2]));
27810
+ return [...byRid.values()].filter(({ row }) => order.has(row.label)).sort((a, b) => order.get(a.row.label) - order.get(b.row.label) || a.row.rid - b.row.rid).map(({ row, attrs }) => ({ logicalId: row.logical_id, version: Number(row.version), validTo: row.valid_to ?? null, attrs }));
27744
27811
  }
27745
27812
  liveCommentsAttachedTo(name2) {
27746
27813
  return this.db.prepare(
@@ -27878,7 +27945,7 @@ var SqliteGraphStore = class {
27878
27945
  logical_id: node.logicalId ?? node.id,
27879
27946
  created_at_seq: node.createdAtSeq ?? null,
27880
27947
  last_reinforced_at_seq: node.lastReinforcedAtSeq ?? null,
27881
- attrs_json: JSON.stringify(node.attrs ?? {})
27948
+ attrs_json: this.nodeAttrsJson(node.id, node.attrs ?? {})
27882
27949
  });
27883
27950
  }
27884
27951
  /** mergeNode, unless the live row already holds this content. Content is what
@@ -27890,7 +27957,7 @@ var SqliteGraphStore = class {
27890
27957
  * whether it wrote. */
27891
27958
  mergeNodeIfChanged(node) {
27892
27959
  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;
27960
+ if (row && row.valid_to === null && row.label === node.label && row.description === node.description && row.extraction_confidence === node.extractionConfidence && row.stability === node.stability && !EXPOSURE_ATTRS.some((k) => node.attrs?.[k] !== void 0) && row.attrs_json === JSON.stringify(node.attrs ?? {})) return false;
27894
27961
  this.mergeNode(node);
27895
27962
  return true;
27896
27963
  }
@@ -27917,7 +27984,37 @@ var SqliteGraphStore = class {
27917
27984
  logical_id: patch.logicalId ?? null,
27918
27985
  created_at_seq: patch.createdAtSeq ?? null,
27919
27986
  last_reinforced_at_seq: patch.lastReinforcedAtSeq ?? null,
27920
- attrs_json: patch.attrs ? JSON.stringify(patch.attrs) : null
27987
+ attrs_json: patch.attrs ? this.nodeAttrsJson(id, patch.attrs) : null
27988
+ });
27989
+ }
27990
+ /**
27991
+ * The node-row attrs of a write, after handing any exposure keys to the side
27992
+ * row (exposure-attrs.ts). `getNode` returns exposure merged into attrs, so a
27993
+ * read-modify-write carries it; kept in the row it would re-bloat the row and
27994
+ * drift from the side row. Unchanged exposure costs no write.
27995
+ */
27996
+ nodeAttrsJson(id, attrs) {
27997
+ if (!EXPOSURE_ATTRS.some((k) => attrs[k] !== void 0)) return JSON.stringify(attrs);
27998
+ const current = this.stmts.exposureOf.get(id);
27999
+ const next = JSON.stringify({ ...current ? JSON.parse(current.attrs_json) : {}, ...pickExposure(attrs) });
28000
+ if (current?.attrs_json !== next) this.stmts.writeExposure.run(id, next);
28001
+ const rest = { ...attrs };
28002
+ for (const k of EXPOSURE_ATTRS) delete rest[k];
28003
+ return JSON.stringify(rest);
28004
+ }
28005
+ /** One-time: exposure keys written to node rows before the side table existed
28006
+ * move to it. Only rows carrying a counter can carry exposure, and those are
28007
+ * what nodes_wm_counted holds, so this reads the index, not the table. */
28008
+ moveExposureToSideRows() {
28009
+ if (this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(EXPOSURE_SIDE_KEY)) return;
28010
+ const rows = this.db.prepare(`SELECT id, attrs_json FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}`).all();
28011
+ const move = this.db.prepare("UPDATE nodes SET attrs_json = ? WHERE id = ?");
28012
+ this.db.transaction(() => {
28013
+ for (const r of rows) {
28014
+ const stripped = this.nodeAttrsJson(r.id, JSON.parse(r.attrs_json));
28015
+ if (stripped !== r.attrs_json) move.run(stripped, r.id);
28016
+ }
28017
+ this.db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, 'done')").run(EXPOSURE_SIDE_KEY);
27921
28018
  });
27922
28019
  }
27923
28020
  /** Fast single-column writes for the nightly pass (avoid the 18-column COALESCE
@@ -28080,7 +28177,27 @@ var SqliteGraphStore = class {
28080
28177
  }
28081
28178
  getNode(id) {
28082
28179
  const row = this.stmts.getNode.get(id);
28083
- return row ? rowToNode(row) : null;
28180
+ if (!row) return null;
28181
+ const node = rowToNode(row);
28182
+ if (row.exposure_json) Object.assign(node.attrs, JSON.parse(row.exposure_json));
28183
+ return node;
28184
+ }
28185
+ /** A node's render exposure: the side row's keys over the node row's own
28186
+ * (exposure-attrs.ts), plus the node's `lastUpdatedAt` (the "shown since
28187
+ * touch" counter is relative to it) — without reading the node's attrs. */
28188
+ exposureOf(id) {
28189
+ const row = this.stmts.lastUpdatedOf.get(id);
28190
+ if (!row) return null;
28191
+ const side = this.stmts.exposureOf.get(id);
28192
+ return {
28193
+ attrs: { ...pickExposure(JSON.parse(row.attrs_json)), ...side ? JSON.parse(side.attrs_json) : {} },
28194
+ lastUpdatedAt: row.t
28195
+ };
28196
+ }
28197
+ /** Replace a node's exposure (only EXPOSURE_ATTRS keys belong here). Touches
28198
+ * neither the node row nor its indexes, nor the change feed. */
28199
+ writeExposure(id, attrs) {
28200
+ this.stmts.writeExposure.run(id, JSON.stringify(attrs));
28084
28201
  }
28085
28202
  nodeLabels(ids) {
28086
28203
  const width = NODE_LABELS_CHUNK;
@@ -28131,7 +28248,7 @@ var SqliteGraphStore = class {
28131
28248
  findNodeStampsByLabel(label, attrKey) {
28132
28249
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attrKey)) throw new Error(`findNodeStampsByLabel: bad attr key ${attrKey}`);
28133
28250
  const rows = this.db.prepare(
28134
- `SELECT id, created_at, created_at_seq, json_type(attrs_json, '$.${attrKey}') IS NOT NULL AS has,
28251
+ `SELECT id, created_at, created_at_seq, json_type(${attrDocSql(attrKey)}, '$.${attrKey}') IS NOT NULL AS has,
28135
28252
  COALESCE(json_extract(attrs_json, '$.source') = 'cloud', 0) AS cloud
28136
28253
  FROM nodes WHERE label = ? AND valid_to IS NULL`
28137
28254
  ).all(label);
@@ -28148,7 +28265,7 @@ var SqliteGraphStore = class {
28148
28265
  const keys = [...new Set(attrKeys)];
28149
28266
  const rows = this.db.prepare(
28150
28267
  `SELECT id, created_at, created_at_seq,
28151
- ${keys.map((k, i2) => `json_type(attrs_json, '$.${k}') IS NOT NULL AS h${i2}`).join(", ")}${keys.length ? "," : ""}
28268
+ ${keys.map((k, i2) => `json_type(${attrDocSql(k)}, '$.${k}') IS NOT NULL AS h${i2}`).join(", ")}${keys.length ? "," : ""}
28152
28269
  COALESCE(json_extract(attrs_json, '$.source') = 'cloud', 0) AS cloud
28153
28270
  FROM nodes WHERE label = ? AND valid_to IS NULL`
28154
28271
  ).all(label);
@@ -28408,6 +28525,9 @@ var SqliteGraphStore = class {
28408
28525
  changedNodeIds(sinceSeq, labels) {
28409
28526
  return readChangedNodeIds(this.db, sinceSeq, labels);
28410
28527
  }
28528
+ changedAmong(sinceSeq, ids) {
28529
+ return readChangedAmong(this.db, sinceSeq, ids);
28530
+ }
28411
28531
  pruneChangeLog() {
28412
28532
  return pruneChangeLog(this.db);
28413
28533
  }
@@ -31097,6 +31217,91 @@ var AGENTS_POINTER_BODY = [
31097
31217
  // ../../packages/context-writer/src/render.ts
31098
31218
  init_src2();
31099
31219
  init_src();
31220
+
31221
+ // ../../packages/context-writer/src/snapshot-inputs.ts
31222
+ var SNAPSHOT_NODE_LABELS = [
31223
+ "Problem",
31224
+ "Vulnerability",
31225
+ "Solution",
31226
+ "Pattern",
31227
+ "Technique",
31228
+ "AntiPattern",
31229
+ "Language",
31230
+ "Package",
31231
+ "Tool"
31232
+ ];
31233
+ var SNAPSHOT_EDGE_TYPES = [
31234
+ "ANCHORED_AT",
31235
+ "CAUSED_BY",
31236
+ "CONCERNS",
31237
+ "DEPENDS_ON",
31238
+ "EVIDENCED_BY",
31239
+ "MANIFESTED_IN",
31240
+ "OCCURS_IN",
31241
+ "PERTAIN_TO",
31242
+ "REGRESSION_OF",
31243
+ "SOLVED_BY"
31244
+ ];
31245
+ var SNAPSHOT_READ_GUARD = typeof process !== "undefined" && Boolean(process.env["VITEST"]);
31246
+ var LABELS = new Set(SNAPSHOT_NODE_LABELS);
31247
+ var EDGES = new Set(SNAPSHOT_EDGE_TYPES);
31248
+ function guardSnapshotReads(store2) {
31249
+ const fail = (what) => {
31250
+ throw new Error(`buildSnapshot read ${what}, outside SNAPSHOT_NODE_LABELS/SNAPSHOT_EDGE_TYPES (snapshot-inputs.ts)`);
31251
+ };
31252
+ const label = (l, via) => {
31253
+ if (typeof l === "string" && !LABELS.has(l)) fail(`label ${l} via ${via}`);
31254
+ };
31255
+ const types = (t, via) => {
31256
+ if (!Array.isArray(t) || t.length === 0) fail(`every edge type via ${via}`);
31257
+ for (const x of t) if (!EDGES.has(String(x))) fail(`edge type ${String(x)} via ${via}`);
31258
+ };
31259
+ const nodes = (r, via) => {
31260
+ for (const n of Array.isArray(r) ? r : r ? [r] : []) label(n.label, via);
31261
+ return r;
31262
+ };
31263
+ const allowed = {
31264
+ getNode: (id) => nodes(store2.getNode(id), "getNode"),
31265
+ selectNodes: (q) => {
31266
+ for (const l of q.labels) label(l, "selectNodes");
31267
+ return store2.selectNodes(q);
31268
+ },
31269
+ countNodes: (q) => {
31270
+ for (const l of q.labels) label(l, "countNodes");
31271
+ return store2.countNodes(q);
31272
+ },
31273
+ findLiveNodesByAttr: (l, ...rest) => {
31274
+ label(l, "findLiveNodesByAttr");
31275
+ return store2.findLiveNodesByAttr(l, ...rest);
31276
+ },
31277
+ outEdges: (id, t) => {
31278
+ types(t, "outEdges");
31279
+ return store2.outEdges(id, t);
31280
+ },
31281
+ inEdges: (id, t) => {
31282
+ types(t, "inEdges");
31283
+ return store2.inEdges(id, t);
31284
+ },
31285
+ inDegreeByType: (t) => {
31286
+ types(t, "inDegreeByType");
31287
+ return store2.inDegreeByType(t);
31288
+ },
31289
+ nodesAsOfWhereTrue: (asOf, attr) => {
31290
+ if (attr !== "revisit") fail(`nodesAsOfWhereTrue(${String(attr)})`);
31291
+ return store2.nodesAsOfWhereTrue(asOf, attr);
31292
+ }
31293
+ };
31294
+ return new Proxy(store2, {
31295
+ get(target, prop) {
31296
+ if (typeof prop === "string" && prop in allowed) return allowed[prop];
31297
+ const v = Reflect.get(target, prop, target);
31298
+ if (typeof v === "function") return () => fail(`store.${String(prop)}()`);
31299
+ return v;
31300
+ }
31301
+ });
31302
+ }
31303
+
31304
+ // ../../packages/context-writer/src/render.ts
31100
31305
  var MOTIF_BAND = 10;
31101
31306
  var MOTIF_POOL = 1e3;
31102
31307
  function rankMotifs(pool, signal, k = MOTIF_BAND) {
@@ -31117,6 +31322,8 @@ var PROBLEM_BAND_ATTRS = [
31117
31322
  ...HISTORY_POLICY_ATTRS
31118
31323
  ];
31119
31324
  function buildSnapshot(opts) {
31325
+ const codeStore = opts.store;
31326
+ if (SNAPSHOT_READ_GUARD) opts = { ...opts, store: guardSnapshotReads(opts.store) };
31120
31327
  const hydrate = (id) => opts.store.getNode(id);
31121
31328
  const problems = opts.store.selectNodes({ labels: ["Problem", "Vulnerability"], attrs: PROBLEM_BAND_ATTRS }).filter((p) => p.attrs["mergedInto"] === void 0 && passiveAllowed(p, opts.atCommit));
31122
31329
  const stillOpen = problems.filter((p) => p.attrs["resolvedAt"] == null);
@@ -31152,7 +31359,7 @@ function buildSnapshot(opts) {
31152
31359
  const recentResolved = resolvedPool.slice(0, RECENT_RESOLVED);
31153
31360
  const motifPool = opts.store.selectNodes({ labels: ["Pattern", "Technique", "AntiPattern"], attrs: ["missedRuns", ...HISTORY_POLICY_ATTRS] }).filter((n) => passiveAllowed(n, opts.atCommit)).filter((n) => !(Number(n.attrs["missedRuns"] ?? 0) > 0)).sort((a, b) => b.pageRank - a.pageRank).slice(0, MOTIF_POOL).map((r) => hydrate(r.id)).map((n) => n.embedding.length > 0 ? { ...n, embedding: [] } : n);
31154
31361
  const motifs = rankMotifs(motifPool, void 0);
31155
- const workingFile = opts.workingFile ? sliceForFile(opts.store, opts.workingFile, opts.atCommit) : void 0;
31362
+ const workingFile = opts.workingFile ? sliceForFile(codeStore, opts.workingFile, opts.atCommit) : void 0;
31156
31363
  const byEpisode = /* @__PURE__ */ new Map();
31157
31364
  for (const sol of opts.store.findLiveNodesByAttr("Solution", "enrichmentPending", 1)) {
31158
31365
  if (!historyReadPolicy(sol, { mode: "passive", atCommit: opts.atCommit }).allow) continue;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "errata-context",
3
- "version": "2.0.22-dev.4669",
3
+ "version": "2.0.22-dev.4676",
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
- 235e69b11fb75b5854bbe0e3e025fdad29c29d8b5b34159bd8270bfb8b235b0b .codex-plugin/plugin.json
1
+ aed45b59cddfbffc63052b049122c7b5303eb3f921379af8ac2e969ded158d58 .codex-plugin/plugin.json
2
2
  a7bebae9fb01c838d6c75fc784129e28be571233fa66ff5ee8039b574f35ccf7 assets/icon.png
3
3
  fc38b64e4df1e0b72c35764bef6c4c05430d97c9009ad0ecc8a9e75b4deab232 configure-local.mjs
4
4
  73dc6028b3b070257c7c31f252d11e55a9e096f4aed0f187f10cb21716e1e6d7 dist/client.js