@inerrata-corporation/errata 2.0.2-dev.214 → 2.0.2-dev.223

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.
Files changed (2) hide show
  1. package/errata.mjs +105 -12
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -22012,11 +22012,26 @@ var init_client = __esm({
22012
22012
  * drain (endpoint-label validation is deferred to the service when an endpoint
22013
22013
  * isn't in-payload), so the split never orphans an edge. Sub-results concat into
22014
22014
  * one. (fix: an un-chunked >200-node backlog 413'd forever and never drained.) */
22015
- async ingest(batch) {
22015
+ async ingest(batch, opts = {}) {
22016
22016
  const runId = randomUUID();
22017
+ const startedAt = Date.now();
22018
+ const totalChunks = Math.max(1, Math.ceil(batch.nodes.length / INGEST_NODE_CHUNK)) + Math.ceil(Math.max(0, batch.edges.length - MAX_EDGES_PER_PAYLOAD) / MAX_EDGES_PER_PAYLOAD);
22019
+ let chunkIndex = 0;
22020
+ const emit = (nodeIds, accepted) => {
22021
+ chunkIndex++;
22022
+ opts.onChunk?.({
22023
+ chunkIndex,
22024
+ totalChunks,
22025
+ nodeIds,
22026
+ accepted,
22027
+ elapsedMs: Date.now() - startedAt
22028
+ });
22029
+ };
22017
22030
  if (batch.nodes.length <= INGEST_NODE_CHUNK && batch.edges.length <= MAX_EDGES_PER_PAYLOAD) {
22018
22031
  const result = await this.ingestWithSplit(batch, batch.nodes, batch.edges);
22019
- return { ...summarizeIngestResult(result), result };
22032
+ const summary = summarizeIngestResult(result);
22033
+ emit(batch.nodes.map((n) => n.id), summary.accepted);
22034
+ return { ...summary, result };
22020
22035
  }
22021
22036
  const merged = { runId, nodes: [], edges: [] };
22022
22037
  let pendingEdges = [...batch.edges];
@@ -22029,6 +22044,7 @@ var init_client = __esm({
22029
22044
  pendingEdges = pendingEdges.filter((e) => !shipped.has(e));
22030
22045
  }
22031
22046
  const r = await this.ingestWithSplit(batch, nodeChunk, inChunk);
22047
+ emit(nodeChunk.map((n) => n.id), summarizeIngestResult(r).accepted);
22032
22048
  merged.nodes.push(...r.nodes);
22033
22049
  merged.edges.push(...r.edges);
22034
22050
  for (const rn of r.nodes) {
@@ -22047,6 +22063,7 @@ var init_client = __esm({
22047
22063
  to: echoedCloudId.get(e.to) ?? e.to
22048
22064
  }));
22049
22065
  const r = await this.ingestWire(toWirePayload({ ...batch, nodes: [], edges: rewritten }, randomUUID()));
22066
+ emit([], summarizeIngestResult(r).accepted);
22050
22067
  merged.edges.push(...r.edges);
22051
22068
  }
22052
22069
  return { ...summarizeIngestResult(merged), result: merged };
@@ -52374,7 +52391,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
52374
52391
  }
52375
52392
 
52376
52393
  // src/engine.ts
52377
- var DAEMON_VERSION = true ? "2.0.2-dev.214" : "2.0.0-alpha.0";
52394
+ var DAEMON_VERSION = true ? "2.0.2-dev.223" : "2.0.0-alpha.0";
52378
52395
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
52379
52396
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
52380
52397
  var GIT_OP_MUTE_MS = 4e3;
@@ -54031,10 +54048,12 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
54031
54048
  const ignored = (s) => ignorePatterns.length > 0 && ignorePatterns.some((p) => s.toLowerCase().includes(p));
54032
54049
  const nodes = [];
54033
54050
  const seen = /* @__PURE__ */ new Set();
54051
+ const localIdByWireId = {};
54034
54052
  for (const label of labels) {
54035
54053
  for (const n of store.findNodesByLabel(label)) {
54036
54054
  if (n.attrs["source"] === "cloud") continue;
54037
54055
  if (ignored(n.description) || ignored(String(n.attrs["name"] ?? ""))) continue;
54056
+ if (typeof n.attrs["contributedAtSeq"] === "number") continue;
54038
54057
  nodes.push({
54039
54058
  ...n,
54040
54059
  // Language wire id = the shared `languageCanonicalId` (warming-spine
@@ -54059,6 +54078,7 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
54059
54078
  anchorVisibility: "public"
54060
54079
  });
54061
54080
  seen.add(n.id);
54081
+ localIdByWireId[nodes[nodes.length - 1].id] = n.id;
54062
54082
  }
54063
54083
  }
54064
54084
  if (nodes.length === 0) return null;
@@ -54078,7 +54098,7 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
54078
54098
  nodes,
54079
54099
  edges
54080
54100
  };
54081
- return { ...base, payloadDigest: digest(base) };
54101
+ return { ...base, payloadDigest: digest(base), localIds: [...seen], localIdByWireId };
54082
54102
  }
54083
54103
 
54084
54104
  // src/lockfile-auto.ts
@@ -54933,14 +54953,41 @@ async function startMultiDaemon(opts = {}) {
54933
54953
  let boundaryFlushing = false;
54934
54954
  let boundaryFlushStartedAt = 0;
54935
54955
  let boundaryFlushStage = "";
54956
+ let boundaryFlushUnits = 0;
54957
+ let boundaryFlushLastAdvanceAt = 0;
54958
+ let boundaryFlushBatches = 0;
54936
54959
  const beginFlush = (stage) => {
54937
54960
  boundaryFlushing = true;
54938
54961
  boundaryFlushStartedAt = Date.now();
54962
+ boundaryFlushLastAdvanceAt = Date.now();
54963
+ boundaryFlushUnits = 0;
54964
+ boundaryFlushBatches = 0;
54965
+ boundaryFlushItems = 0;
54966
+ boundaryFlushDetail = "";
54939
54967
  boundaryFlushStage = stage;
54940
54968
  };
54941
54969
  const flushStage = (stage) => {
54942
54970
  boundaryFlushStage = stage;
54943
54971
  };
54972
+ let boundaryFlushItems = 0;
54973
+ let boundaryFlushDetail = "";
54974
+ const noteFlushProgress = (units, items = 0, detail = "") => {
54975
+ boundaryFlushBatches++;
54976
+ boundaryFlushUnits += Math.max(0, units);
54977
+ boundaryFlushItems += Math.max(0, items);
54978
+ if (detail) boundaryFlushDetail = detail;
54979
+ if (units > 0 || items > 0) boundaryFlushLastAdvanceAt = Date.now();
54980
+ };
54981
+ const flushSnapshot = () => ({
54982
+ running: boundaryFlushing,
54983
+ stage: boundaryFlushStage,
54984
+ detail: boundaryFlushDetail,
54985
+ ageMs: boundaryFlushStartedAt ? Date.now() - boundaryFlushStartedAt : 0,
54986
+ units: boundaryFlushUnits,
54987
+ items: boundaryFlushItems,
54988
+ chunks: boundaryFlushBatches,
54989
+ stalledMs: boundaryFlushLastAdvanceAt ? Date.now() - boundaryFlushLastAdvanceAt : 0
54990
+ });
54944
54991
  const endFlush = () => {
54945
54992
  boundaryFlushing = false;
54946
54993
  boundaryFlushStage = "";
@@ -55536,8 +55583,20 @@ async function startMultiDaemon(opts = {}) {
55536
55583
  });
55537
55584
  if (context) {
55538
55585
  await lane("context", projectName, async () => {
55539
- const res = await client.ingest(context);
55586
+ const res = await client.ingest(context, {
55587
+ onChunk: (p) => {
55588
+ const seq = store.currentIngestSeq();
55589
+ for (const wireId of p.nodeIds) {
55590
+ const localId = context.localIdByWireId[wireId] ?? wireId;
55591
+ const local = store.getNode(localId);
55592
+ if (!local) continue;
55593
+ store.updateNode(localId, { attrs: { ...local.attrs, contributedAtSeq: seq } });
55594
+ }
55595
+ noteFlushProgress(p.accepted, p.nodeIds.length, `context ${p.chunkIndex}/${p.totalChunks}`);
55596
+ }
55597
+ });
55540
55598
  uploaded += res.accepted;
55599
+ return res.accepted;
55541
55600
  });
55542
55601
  }
55543
55602
  let lexicon;
@@ -55562,7 +55621,9 @@ async function startMultiDaemon(opts = {}) {
55562
55621
  if (instances) {
55563
55622
  if (project) instances.projectId = project.projectId;
55564
55623
  await lane("instances", projectName, async () => {
55565
- const res = await client.ingest(instances);
55624
+ const res = await client.ingest(instances, {
55625
+ onChunk: (p) => noteFlushProgress(p.accepted, p.nodeIds.length, `instances ${p.chunkIndex}/${p.totalChunks}`)
55626
+ });
55566
55627
  uploaded += res.accepted;
55567
55628
  const seq = store.currentIngestSeq();
55568
55629
  const cloudIdByLocal = new Map(
@@ -55594,6 +55655,7 @@ async function startMultiDaemon(opts = {}) {
55594
55655
  attrs: { ...local.attrs, semanticEdgesContributedDigest: dg }
55595
55656
  });
55596
55657
  }
55658
+ return res.accepted;
55597
55659
  });
55598
55660
  }
55599
55661
  }
@@ -55703,11 +55765,7 @@ async function startMultiDaemon(opts = {}) {
55703
55765
  }
55704
55766
  };
55705
55767
  app.post("/api/sync", async (c) => {
55706
- if (boundaryFlushing)
55707
- return c.json(
55708
- { skipped: "in-flight", stage: boundaryFlushStage, ageMs: Date.now() - boundaryFlushStartedAt },
55709
- 409
55710
- );
55768
+ if (boundaryFlushing) return c.json({ skipped: "in-flight", ...flushSnapshot() }, 409);
55711
55769
  beginFlush("principles");
55712
55770
  try {
55713
55771
  const principles = await daemon.syncPrinciplesPublic();
@@ -55724,6 +55782,7 @@ async function startMultiDaemon(opts = {}) {
55724
55782
  endFlush();
55725
55783
  }
55726
55784
  });
55785
+ app.get("/api/sync", (c) => c.json(flushSnapshot()));
55727
55786
  app.post("/api/tick", async (c) => {
55728
55787
  try {
55729
55788
  return c.json({ reports: Object.fromEntries(await daemon.tickAll()) });
@@ -56879,6 +56938,28 @@ async function cmdStatus() {
56879
56938
  }
56880
56939
  console.log(` graph db: ${existsSync25(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
56881
56940
  console.log(` event log: ${existsSync25(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
56941
+ if (existsSync25(paths.castalia)) {
56942
+ try {
56943
+ const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
56944
+ const store = openGraphStore2({ path: paths.castalia });
56945
+ try {
56946
+ let pending = 0;
56947
+ let total = 0;
56948
+ for (const label of ["Problem", "Solution", "RootCause"]) {
56949
+ for (const n of store.findNodesByLabel(label)) {
56950
+ if (n.attrs["source"] === "cloud") continue;
56951
+ total++;
56952
+ const c = n.attrs["contributedAtSeq"];
56953
+ if (typeof c !== "number" || (n.lastReinforcedAtSeq ?? 0) > c) pending++;
56954
+ }
56955
+ }
56956
+ console.log(` contribute: ${pending} pending of ${total} instance node(s)`);
56957
+ } finally {
56958
+ store.close();
56959
+ }
56960
+ } catch {
56961
+ }
56962
+ }
56882
56963
  const lockPath = globalDaemonLock();
56883
56964
  const running = isDaemonAlive(lockPath) ? readDaemonLock(lockPath) : null;
56884
56965
  console.log(
@@ -58552,9 +58633,21 @@ async function cmdSync(arg) {
58552
58633
  const ageMs = typeof body2?.ageMs === "number" ? body2.ageMs : 0;
58553
58634
  const dur = ageMs >= 6e4 ? `${Math.floor(ageMs / 6e4)}m` : `${Math.max(1, Math.round(ageMs / 1e3))}s`;
58554
58635
  const stage = body2?.stage ? ` \u2014 stage: ${body2.stage}` : "";
58636
+ const units = typeof body2?.units === "number" ? body2.units : 0;
58637
+ const items = typeof body2?.items === "number" ? body2.items : 0;
58638
+ const chunks = typeof body2?.chunks === "number" ? body2.chunks : 0;
58639
+ const stalledMs = typeof body2?.stalledMs === "number" ? body2.stalledMs : 0;
58640
+ const where = body2?.detail ? ` [${body2.detail}]` : "";
58641
+ const moved = chunks > 0 ? ` \xB7 ${chunks} chunk(s), ${items} shipped, ${units} accepted${where}` : "";
58555
58642
  console.log(
58556
- ageMs > 0 ? `sync already running: the daemon has been draining for ${dur}${stage}. It will finish on its own; nothing new was started.` : "sync already running: the daemon is mid-drain \u2014 it will finish on its own. Nothing new was started."
58643
+ ageMs > 0 ? `sync already running: the daemon has been draining for ${dur}${stage}${moved}. It will finish on its own; nothing new was started.` : "sync already running: the daemon is mid-drain \u2014 it will finish on its own. Nothing new was started."
58557
58644
  );
58645
+ if (stalledMs > 12e4 && chunks > 0) {
58646
+ const stalledMin = Math.floor(stalledMs / 6e4);
58647
+ console.log(
58648
+ ` \u26A0 STALLED: ${chunks} chunk(s) shipped but nothing has moved for ${stalledMin}m \u2014 the lane is looping, not draining. Check \`errata status\` for the backlog depth and daemon.log for the failing lane.`
58649
+ );
58650
+ }
58558
58651
  if (ageMs > 30 * 6e4) {
58559
58652
  console.log(
58560
58653
  " \u26A0 this pass has run past 30 min \u2014 if daemon.log shows no upload progress it may be stranded (a sleep/resume can do this). Restart with: errata stop && errata start"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.2-dev.214",
3
+ "version": "2.0.2-dev.223",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {