@inerrata-corporation/errata 2.0.3 → 2.0.4-dev.1806

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/errata.mjs CHANGED
@@ -25280,8 +25280,8 @@ var init_config = __esm({
25280
25280
  "use strict";
25281
25281
  init_paths();
25282
25282
  LEGACY_CLOUD_URL = "https://inerrata-gateway.onrender.com";
25283
- DEFAULT_CLOUD_URL = true ? "https://inerrata.dev" : DEV_CLOUD_URL;
25284
- DEFAULT_UPDATE_CHANNEL = true ? "latest" : "dev";
25283
+ DEFAULT_CLOUD_URL = true ? "https://dev.inerrata.dev" : DEV_CLOUD_URL;
25284
+ DEFAULT_UPDATE_CHANNEL = false ? "latest" : "dev";
25285
25285
  }
25286
25286
  });
25287
25287
 
@@ -31782,6 +31782,10 @@ function fileHasStrandedSymbol(store, fileId) {
31782
31782
  }
31783
31783
  return false;
31784
31784
  }
31785
+ function fileIndexUnsettled(store, fnode) {
31786
+ if (fnode.attrs["symbolCount"] === 0 || typeof fnode.attrs["indexSkipped"] === "string") return false;
31787
+ return store.outEdges(fnode.id, ["DEFINES", "CONTAINS"]).length === 0;
31788
+ }
31785
31789
  function symbolNodeId(workspaceId2, relPath, qname, kind) {
31786
31790
  return `${kind}_${sha(
31787
31791
  workspaceId2 + ":" + relPath.replace(/\\/g, "/") + ":" + qname
@@ -31897,7 +31901,7 @@ async function incrementalReindex(store, rootPath, workspaceId2, changedAbsPaths
31897
31901
  }
31898
31902
  fileHashes.set(rel, h);
31899
31903
  const fnode = store.getNode(fileNodeId(workspaceId2, rel));
31900
- if (fnode && fnode.attrs["contentHash"] === h && !fileHasStrandedSymbol(store, fnode.id)) {
31904
+ if (fnode && fnode.attrs["contentHash"] === h && !fileHasStrandedSymbol(store, fnode.id) && !fileIndexUnsettled(store, fnode)) {
31901
31905
  store.updateNode(fnode.id, { lastUpdatedAt: t });
31902
31906
  changedRelPaths.delete(rel);
31903
31907
  } else if (!fnode || fnode.attrs["contentHash"] !== h) {
@@ -32238,7 +32242,8 @@ async function runIndexer(store, opts) {
32238
32242
  parentId = fid2;
32239
32243
  }
32240
32244
  const fid = fileNodeId(opts.workspaceId, f.relPath);
32241
- upsertFile(store, fid, f, opts.workspaceId);
32245
+ const skip = !f.provider ? "no-provider" : f.size > maxBytes ? "oversize" : void 0;
32246
+ upsertFile(store, fid, f, opts.workspaceId, skip);
32242
32247
  report.nodesEmitted++;
32243
32248
  upsertEdge(store, parentId, "CONTAINS", fid, 1);
32244
32249
  report.edgesEmitted++;
@@ -32283,6 +32288,7 @@ async function runIndexer(store, opts) {
32283
32288
  symbols = f.provider.extractSymbols(source, f.absPath);
32284
32289
  } catch {
32285
32290
  report.filesSkipped++;
32291
+ stampIndexOutcome(store, fileNodeId(opts.workspaceId, f.relPath), { indexSkipped: "parse-error" });
32286
32292
  continue;
32287
32293
  }
32288
32294
  reExports = scanReExports(source);
@@ -32684,6 +32690,12 @@ async function runIndexer(store, opts) {
32684
32690
  }
32685
32691
  }
32686
32692
  perf("flush cross-file edges");
32693
+ for (const relPath of symbolsByFile.keys()) {
32694
+ const fid = fileNodeId(opts.workspaceId, relPath);
32695
+ if (store.outEdges(fid, ["DEFINES", "CONTAINS"]).length === 0) {
32696
+ stampIndexOutcome(store, fid, { symbolCount: 0 });
32697
+ }
32698
+ }
32687
32699
  report.durationMs = Date.now() - started2;
32688
32700
  indexNow = null;
32689
32701
  return report;
@@ -32792,7 +32804,7 @@ function gitListFiles(root, ignores, providers) {
32792
32804
  }
32793
32805
  return out2;
32794
32806
  }
32795
- function upsertFile(store, id, f, workspaceId2) {
32807
+ function upsertFile(store, id, f, workspaceId2, skip) {
32796
32808
  const now = nowTs();
32797
32809
  let contentHash;
32798
32810
  try {
@@ -32822,11 +32834,17 @@ function upsertFile(store, id, f, workspaceId2) {
32822
32834
  size: f.size,
32823
32835
  language: f.language ?? null,
32824
32836
  workspaceId: workspaceId2,
32825
- ...contentHash ? { contentHash } : {}
32837
+ ...contentHash ? { contentHash } : {},
32838
+ ...skip ? { indexSkipped: skip } : {}
32826
32839
  }
32827
32840
  };
32828
32841
  store.mergeNode(node2);
32829
32842
  }
32843
+ function stampIndexOutcome(store, fileId, outcome) {
32844
+ const fnode = store.getNode(fileId);
32845
+ if (!fnode) return;
32846
+ store.updateNode(fileId, { attrs: { ...fnode.attrs, ...outcome } });
32847
+ }
32830
32848
  function upsertFolder(store, id, name2, workspaceId2, relPath) {
32831
32849
  const now = nowTs();
32832
32850
  const node2 = {
@@ -40188,6 +40206,14 @@ var init_src11 = __esm({
40188
40206
  import { execFileSync as execFileSync2 } from "node:child_process";
40189
40207
  import { readdirSync as readdirSync4, statSync as statSync3 } from "node:fs";
40190
40208
  import { join as join11, relative as relative3, sep as sep2 } from "node:path";
40209
+ function indexerWouldScan(rel) {
40210
+ for (const seg of rel.split(/[\\/]/)) {
40211
+ if (seg.length === 0) continue;
40212
+ if (DEFAULT_IGNORES.has(seg)) return false;
40213
+ if (seg.startsWith(".") && seg !== ".") return false;
40214
+ }
40215
+ return true;
40216
+ }
40191
40217
  function gitSourceFiles(root) {
40192
40218
  let stdout;
40193
40219
  try {
@@ -40201,7 +40227,7 @@ function gitSourceFiles(root) {
40201
40227
  }
40202
40228
  const out2 = [];
40203
40229
  for (const rel of stdout.split("\0")) {
40204
- if (!rel || !SOURCE_RE.test(rel)) continue;
40230
+ if (!rel || !SOURCE_RE.test(rel) || !indexerWouldScan(rel)) continue;
40205
40231
  const abs = join11(root, rel);
40206
40232
  if (IGNORED.test(abs)) continue;
40207
40233
  out2.push(abs);
@@ -40219,6 +40245,7 @@ function* walkSource(dir) {
40219
40245
  const name2 = String(e.name);
40220
40246
  const full = join11(dir, name2);
40221
40247
  if (IGNORED.test(full)) continue;
40248
+ if (!indexerWouldScan(name2)) continue;
40222
40249
  if (e.isDirectory()) yield* walkSource(full);
40223
40250
  else if (SOURCE_RE.test(name2)) yield full;
40224
40251
  }
@@ -40250,7 +40277,8 @@ async function findStaleFiles(store, rootPath, workspaceId2) {
40250
40277
  } else {
40251
40278
  const edges = store.outEdges(fnode.id, ["DEFINES", "CONTAINS"]);
40252
40279
  if (edges.length === 0) {
40253
- stale.push(abs);
40280
+ const converged = fnode.attrs["symbolCount"] === 0 || typeof fnode.attrs["indexSkipped"] === "string";
40281
+ if (!converged) stale.push(abs);
40254
40282
  } else {
40255
40283
  const targets = edges.map((e) => e.to);
40256
40284
  pending.push({ abs, targets });
@@ -57548,7 +57576,14 @@ var census = {
57548
57576
  since: Date.now(),
57549
57577
  breakerRecycles: 0,
57550
57578
  breakerQuarantined: [],
57551
- topOffender: null
57579
+ topOffender: null,
57580
+ liveWatchers: 0
57581
+ };
57582
+ var noteWatcherOpened = () => {
57583
+ census.liveWatchers = (census.liveWatchers ?? 0) + 1;
57584
+ };
57585
+ var noteWatcherClosed = () => {
57586
+ census.liveWatchers = Math.max(0, (census.liveWatchers ?? 0) - 1);
57552
57587
  };
57553
57588
  var QUARANTINE_LIST_CAP = 8;
57554
57589
  var noteWatchDispatched = () => void census.dispatched++;
@@ -57567,19 +57602,33 @@ var noteWindowOffender = (path2, count, windowMs) => {
57567
57602
  function watchCensus() {
57568
57603
  return { ...census };
57569
57604
  }
57570
- function watchCensusLine(c = watchCensus()) {
57605
+ var WATCH_STORM_RATE_PER_SEC = 20;
57606
+ function ago(ms) {
57607
+ const s = Math.max(0, Math.round(ms / 1e3));
57608
+ if (s < 60) return `${s}s ago`;
57609
+ const m = Math.round(s / 60);
57610
+ if (m < 60) return `${m}m ago`;
57611
+ return `${Math.floor(m / 60)}h ${m % 60}m ago`;
57612
+ }
57613
+ function watchCensusLine(c = watchCensus(), now = Date.now()) {
57571
57614
  if (c.dispatched === 0 && c.emitted === 0) return null;
57572
- const mins = Math.max(1, Math.round((Date.now() - c.since) / 6e4));
57573
- const perMin = Math.round(c.dispatched / mins);
57615
+ const elapsedSec = Math.max(1, (now - c.since) / 1e3);
57616
+ const perMin = Math.round(c.dispatched / Math.max(1, elapsedSec / 60));
57574
57617
  let base = `${c.dispatched} OS event(s) \u2192 ${c.logged} logged (${perMin}/min, ${c.rejected} rejected here)`;
57575
57618
  if (c.breakerRecycles > 0 || c.breakerQuarantined.length > 0) {
57576
57619
  const q = c.breakerQuarantined.length > 0 ? `, ${c.breakerQuarantined.length} quarantined` : "";
57577
57620
  base += ` \xB7 breaker: ${c.breakerRecycles} recycled${q}`;
57578
57621
  }
57579
- if (c.dispatched > 500 && c.dispatched / Math.max(c.logged, 1) > 1e4) {
57580
- const top = c.topOffender ? ` \u2014 top: ${c.topOffender.path} (${c.topOffender.count}/${Math.round(c.topOffender.windowMs / 1e3)}s)` : "";
57622
+ const top = c.topOffender ? ` \u2014 top: ${c.topOffender.path} (${c.topOffender.count}/${Math.round(c.topOffender.windowMs / 1e3)}s, ${ago(now - c.topOffender.at)})` : "";
57623
+ const cold = c.liveWatchers === 0;
57624
+ if (cold) return `${base} \xB7 no live watcher (all workspaces cold; lifetime totals of retired engines)${top}`;
57625
+ const noOutput = c.dispatched > 500 && c.dispatched / Math.max(c.logged, 1) > 1e4;
57626
+ if (noOutput && c.dispatched / elapsedSec >= WATCH_STORM_RATE_PER_SEC) {
57581
57627
  return `${base} \u26A0 watcher is doing work with NO output \u2014 a watch target likely contains an ignored tree${top}`;
57582
57628
  }
57629
+ if (noOutput) {
57630
+ return `${base} \xB7 no-output dispatch at a low rate = last-access echo of bulk reads (harmless)${top}`;
57631
+ }
57583
57632
  return base;
57584
57633
  }
57585
57634
 
@@ -57750,7 +57799,7 @@ function createWatchBreaker(deps) {
57750
57799
  }
57751
57800
 
57752
57801
  // src/engine.ts
57753
- var DAEMON_VERSION = true ? "2.0.3" : "2.0.0-alpha.0";
57802
+ var DAEMON_VERSION = true ? "2.0.4-dev.1806" : "2.0.0-alpha.0";
57754
57803
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
57755
57804
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
57756
57805
  var GIT_OP_MUTE_MS = 4e3;
@@ -57895,6 +57944,7 @@ function createWorkspaceEngine(opts) {
57895
57944
  ignoreInitial: true,
57896
57945
  persistent: true
57897
57946
  });
57947
+ noteWatcherOpened();
57898
57948
  watchBreaker = createWatchBreaker({
57899
57949
  unwatch: (p) => void watcher?.unwatch(p),
57900
57950
  readd: (p) => void watcher?.add(p),
@@ -59963,7 +60013,10 @@ B: ${b}`,
59963
60013
  if (stopGit) stopGit();
59964
60014
  if (passWorker) await passWorker.stop();
59965
60015
  if (watchBreaker) watchBreaker.stop();
59966
- if (watcher) await watcher.close();
60016
+ if (watcher) {
60017
+ await watcher.close();
60018
+ noteWatcherClosed();
60019
+ }
59967
60020
  if (flushTimer) clearTimeout(flushTimer);
59968
60021
  for (const tmr of diagTimers.values()) clearTimeout(tmr);
59969
60022
  diagTimers.clear();
@@ -60554,6 +60607,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
60554
60607
  };
