@inerrata-corporation/errata 2.0.3-dev.1792 → 2.0.3-dev.1796

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
@@ -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) {
@@ -32295,9 +32299,6 @@ async function runIndexer(store, opts) {
32295
32299
  }
32296
32300
  symbolsByFile.set(f.relPath, symbols);
32297
32301
  if (reExports.length > 0) reExportsByFile.set(f.relPath, reExports);
32298
- if (symbols.length === 0) {
32299
- stampIndexOutcome(store, fileNodeId(opts.workspaceId, f.relPath), { symbolCount: 0 });
32300
- }
32301
32302
  report.filesParsed++;
32302
32303
  report.byLanguage[f.provider.id] = (report.byLanguage[f.provider.id] ?? 0) + 1;
32303
32304
  if (report.filesParsed % 250 === 0) perf(`parse @ ${report.filesParsed}/${files.length}`);
@@ -32689,6 +32690,12 @@ async function runIndexer(store, opts) {
32689
32690
  }
32690
32691
  }
32691
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
+ }
32692
32699
  report.durationMs = Date.now() - started2;
32693
32700
  indexNow = null;
32694
32701
  return report;
@@ -40199,6 +40206,14 @@ var init_src11 = __esm({
40199
40206
  import { execFileSync as execFileSync2 } from "node:child_process";
40200
40207
  import { readdirSync as readdirSync4, statSync as statSync3 } from "node:fs";
40201
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
+ }
40202
40217
  function gitSourceFiles(root) {
40203
40218
  let stdout;
40204
40219
  try {
@@ -40212,7 +40227,7 @@ function gitSourceFiles(root) {
40212
40227
  }
40213
40228
  const out2 = [];
40214
40229
  for (const rel of stdout.split("\0")) {
40215
- if (!rel || !SOURCE_RE.test(rel)) continue;
40230
+ if (!rel || !SOURCE_RE.test(rel) || !indexerWouldScan(rel)) continue;
40216
40231
  const abs = join11(root, rel);
40217
40232
  if (IGNORED.test(abs)) continue;
40218
40233
  out2.push(abs);
@@ -40230,6 +40245,7 @@ function* walkSource(dir) {
40230
40245
  const name2 = String(e.name);
40231
40246
  const full = join11(dir, name2);
40232
40247
  if (IGNORED.test(full)) continue;
40248
+ if (!indexerWouldScan(name2)) continue;
40233
40249
  if (e.isDirectory()) yield* walkSource(full);
40234
40250
  else if (SOURCE_RE.test(name2)) yield full;
40235
40251
  }
@@ -57560,7 +57576,14 @@ var census = {
57560
57576
  since: Date.now(),
57561
57577
  breakerRecycles: 0,
57562
57578
  breakerQuarantined: [],
57563
- 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);
57564
57587
  };
57565
57588
  var QUARANTINE_LIST_CAP = 8;
57566
57589
  var noteWatchDispatched = () => void census.dispatched++;
@@ -57579,19 +57602,33 @@ var noteWindowOffender = (path2, count, windowMs) => {
57579
57602
  function watchCensus() {
57580
57603
  return { ...census };
57581
57604
  }
57582
- 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()) {
57583
57614
  if (c.dispatched === 0 && c.emitted === 0) return null;
57584
- const mins = Math.max(1, Math.round((Date.now() - c.since) / 6e4));
57585
- 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));
57586
57617
  let base = `${c.dispatched} OS event(s) \u2192 ${c.logged} logged (${perMin}/min, ${c.rejected} rejected here)`;
57587
57618
  if (c.breakerRecycles > 0 || c.breakerQuarantined.length > 0) {
57588
57619
  const q = c.breakerQuarantined.length > 0 ? `, ${c.breakerQuarantined.length} quarantined` : "";
57589
57620
  base += ` \xB7 breaker: ${c.breakerRecycles} recycled${q}`;
57590
57621
  }
57591
- if (c.dispatched > 500 && c.dispatched / Math.max(c.logged, 1) > 1e4) {
57592
- 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) {
57593
57627
  return `${base} \u26A0 watcher is doing work with NO output \u2014 a watch target likely contains an ignored tree${top}`;
57594
57628
  }
57629
+ if (noOutput) {
57630
+ return `${base} \xB7 no-output dispatch at a low rate = last-access echo of bulk reads (harmless)${top}`;
57631
+ }
57595
57632
  return base;
57596
57633
  }
57597
57634
 
@@ -57762,7 +57799,7 @@ function createWatchBreaker(deps) {
57762
57799
  }
57763
57800
 
57764
57801
  // src/engine.ts
57765
- var DAEMON_VERSION = true ? "2.0.3-dev.1792" : "2.0.0-alpha.0";
57802
+ var DAEMON_VERSION = true ? "2.0.3-dev.1796" : "2.0.0-alpha.0";
57766
57803
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
57767
57804
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
57768
57805
  var GIT_OP_MUTE_MS = 4e3;
@@ -57907,6 +57944,7 @@ function createWorkspaceEngine(opts) {
57907
57944
  ignoreInitial: true,
57908
57945
  persistent: true
57909
57946
  });
57947
+ noteWatcherOpened();
57910
57948
  watchBreaker = createWatchBreaker({
57911
57949
  unwatch: (p) => void watcher?.unwatch(p),
57912
57950
  readd: (p) => void watcher?.add(p),
@@ -59975,7 +60013,10 @@ B: ${b}`,
59975
60013
  if (stopGit) stopGit();
59976
60014
  if (passWorker) await passWorker.stop();
59977
60015
  if (watchBreaker) watchBreaker.stop();
59978
- if (watcher) await watcher.close();
60016
+ if (watcher) {
60017
+ await watcher.close();
60018
+ noteWatcherClosed();
60019
+ }
59979
60020
  if (flushTimer) clearTimeout(flushTimer);
59980
60021
  for (const tmr of diagTimers.values()) clearTimeout(tmr);
59981
60022
  diagTimers.clear();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.3-dev.1792",
3
+ "version": "2.0.3-dev.1796",
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) {
@@ -16390,9 +16394,6 @@ async function runIndexer(store2, opts) {
16390
16394
  }
16391
16395
  symbolsByFile.set(f.relPath, symbols);
16392
16396
  if (reExports.length > 0) reExportsByFile.set(f.relPath, reExports);
16393
- if (symbols.length === 0) {
16394
- stampIndexOutcome(store2, fileNodeId(opts.workspaceId, f.relPath), { symbolCount: 0 });
16395
- }
16396
16397
  report.filesParsed++;
16397
16398
  report.byLanguage[f.provider.id] = (report.byLanguage[f.provider.id] ?? 0) + 1;
16398
16399
  if (report.filesParsed % 250 === 0) perf(`parse @ ${report.filesParsed}/${files.length}`);
@@ -16784,6 +16785,12 @@ async function runIndexer(store2, opts) {
16784
16785
  }
16785
16786
  }
16786
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
+ }
16787
16794
  report.durationMs = Date.now() - started;
16788
16795
  indexNow = null;
16789
16796
  return report;
@@ -27292,6 +27299,14 @@ import { readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
27292
27299
  import { join as join5, relative as relative2, sep as sep2 } from "node:path";
27293
27300
  var IGNORED = /[\\/](?:\.git|node_modules|\.errata|dist|__pycache__)(?:[\\/]|$)/;
27294
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
+ }
27295
27310
  function gitSourceFiles(root) {
27296
27311
  let stdout;
27297
27312
  try {
@@ -27305,7 +27320,7 @@ function gitSourceFiles(root) {
27305
27320
  }
27306
27321
  const out2 = [];
27307
27322
  for (const rel of stdout.split("\0")) {
27308
- if (!rel || !SOURCE_RE.test(rel)) continue;
27323
+ if (!rel || !SOURCE_RE.test(rel) || !indexerWouldScan(rel)) continue;
27309
27324
  const abs = join5(root, rel);
27310
27325
  if (IGNORED.test(abs)) continue;
27311
27326
  out2.push(abs);
@@ -27323,6 +27338,7 @@ function* walkSource(dir) {
27323
27338
  const name2 = String(e.name);
27324
27339
  const full = join5(dir, name2);
27325
27340
  if (IGNORED.test(full)) continue;
27341
+ if (!indexerWouldScan(name2)) continue;
27326
27342
  if (e.isDirectory()) yield* walkSource(full);
27327
27343
  else if (SOURCE_RE.test(name2)) yield full;
27328
27344
  }