@inerrata-corporation/errata 2.0.22-dev.4635 → 2.0.22-dev.4641

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.
@@ -18253,6 +18253,22 @@ var SqliteGraphStore = class {
18253
18253
  AND (t.id IS NULL OR t.valid_to IS NOT NULL)
18254
18254
  `).all();
18255
18255
  }
18256
+ strandedSymbolsForFiles(fileIds) {
18257
+ const out = [];
18258
+ for (let i = 0; i < fileIds.length; i += 500) {
18259
+ const slice = fileIds.slice(i, i + 500);
18260
+ if (slice.length === 0) continue;
18261
+ out.push(...this.db.prepare(`
18262
+ SELECT f.id AS fileId, e.to_id AS symbolId
18263
+ FROM nodes f CROSS JOIN edges e ON e.from_id = f.id
18264
+ LEFT JOIN nodes t ON t.id = e.to_id
18265
+ WHERE f.id IN (${slice.map(() => "?").join(",")}) AND f.label = 'File' AND f.valid_to IS NULL AND e.valid_to IS NULL
18266
+ AND e.type IN ('DEFINES', 'CONTAINS')
18267
+ AND (t.id IS NULL OR t.valid_to IS NOT NULL)
18268
+ `).all(...slice));
18269
+ }
18270
+ return out;
18271
+ }
18256
18272
  strandedEdges() {
18257
18273
  return countStrandedEdges(this.db);
18258
18274
  }
@@ -21754,7 +21770,7 @@ function runPass(msg) {
21754
21770
  return revisitContradictedPrinciples(shared, msg.ts ?? Date.now());
21755
21771
  case "strandedCount": {
21756
21772
  if (!msg.castaliaPath) throw new Error("strandedCount: castaliaPath required");
21757
- return findStrandedSymbols(storeAt(msg.castaliaPath)).length;
21773
+ return findStrandedSymbols(storeAt(msg.castaliaPath));
21758
21774
  }
21759
21775
  case "drainContext": {
21760
21776
  if (!msg.castaliaPath) throw new Error("drainContext: castaliaPath required");
package/errata.mjs CHANGED
@@ -21105,6 +21105,22 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21105
21105
  AND (t.id IS NULL OR t.valid_to IS NOT NULL)
21106
21106
  `).all();
21107
21107
  }
21108
+ strandedSymbolsForFiles(fileIds) {
21109
+ const out2 = [];
21110
+ for (let i2 = 0; i2 < fileIds.length; i2 += 500) {
21111
+ const slice = fileIds.slice(i2, i2 + 500);
21112
+ if (slice.length === 0) continue;
21113
+ out2.push(...this.db.prepare(`
21114
+ SELECT f.id AS fileId, e.to_id AS symbolId
21115
+ FROM nodes f CROSS JOIN edges e ON e.from_id = f.id
21116
+ LEFT JOIN nodes t ON t.id = e.to_id
21117
+ WHERE f.id IN (${slice.map(() => "?").join(",")}) AND f.label = 'File' AND f.valid_to IS NULL AND e.valid_to IS NULL
21118
+ AND e.type IN ('DEFINES', 'CONTAINS')
21119
+ AND (t.id IS NULL OR t.valid_to IS NOT NULL)
21120
+ `).all(...slice));
21121
+ }
21122
+ return out2;
21123
+ }
21108
21124
  strandedEdges() {
21109
21125
  return countStrandedEdges(this.db);
21110
21126
  }
@@ -35651,7 +35667,8 @@ function readContextCard(root, hash2) {
35651
35667
  }
