@inerrata-corporation/errata 2.0.8-dev.2881 → 2.0.8-dev.2890

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.
@@ -16146,6 +16146,9 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
16146
16146
  -- Live rows by name, for looking up the few symbols a statement mentions
16147
16147
  -- instead of hydrating every code symbol per minted problem.
16148
16148
  CREATE INDEX IF NOT EXISTS nodes_live_description ON nodes(description, label) WHERE valid_to IS NULL;
16149
+ -- Problems by the file their anchor hint names: the harvest upgrades hints for
16150
+ -- every edited file of every turn, and a scan parses every Problem's attrs.
16151
+ CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_json, '$.anchorHint.path')) WHERE valid_to IS NULL;
16149
16152
  `;
16150
16153
  function encodeEmbedding(emb) {
16151
16154
  if (!emb || emb.length === 0) return null;
@@ -17004,6 +17007,13 @@ var SqliteGraphStore = class {
17004
17007
  ).all(...relPaths);
17005
17008
  return rows.map(rowToNode);
17006
17009
  }
17010
+ findLiveNodesByAttr(label, attrPath, value) {
17011
+ if (!/^[A-Za-z0-9_]+(\.[A-Za-z0-9_]+)*$/.test(attrPath)) throw new Error(`invalid attr path: ${attrPath}`);
17012
+ const rows = this.db.prepare(
17013
+ `SELECT * FROM nodes WHERE json_extract(attrs_json, '$.${attrPath}') = ? AND valid_to IS NULL AND label = ?`
17014
+ ).all(value, label);
17015
+ return rows.map(rowToNode);
17016
+ }
17007
17017
  // ── Bi-temporal versioning ───────────────────────────────────────────
17008
17018
  // NOTE: the adapter's transaction() is non-reentrant (raw BEGIN/COMMIT), and
17009
17019
  // the indexer calls freezeNode inside its own store.transaction(). So this
package/errata.mjs CHANGED
@@ -17568,6 +17568,9 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
17568
17568
  -- Live rows by name, for looking up the few symbols a statement mentions
17569
17569
  -- instead of hydrating every code symbol per minted problem.
17570
17570
  CREATE INDEX IF NOT EXISTS nodes_live_description ON nodes(description, label) WHERE valid_to IS NULL;
17571
+ -- Problems by the file their anchor hint names: the harvest upgrades hints for
17572
+ -- every edited file of every turn, and a scan parses every Problem's attrs.
17573
+ CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_json, '$.anchorHint.path')) WHERE valid_to IS NULL;
17571
17574
  `;
17572
17575
  SqliteGraphStore = class {
17573
17576
  db;
@@ -18369,6 +18372,13 @@ CREATE INDEX IF NOT EXISTS nodes_live_description ON nodes(description, label) W
18369
18372
  ).all(...relPaths);
18370
18373
  return rows.map(rowToNode);
18371
18374
  }
18375
+ findLiveNodesByAttr(label, attrPath, value) {
18376
+ if (!/^[A-Za-z0-9_]+(\.[A-Za-z0-9_]+)*$/.test(attrPath)) throw new Error(`invalid attr path: ${attrPath}`);
18377
+ const rows = this.db.prepare(
18378
+ `SELECT * FROM nodes WHERE json_extract(attrs_json, '$.${attrPath}') = ? AND valid_to IS NULL AND label = ?`
18379
+ ).all(value, label);
18380
+ return rows.map(rowToNode);
18381
+ }
18372
18382
  // ── Bi-temporal versioning ───────────────────────────────────────────
18373
18383
  // NOTE: the adapter's transaction() is non-reentrant (raw BEGIN/COMMIT), and
18374
18384
  // the indexer calls freezeNode inside its own store.transaction(). So this
@@ -20645,7 +20655,7 @@ function consumeAnchorHint(store, p, ts) {
20645
20655
  }