60555
60608
  const nodes = [];
60556
60609
  const seen = /* @__PURE__ */ new Set();
60610
+ const walked = /* @__PURE__ */ new Set();
60557
60611
  const shippedById = /* @__PURE__ */ new Map();
60558
60612
  const anchorSources = /* @__PURE__ */ new Set();
60559
60613
  const semanticEndpoints = /* @__PURE__ */ new Map();
@@ -60585,6 +60639,8 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
60585
60639
  const population = label === "RootCause" && opts.sharedStore ? [...store.findNodesByLabel(label), ...opts.sharedStore.findNodesByLabel(label)] : store.findNodesByLabel(label);
60586
60640
  for (const n of population) {
60587
60641
  if (seen.has(n.id)) continue;
60642
+ if (walked.has(n.id)) continue;
60643
+ walked.add(n.id);
60588
60644
  composition.candidates++;
60589
60645
  if (n.attrs["source"] === "cloud") {
60590
60646
  composition.cloudSourced++;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.3",
3
+ "version": "2.0.4-dev.1806",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -15877,6 +15877,10 @@ function fileHasStrandedSymbol(store2, fileId) {
15877
15877
  }
15878
15878
  return false;
15879
15879
  }
15880
+ function fileIndexUnsettled(store2, fnode) {
15881
+ if (fnode.attrs["symbolCount"] === 0 || typeof fnode.attrs["indexSkipped"] === "string") return false;
15882
+ return store2.outEdges(fnode.id, ["DEFINES", "CONTAINS"]).length === 0;
15883
+ }
15880
15884
  function symbolNodeId(workspaceId, relPath, qname, kind) {
15881
15885
  return `${kind}_${sha(
15882
15886
  workspaceId + ":" + relPath.replace(/\\/g, "/") + ":" + qname
@@ -15992,7 +15996,7 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
15992
15996
  }
15993
15997
  fileHashes.set(rel, h);
15994
15998
  const fnode = store2.getNode(fileNodeId(workspaceId, rel));
15995
- if (fnode && fnode.attrs["contentHash"] === h && !fileHasStrandedSymbol(store2, fnode.id)) {
15999
+ if (fnode && fnode.attrs["contentHash"] === h && !fileHasStrandedSymbol(store2, fnode.id) && !fileIndexUnsettled(store2, fnode)) {
15996
16000
  store2.updateNode(fnode.id, { lastUpdatedAt: t });
15997
16001
  changedRelPaths.delete(rel);
15998
16002
  } else if (!fnode || fnode.attrs["contentHash"] !== h) {
@@ -16333,7 +16337,8 @@ async function runIndexer(store2, opts) {
16333
16337
  parentId = fid2;
16334
16338
  }
16335
16339
  const fid = fileNodeId(opts.workspaceId, f.relPath);
16336
- upsertFile(store2, fid, f, opts.workspaceId);
16340
+ const skip = !f.provider ? "no-provider" : f.size > maxBytes ? "oversize" : void 0;
16341
+ upsertFile(store2, fid, f, opts.workspaceId, skip);
16337
16342
  report.nodesEmitted++;
16338
16343
  upsertEdge(store2, parentId, "CONTAINS", fid, 1);
16339
16344
  report.edgesEmitted++;
@@ -16378,6 +16383,7 @@ async function runIndexer(store2, opts) {
16378
16383
  symbols = f.provider.extractSymbols(source, f.absPath);
16379
16384
  } catch {
16380
16385
  report.filesSkipped++;
16386
+ stampIndexOutcome(store2, fileNodeId(opts.workspaceId, f.relPath), { indexSkipped: "parse-error" });
16381
16387
  continue;
16382
16388
  }
16383
16389
  reExports = scanReExports(source);
@@ -16779,6 +16785,12 @@ async function runIndexer(store2, opts) {
16779
16785
  }
16780
16786
  }
16781
16787
  perf("flush cross-file edges");
16788
+ for (const relPath of symbolsByFile.keys()) {
16789
+ const fid = fileNodeId(opts.workspaceId, relPath);
16790
+ if (store2.outEdges(fid, ["DEFINES", "CONTAINS"]).length === 0) {
16791
+ stampIndexOutcome(store2, fid, { symbolCount: 0 });
16792
+ }
16793
+ }
16782
16794
  report.durationMs = Date.now() - started;
16783
16795
  indexNow = null;
16784
16796
  return report;
@@ -16887,7 +16899,7 @@ function gitListFiles(root, ignores, providers) {
16887
16899
  }
16888
16900
  return out2;
16889
16901
  }
16890
- function upsertFile(store2, id, f, workspaceId) {
16902
+ function upsertFile(store2, id, f, workspaceId, skip) {
16891
16903
  const now = nowTs();
16892
16904
  let contentHash;
16893
16905
  try {
@@ -16917,11 +16929,17 @@ function upsertFile(store2, id, f, workspaceId) {
16917
16929
  size: f.size,
16918
16930
  language: f.language ?? null,
16919
16931
  workspaceId,
16920
- ...contentHash ? { contentHash } : {}
16932
+ ...contentHash ? { contentHash } : {},
16933
+ ...skip ? { indexSkipped: skip } : {}
16921
16934
  }
16922
16935
  };
16923
16936
  store2.mergeNode(node);
16924
16937
  }
16938
+ function stampIndexOutcome(store2, fileId, outcome) {
16939
+ const fnode = store2.getNode(fileId);
16940
+ if (!fnode) return;
16941
+ store2.updateNode(fileId, { attrs: { ...fnode.attrs, ...outcome } });
16942
+ }
16925
16943
  function upsertFolder(store2, id, name2, workspaceId, relPath) {
16926
16944
  const now = nowTs();
16927
16945
  const node = {
@@ -27281,6 +27299,14 @@ import { readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
27281
27299
  import { join as join5, relative as relative2, sep as sep2 } from "node:path";
27282
27300
  var IGNORED = /[\\/](?:\.git|node_modules|\.errata|dist|__pycache__)(?:[\\/]|$)/;
27283
27301
  var SOURCE_RE = /\.(ts|tsx|js|jsx|mjs|cjs|py)$/i;
27302
+ function indexerWouldScan(rel) {
27303
+ for (const seg of rel.split(/[\\/]/)) {
27304
+ if (seg.length === 0) continue;
27305
+ if (DEFAULT_IGNORES.has(seg)) return false;
27306
+ if (seg.startsWith(".") && seg !== ".") return false;
27307
+ }
27308
+ return true;
27309
+ }
27284
27310
  function gitSourceFiles(root) {
27285
27311
  let stdout;
27286
27312
  try {
@@ -27294,7 +27320,7 @@ function gitSourceFiles(root) {
27294
27320
  }
27295
27321
  const out2 = [];
27296
27322
  for (const rel of stdout.split("\0")) {
27297
- if (!rel || !SOURCE_RE.test(rel)) continue;
27323
+ if (!rel || !SOURCE_RE.test(rel) || !indexerWouldScan(rel)) continue;
27298
27324
  const abs = join5(root, rel);
27299
27325
  if (IGNORED.test(abs)) continue;
27300
27326
  out2.push(abs);
@@ -27312,6 +27338,7 @@ function* walkSource(dir) {
27312
27338
  const name2 = String(e.name);
27313
27339
  const full = join5(dir, name2);
27314
27340
  if (IGNORED.test(full)) continue;
27341
+ if (!indexerWouldScan(name2)) continue;
27315
27342
  if (e.isDirectory()) yield* walkSource(full);
27316
27343
  else if (SOURCE_RE.test(name2)) yield full;
27317
27344
  }
@@ -27345,7 +27372,8 @@ async function findStaleFiles(store2, rootPath, workspaceId) {
27345
27372
  } else {
27346
27373
  const edges = store2.outEdges(fnode.id, ["DEFINES", "CONTAINS"]);
27347
27374
  if (edges.length === 0) {
27348
- stale.push(abs);
27375
+ const converged = fnode.attrs["symbolCount"] === 0 || typeof fnode.attrs["indexSkipped"] === "string";
27376
+ if (!converged) stale.push(abs);
27349
27377
  } else {
27350
27378
  const targets = edges.map((e) => e.to);
27351
27379
  pending.push({ abs, targets });