35652
35668
  function openProblemCount(store, now = Date.now()) {
35653
35669
  const hit = openProblemsCache.get(store);
35654
- if (hit && now - hit.at < OPEN_PROBLEMS_TTL_MS) return hit.count;
35670
+ const seq = store.changeSeq?.(OPEN_PROBLEM_SCOPES);
35671
+ if (hit && (seq !== void 0 ? hit.seq === seq : now - hit.at < OPEN_PROBLEMS_TTL_MS)) return hit.count;
35655
35672
  let count = 0;
35656
35673
  const labels = ["Problem", "Vulnerability"];
35657
35674
  const plain = store.selectNodes({
@@ -35676,10 +35693,10 @@ function openProblemCount(store, now = Date.now()) {
35676
35693
  for (const p of [...plain, ...qualified]) {
35677
35694
  if (live(p) && !p.attrs["resolvedAt"] && !isConstraintProblem(p)) count++;
35678
35695
  }
35679
- openProblemsCache.set(store, { at: now, count });
35696
+ openProblemsCache.set(store, { at: now, count, ...seq !== void 0 ? { seq } : {} });
35680
35697
  return count;
35681
35698
  }
35682
- var CONTEXT_CARD_FILE, OPEN_PROBLEMS_TTL_MS, openProblemsCache, OPEN_PROBLEM_ATTRS, HISTORY_QUALITY_READ_KEYS;
35699
+ var CONTEXT_CARD_FILE, OPEN_PROBLEMS_TTL_MS, openProblemsCache, OPEN_PROBLEM_SCOPES, OPEN_PROBLEM_ATTRS, HISTORY_QUALITY_READ_KEYS;
35683
35700
  var init_recall_signal = __esm({
35684
35701
  "src/recall-signal.ts"() {
35685
35702
  "use strict";
@@ -35689,6 +35706,7 @@ var init_recall_signal = __esm({
35689
35706
  CONTEXT_CARD_FILE = "context-card.json";
35690
35707
  OPEN_PROBLEMS_TTL_MS = 6e4;
35691
35708
  openProblemsCache = /* @__PURE__ */ new WeakMap();
35709
+ OPEN_PROBLEM_SCOPES = [nodeScope("Problem"), nodeScope("Vulnerability")];
35692
35710
  OPEN_PROBLEM_ATTRS = [
35693
35711
  "resolvedAt",
35694
35712
  "kind",
@@ -47120,14 +47138,31 @@ function standingStrandedCount(store) {
47120
47138
  try {
47121
47139
  const last = JSON.parse(store.getMeta?.(STRANDED_KEY) ?? "null");
47122
47140
  if (!last) return null;
47123
- if ((store.changeSeq?.() ?? Infinity) - last.seq > STRANDED_WINDOW_MAX) return null;
47124
- return store.lineageChangedFiles?.(last.seq)?.length === 0 ? last.count : null;
47141
+ const seq = store.changeSeq?.();
47142
+ if ((seq ?? Infinity) - last.seq > STRANDED_WINDOW_MAX) return null;
47143
+ const changed = store.lineageChangedFiles?.(last.seq);
47144
+ if (!changed) return null;
47145
+ if (changed.length === 0) return last.count;
47146
+ if (!last.byFile || !store.strandedSymbolsForFiles || seq === void 0) return null;
47147
+ const byFile = { ...last.byFile };
47148
+ for (const f of changed) delete byFile[f];
47149
+ for (const s of store.strandedSymbolsForFiles(changed)) byFile[s.fileId] = (byFile[s.fileId] ?? 0) + 1;
47150
+ const count = Object.values(byFile).reduce((a, b) => a + b, 0);
47151
+ store.setMeta?.(STRANDED_KEY, JSON.stringify({ seq, count, byFile }));
47152
+ return count;
47125
47153
  } catch {
47126
47154
  return null;
47127
47155
  }
47128
47156
  }
47129
- function recordStrandedCount(store, seq, count) {
47130
- if (seq !== void 0) store.setMeta?.(STRANDED_KEY, JSON.stringify({ seq, count }));
47157
+ function recordStrandedCount(store, seq, stranded) {
47158
+ if (seq === void 0) return;
47159
+ if (typeof stranded === "number") {
47160
+ store.setMeta?.(STRANDED_KEY, JSON.stringify({ seq, count: stranded }));
47161
+ return;
47162
+ }
47163
+ const byFile = {};
47164
+ for (const s of stranded) byFile[s.fileId] = (byFile[s.fileId] ?? 0) + 1;
47165
+ store.setMeta?.(STRANDED_KEY, JSON.stringify({ seq, count: stranded.length, byFile }));
47131
47166
  }
47132
47167
  function reapOrphanedFiles(store, rootPath, workspaceId2, ignoreDirs = []) {
47133
47168
  const ignores = /* @__PURE__ */ new Set([...DEFAULT_IGNORES, ...ignoreDirs]);
@@ -80197,7 +80232,7 @@ import { readFileSync as readFileSync18, renameSync as renameSync3, statSync as
80197
80232
  import { fileURLToPath as fileURLToPath2 } from "node:url";
80198
80233
  import { dirname as dirname11, join as join22 } from "node:path";
80199
80234
  function snapshotReusable(held, want) {
80200
- return held !== null && held.key === want.key && held.atCommit === want.atCommit && held.workingFile === want.workingFile;
80235
+ return held !== null && held.key === want.key && held.atCommit === want.atCommit;
80201
80236
  }
80202
80237
  var codeIdentityMemo;
80203
80238
  function snapshotCodeIdentity(version3) {
@@ -86859,7 +86894,7 @@ function createWatchBreaker(deps) {
86859
86894
  }
86860
86895
 
86861
86896
  // src/engine.ts
86862
- var DAEMON_VERSION = true ? "2.0.22-dev.4635" : "2.0.0-alpha.0";
86897
+ var DAEMON_VERSION = true ? "2.0.22-dev.4641" : "2.0.0-alpha.0";
86863
86898
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
86864
86899
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
86865
86900
  var GIT_OP_MUTE_MS = 4e3;
@@ -87372,10 +87407,18 @@ function createWorkspaceEngine(opts) {
87372
87407
  };
87373
87408
  const changes = store.changeSeq ? store.changeSeq() - store.changeSeq([EXPOSURE_SCOPE]) : store.changeStamp();
87374
87409
  const { id, name: name2, stack, languages, domains, goals } = snapOpts.profile;
87375
- const snapshotKey = `${snapshotCodeIdentity(DAEMON_VERSION)}|${changes}|${JSON.stringify({ ...snapOpts, profile: { id, name: name2, stack, languages, domains, goals } })}`;
87410
+ const { workingFile: _workingFile, ...keyedOpts } = snapOpts;
87411
+ const snapshotKey = `${snapshotCodeIdentity(DAEMON_VERSION)}|${changes}|${JSON.stringify({ ...keyedOpts, profile: { id, name: name2, stack, languages, domains, goals } })}`;
87376
87412
  lastWorkerSnapshot ??= loadHeldSnapshot(heldSnapshotPath(paths.configDir));
87377
87413
  const held = lastWorkerSnapshot;
87378
- const prebuilt = snapshotReusable(held, { key: snapshotKey, atCommit: snapOpts.atCommit, workingFile: snapOpts.workingFile }) ? structuredClone(held.snapshot) : passWorker ? await passWorker.snapshot(snapOpts).then((snapshot2) => {
87414
+ const withWorkingFile = (snap, builtFor, want, atCommit) => {
87415
+ if (builtFor === want) return snap;
87416
+ const slice = want ? sliceForFile(store, want, atCommit) : void 0;
87417
+ if (slice) snap.workingFile = slice;
87418
+ else delete snap.workingFile;
87419
+ return snap;
87420
+ };
87421
+ const prebuilt = snapshotReusable(held, { key: snapshotKey, atCommit: snapOpts.atCommit }) ? withWorkingFile(structuredClone(held.snapshot), held.workingFile, snapOpts.workingFile, snapOpts.atCommit) : passWorker ? await passWorker.snapshot(snapOpts).then((snapshot2) => {
87379
87422
  lastWorkerSnapshot = { key: snapshotKey, at: Date.now(), atCommit: snapOpts.atCommit, workingFile: snapOpts.workingFile, snapshot: structuredClone(snapshot2) };
87380
87423
  saveHeldSnapshot(heldSnapshotPath(paths.configDir), lastWorkerSnapshot);
87381
87424
  return snapshot2;
@@ -95312,17 +95355,17 @@ async function startMultiDaemon(opts = {}) {
95312
95355
  if (!strandedRefreshing.has(r.id)) {
95313
95356
  strandedRefreshing.add(r.id);
95314
95357
  const seq = store.changeSeq?.();
95315
- const finish = (count) => {
95316
- strandedCache.set(r.id, { at: Date.now(), count });
95358
+ const finish = (stranded) => {
95359
+ strandedCache.set(r.id, { at: Date.now(), count: stranded.length });
95317
95360
  strandedRefreshing.delete(r.id);
95318
- recordStrandedCount(store, seq, count);
95361
+ recordStrandedCount(store, seq, stranded);
95319
95362
  };
95320
95363
  if (consolidateWorker && r.engine.paths.castalia !== ":memory:") {
95321
- consolidateWorker.run({ kind: "strandedCount", sharedDbPath, castaliaPath: r.engine.paths.castalia }).then((n) => finish(Number(n))).catch(() => {
95364
+ consolidateWorker.run({ kind: "strandedCount", sharedDbPath, castaliaPath: r.engine.paths.castalia }).then((stranded) => finish(Array.isArray(stranded) ? stranded : [])).catch(() => {
95322
95365
  setImmediate(() => {
95323
95366
  const done = markPass(`stranded-scan:${r.entry.name}`);
95324
95367
  try {
95325
- finish(findStrandedSymbols(r.engine.store).length);
95368
+ finish(findStrandedSymbols(r.engine.store));
95326
95369
  } catch {
95327
95370
  strandedRefreshing.delete(r.id);
95328
95371
  } finally {
@@ -95334,7 +95377,7 @@ async function startMultiDaemon(opts = {}) {
95334
95377
  setImmediate(() => {
95335
95378
  const done = markPass(`stranded-scan:${r.entry.name}`);
95336
95379
  try {
95337
- finish(findStrandedSymbols(r.engine.store).length);
95380
+ finish(findStrandedSymbols(r.engine.store));
95338
95381
  } catch {
95339
95382
  strandedRefreshing.delete(r.id);
95340
95383
  } finally {
@@ -16650,6 +16650,22 @@ var SqliteGraphStore = class {
16650
16650
  AND (t.id IS NULL OR t.valid_to IS NOT NULL)
16651
16651
  `).all();
16652
16652
  }
16653
+ strandedSymbolsForFiles(fileIds) {
16654
+ const out = [];
16655
+ for (let i = 0; i < fileIds.length; i += 500) {
16656
+ const slice = fileIds.slice(i, i + 500);
16657
+ if (slice.length === 0) continue;
16658
+ out.push(...this.db.prepare(`
16659
+ SELECT f.id AS fileId, e.to_id AS symbolId
16660
+ FROM nodes f CROSS JOIN edges e ON e.from_id = f.id
16661
+ LEFT JOIN nodes t ON t.id = e.to_id
16662
+ WHERE f.id IN (${slice.map(() => "?").join(",")}) AND f.label = 'File' AND f.valid_to IS NULL AND e.valid_to IS NULL
16663
+ AND e.type IN ('DEFINES', 'CONTAINS')
16664
+ AND (t.id IS NULL OR t.valid_to IS NOT NULL)
16665
+ `).all(...slice));
16666
+ }
16667
+ return out;
16668
+ }
16653
16669
  strandedEdges() {
16654
16670
  return countStrandedEdges(this.db);
16655
16671
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.22-dev.4635",
3
+ "version": "2.0.22-dev.4641",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -27528,6 +27528,22 @@ var SqliteGraphStore = class {
27528
27528
  AND (t.id IS NULL OR t.valid_to IS NOT NULL)
27529
27529
  `).all();
27530
27530
  }
27531
+ strandedSymbolsForFiles(fileIds) {
27532
+ const out2 = [];
27533
+ for (let i2 = 0; i2 < fileIds.length; i2 += 500) {
27534
+ const slice = fileIds.slice(i2, i2 + 500);
27535
+ if (slice.length === 0) continue;
27536
+ out2.push(...this.db.prepare(`
27537
+ SELECT f.id AS fileId, e.to_id AS symbolId
27538
+ FROM nodes f CROSS JOIN edges e ON e.from_id = f.id
27539
+ LEFT JOIN nodes t ON t.id = e.to_id
27540
+ WHERE f.id IN (${slice.map(() => "?").join(",")}) AND f.label = 'File' AND f.valid_to IS NULL AND e.valid_to IS NULL
27541
+ AND e.type IN ('DEFINES', 'CONTAINS')
27542
+ AND (t.id IS NULL OR t.valid_to IS NOT NULL)
27543
+ `).all(...slice));
27544
+ }
27545
+ return out2;
27546
+ }
27531
27547
  strandedEdges() {
27532
27548
  return countStrandedEdges(this.db);
27533
27549
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "errata-context",
3
- "version": "2.0.22-dev.4635",
3
+ "version": "2.0.22-dev.4641",
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
- 97441718c0941fc62b087e0948ee625bb750c45d4938148ef8414d8ee4767bf4 .codex-plugin/plugin.json
1
+ ad67194b8165c74c1b5c411cac4f968637fe21c28e678627b77597b0b54c2339 .codex-plugin/plugin.json
2
2
  a7bebae9fb01c838d6c75fc784129e28be571233fa66ff5ee8039b574f35ccf7 assets/icon.png
3
3
  fc38b64e4df1e0b72c35764bef6c4c05430d97c9009ad0ecc8a9e75b4deab232 configure-local.mjs
4
4
  73dc6028b3b070257c7c31f252d11e55a9e096f4aed0f187f10cb21716e1e6d7 dist/client.js