20646
20656
  function upgradeAnchorHintsForFile(store, relPath, workspaceId2, ts) {
20647
20657
  let upgraded = 0;
20648
- for (const p of store.findNodesByLabel("Problem")) {
20658
+ for (const p of store.findLiveNodesByAttr("Problem", "anchorHint.path", relPath)) {
20649
20659
  if (p.validTo != null || p.attrs["resolvedAt"]) continue;
20650
20660
  const hint = openAnchorHint(p);
20651
20661
  if (!hint || hint.path !== relPath) continue;
@@ -80114,7 +80124,7 @@ function createWatchBreaker(deps) {
80114
80124
  }
80115
80125
 
80116
80126
  // src/engine.ts
80117
- var DAEMON_VERSION = true ? "2.0.8-dev.2881" : "2.0.0-alpha.0";
80127
+ var DAEMON_VERSION = true ? "2.0.8-dev.2890" : "2.0.0-alpha.0";
80118
80128
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
80119
80129
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
80120
80130
  var GIT_OP_MUTE_MS = 4e3;
@@ -85807,6 +85817,27 @@ function codexWakeCandidates(input) {
85807
85817
  return out2;
85808
85818
  }
85809
85819
 
85820
+ // src/wake-policy.ts
85821
+ function wakeIndexPlan(nodeCount, reindexOnStart) {
85822
+ if (nodeCount > 0) return "catch-up";
85823
+ return reindexOnStart ? "full-index" : "skip";
85824
+ }
85825
+ var WAKE_STAGGER_MS = 2e4;
85826
+ function wakeDelay(index, staggerMs = WAKE_STAGGER_MS) {
85827
+ return Math.max(0, index) * staggerMs;
85828
+ }
85829
+ function serialQueue() {
85830
+ let chain2 = Promise.resolve();
85831
+ return (job) => {
85832
+ const run3 = chain2.then(job).then(
85833
+ () => void 0,
85834
+ () => void 0
85835
+ );
85836
+ chain2 = run3;
85837
+ return run3;
85838
+ };
85839
+ }
85840
+
85810
85841
  // src/multi.ts
85811
85842
  init_profile2();
85812
85843
 
@@ -86277,6 +86308,7 @@ async function startMultiDaemon(opts = {}) {
86277
86308
  );
86278
86309
  }
86279
86310
  const app = new Hono2();
86311
+ const enqueueWakeIndex = serialQueue();
86280
86312
  const attachWorkspace = (root) => {
86281
86313
  const norm = normPath(root);
86282
86314
  if (records.some((r) => normPath(r.root) === norm)) return { attached: false, id: null, reason: "already-watched" };
@@ -86306,8 +86338,16 @@ async function startMultiDaemon(opts = {}) {
86306
86338
  registerWorkspace(rec.engine.profile, rec.root);
86307
86339
  } catch {
86308
86340
  }
86309
- void rec.engine.reindex({ skipEmbed: true, quiet: rec.engine.store.nodeCount() > 0 }).catch(() => {
86310
- });
86341
+ if (wakeIndexPlan(rec.engine.store.nodeCount(), opts.reindexOnStart !== false) === "full-index") {
86342
+ void enqueueWakeIndex(async () => {
86343
+ const done = markPass(`wake-index:${rec.entry.name}`);
86344
+ try {
86345
+ await rec.engine.reindex({ skipEmbed: true });
86346
+ } finally {
86347
+ done();
86348
+ }
86349
+ });
86350
+ }
86311
86351
  console.log(`[errata] hot-registered ${rec.entry.name} (${rec.id}) \u2014 now watching live`);
86312
86352
  return { attached: true, id: rec.id };
86313
86353
  };
@@ -86583,11 +86623,14 @@ async function startMultiDaemon(opts = {}) {
86583
86623
  sinceMs: Date.now() - lookback,
86584
86624
  disabled: process.env["ERRATA_CODEX_WAKE_DISABLE"] === "1"
86585
86625
  });
86586
- for (const c of candidates) {
86587
- if (attachWorkspace(c.path).attached) {
86588
- console.log(`[errata] woke workspace ${c.name} \u2014 recent Codex/Gemini/OpenCode transcript (headless capture)`);
86589
- }
86590
- }
86626
+ candidates.forEach((c, i2) => {
86627
+ const wake = setTimeout(() => {
86628
+ if (attachWorkspace(c.path).attached) {
86629
+ console.log(`[errata] woke workspace ${c.name} \u2014 recent Codex/Gemini/OpenCode transcript (headless capture)`);
86630
+ }
86631
+ }, wakeDelay(i2));
86632
+ wake.unref?.();
86633
+ });
86591
86634
  };
86592
86635
  const codexWake = setInterval(sweepCodexWake, WORKSPACE_SWEEP_INTERVAL_MS);
86593
86636
  codexWake.unref?.();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.8-dev.2881",
3
+ "version": "2.0.8-dev.2890",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -24528,6 +24528,9 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
24528
24528
  -- Live rows by name, for looking up the few symbols a statement mentions
24529
24529
  -- instead of hydrating every code symbol per minted problem.
24530
24530
  CREATE INDEX IF NOT EXISTS nodes_live_description ON nodes(description, label) WHERE valid_to IS NULL;
24531
+ -- Problems by the file their anchor hint names: the harvest upgrades hints for
24532
+ -- every edited file of every turn, and a scan parses every Problem's attrs.
24533
+ CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_json, '$.anchorHint.path')) WHERE valid_to IS NULL;
24531
24534
  `;
24532
24535
  function encodeEmbedding(emb) {
24533
24536
  if (!emb || emb.length === 0) return null;
@@ -25386,6 +25389,13 @@ var SqliteGraphStore = class {
25386
25389
  ).all(...relPaths);
25387
25390
  return rows.map(rowToNode);
25388
25391
  }
25392
+ findLiveNodesByAttr(label, attrPath, value) {
25393
+ if (!/^[A-Za-z0-9_]+(\.[A-Za-z0-9_]+)*$/.test(attrPath)) throw new Error(`invalid attr path: ${attrPath}`);
25394
+ const rows = this.db.prepare(
25395
+ `SELECT * FROM nodes WHERE json_extract(attrs_json, '$.${attrPath}') = ? AND valid_to IS NULL AND label = ?`
25396
+ ).all(value, label);
25397
+ return rows.map(rowToNode);
25398
+ }
25389
25399
  // ── Bi-temporal versioning ───────────────────────────────────────────
25390
25400
  // NOTE: the adapter's transaction() is non-reentrant (raw BEGIN/COMMIT), and
25391
25401
  // the indexer calls freezeNode inside its own store.transaction(). So this