@inerrata-corporation/errata 2.0.2-dev.211 → 2.0.2-dev.220

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
@@ -21852,7 +21852,7 @@ var init_client = __esm({
21852
21852
  init_oauth();
21853
21853
  init_src();
21854
21854
  asWireCount = (n) => typeof n === "number" && Number.isFinite(n) && n >= 0 ? Math.floor(n) : null;
21855
- INGEST_NODE_CHUNK = 25;
21855
+ INGEST_NODE_CHUNK = 8;
21856
21856
  CloudClient = class {
21857
21857
  baseUrl;
21858
21858
  apiKey;
@@ -22015,7 +22015,7 @@ var init_client = __esm({
22015
22015
  async ingest(batch) {
22016
22016
  const runId = randomUUID();
22017
22017
  if (batch.nodes.length <= INGEST_NODE_CHUNK && batch.edges.length <= MAX_EDGES_PER_PAYLOAD) {
22018
- const result = await this.ingestWire(toWirePayload(batch, runId));
22018
+ const result = await this.ingestWithSplit(batch, batch.nodes, batch.edges);
22019
22019
  return { ...summarizeIngestResult(result), result };
22020
22020
  }
22021
22021
  const merged = { runId, nodes: [], edges: [] };
@@ -22028,7 +22028,7 @@ var init_client = __esm({
22028
22028
  const shipped = new Set(inChunk);
22029
22029
  pendingEdges = pendingEdges.filter((e) => !shipped.has(e));
22030
22030
  }
22031
- const r = await this.ingestWire(toWirePayload({ ...batch, nodes: nodeChunk, edges: inChunk }, randomUUID()));
22031
+ const r = await this.ingestWithSplit(batch, nodeChunk, inChunk);
22032
22032
  merged.nodes.push(...r.nodes);
22033
22033
  merged.edges.push(...r.edges);
22034
22034
  for (const rn of r.nodes) {
@@ -22055,6 +22055,38 @@ var init_client = __esm({
22055
22055
  async ingestWire(payload) {
22056
22056
  return this.json("POST", "/v2/ingest", payload);
22057
22057
  }
22058
+ /**
22059
+ * Ship a node chunk, and on an EDGE TIMEOUT split it and retry the halves.
22060
+ *
22061
+ * `INGEST_NODE_CHUNK` is a guess about how long the server takes per node, and
22062
+ * that number moves with the size of the graph — so any fixed value eventually
22063
+ * drifts into the proxy's timeout and the lane dies silently (the 2026-07-31
22064
+ * three-day instances-lane outage). This makes the client self-correcting
22065
+ * instead: a chunk that times out is halved and retried, down to a single node,
22066
+ * so the drain degrades to slower rather than to stopped.
22067
+ *
22068
+ * ONLY on timeout-shaped failures (502/504/408, or a transport abort). A 4xx is
22069
+ * a verdict about the payload — splitting it would just re-send a rejected batch
22070
+ * N more times — and a 409/413 has its own handling upstream.
22071
+ */
22072
+ async ingestWithSplit(batch, nodes, edges) {
22073
+ try {
22074
+ return await this.ingestWire(toWirePayload({ ...batch, nodes, edges }, randomUUID()));
22075
+ } catch (err2) {
22076
+ const status = err2 instanceof CloudError ? err2.status : 0;
22077
+ const timedOut = status === 502 || status === 504 || status === 408 || status === 0;
22078
+ if (!timedOut || nodes.length <= 1) throw err2;
22079
+ const mid = Math.ceil(nodes.length / 2);
22080
+ const a = await this.ingestWithSplit(batch, nodes.slice(0, mid), edges);
22081
+ const b = await this.ingestWithSplit(batch, nodes.slice(mid), []);
22082
+ return {
22083
+ runId: a.runId,
22084
+ nodes: [...a.nodes, ...b.nodes],
22085
+ edges: [...a.edges, ...b.edges],
22086
+ ...a.patternReconciliation || b.patternReconciliation ? { patternReconciliation: { ...a.patternReconciliation, ...b.patternReconciliation } } : {}
22087
+ };
22088
+ }
22089
+ }
22058
22090
  /**
22059
22091
  * SDK v2 graph-native write. It remembers a typed diagnostic spine through
22060
22092
  * `/v2/ingest` instead of the legacy forum question/answer routes:
@@ -52342,7 +52374,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
52342
52374
  }
52343
52375
 
52344
52376
  // src/engine.ts
52345
- var DAEMON_VERSION = true ? "2.0.2-dev.211" : "2.0.0-alpha.0";
52377
+ var DAEMON_VERSION = true ? "2.0.2-dev.220" : "2.0.0-alpha.0";
52346
52378
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
52347
52379
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
52348
52380
  var GIT_OP_MUTE_MS = 4e3;
@@ -54003,6 +54035,7 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
54003
54035
  for (const n of store.findNodesByLabel(label)) {
54004
54036
  if (n.attrs["source"] === "cloud") continue;
54005
54037
  if (ignored(n.description) || ignored(String(n.attrs["name"] ?? ""))) continue;
54038
+ if (typeof n.attrs["contributedAtSeq"] === "number") continue;
54006
54039
  nodes.push({
54007
54040
  ...n,
54008
54041
  // Language wire id = the shared `languageCanonicalId` (warming-spine
@@ -54046,7 +54079,7 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
54046
54079
  nodes,
54047
54080
  edges
54048
54081
  };
54049
- return { ...base, payloadDigest: digest(base) };
54082
+ return { ...base, payloadDigest: digest(base), localIds: [...seen] };
54050
54083
  }
54051
54084
 
54052
54085
  // src/lockfile-auto.ts
@@ -54901,14 +54934,35 @@ async function startMultiDaemon(opts = {}) {
54901
54934
  let boundaryFlushing = false;
54902
54935
  let boundaryFlushStartedAt = 0;
54903
54936
  let boundaryFlushStage = "";
54937
+ let boundaryFlushUnits = 0;
54938
+ let boundaryFlushLastAdvanceAt = 0;
54939
+ let boundaryFlushBatches = 0;
54904
54940
  const beginFlush = (stage) => {
54905
54941
  boundaryFlushing = true;
54906
54942
  boundaryFlushStartedAt = Date.now();
54943
+ boundaryFlushLastAdvanceAt = Date.now();
54944
+ boundaryFlushUnits = 0;
54945
+ boundaryFlushBatches = 0;
54907
54946
  boundaryFlushStage = stage;
54908
54947
  };
54909
54948
  const flushStage = (stage) => {
54910
54949
  boundaryFlushStage = stage;
54911
54950
  };
54951
+ const noteFlushProgress = (units) => {
54952
+ boundaryFlushBatches++;
54953
+ if (units > 0) {
54954
+ boundaryFlushUnits += units;
54955
+ boundaryFlushLastAdvanceAt = Date.now();
54956
+ }
54957
+ };
54958
+ const flushSnapshot = () => ({
54959
+ running: boundaryFlushing,
54960
+ stage: boundaryFlushStage,
54961
+ ageMs: boundaryFlushStartedAt ? Date.now() - boundaryFlushStartedAt : 0,
54962
+ units: boundaryFlushUnits,
54963
+ batches: boundaryFlushBatches,
54964
+ stalledMs: boundaryFlushLastAdvanceAt ? Date.now() - boundaryFlushLastAdvanceAt : 0
54965
+ });
54912
54966
  const endFlush = () => {
54913
54967
  boundaryFlushing = false;
54914
54968
  boundaryFlushStage = "";
@@ -55489,8 +55543,10 @@ async function startMultiDaemon(opts = {}) {
55489
55543
  const errors = [];
55490
55544
  const lane = async (name2, projectName, run3) => {
55491
55545
  try {
55492
- await run3();
55546
+ const accepted = await run3();
55547
+ noteFlushProgress(typeof accepted === "number" ? accepted : 0);
55493
55548
  } catch (err2) {
55549
+ noteFlushProgress(0);
55494
55550
  const msg = `[${projectName}] ${name2}: ${err2 instanceof Error ? err2.message : String(err2)}`;
55495
55551
  errors.push(msg);
55496
55552
  console.error(`[sync\u2192cloud] ${msg}`);
@@ -55506,6 +55562,13 @@ async function startMultiDaemon(opts = {}) {
55506
55562
  await lane("context", projectName, async () => {
55507
55563
  const res = await client.ingest(context);
55508
55564
  uploaded += res.accepted;
55565
+ const seq = store.currentIngestSeq();
55566
+ for (const localId of context.localIds) {
55567
+ const local = store.getNode(localId);
55568
+ if (!local) continue;
55569
+ store.updateNode(localId, { attrs: { ...local.attrs, contributedAtSeq: seq } });
55570
+ }
55571
+ return res.accepted;
55509
55572
  });
55510
55573
  }
55511
55574
  let lexicon;
@@ -55562,6 +55625,7 @@ async function startMultiDaemon(opts = {}) {
55562
55625
  attrs: { ...local.attrs, semanticEdgesContributedDigest: dg }
55563
55626
  });
55564
55627
  }
55628
+ return res.accepted;
55565
55629
  });
55566
55630
  }
55567
55631
  }
@@ -55671,11 +55735,7 @@ async function startMultiDaemon(opts = {}) {
55671
55735
  }
55672
55736
  };
55673
55737
  app.post("/api/sync", async (c) => {
55674
- if (boundaryFlushing)
55675
- return c.json(
55676
- { skipped: "in-flight", stage: boundaryFlushStage, ageMs: Date.now() - boundaryFlushStartedAt },
55677
- 409
55678
- );
55738
+ if (boundaryFlushing) return c.json({ skipped: "in-flight", ...flushSnapshot() }, 409);
55679
55739
  beginFlush("principles");
55680
55740
  try {
55681
55741
  const principles = await daemon.syncPrinciplesPublic();
@@ -55692,6 +55752,7 @@ async function startMultiDaemon(opts = {}) {
55692
55752
  endFlush();
55693
55753
  }
55694
55754
  });
55755
+ app.get("/api/sync", (c) => c.json(flushSnapshot()));
55695
55756
  app.post("/api/tick", async (c) => {
55696
55757
  try {
55697
55758
  return c.json({ reports: Object.fromEntries(await daemon.tickAll()) });
@@ -56847,6 +56908,28 @@ async function cmdStatus() {
56847
56908
  }
56848
56909
  console.log(` graph db: ${existsSync25(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
56849
56910
  console.log(` event log: ${existsSync25(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
56911
+ if (existsSync25(paths.castalia)) {
56912
+ try {
56913
+ const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src4(), src_exports2));
56914
+ const store = openGraphStore2({ path: paths.castalia });
56915
+ try {
56916
+ let pending = 0;
56917
+ let total = 0;
56918
+ for (const label of ["Problem", "Solution", "RootCause"]) {
56919
+ for (const n of store.findNodesByLabel(label)) {
56920
+ if (n.attrs["source"] === "cloud") continue;
56921
+ total++;
56922
+ const c = n.attrs["contributedAtSeq"];
56923
+ if (typeof c !== "number" || (n.lastReinforcedAtSeq ?? 0) > c) pending++;
56924
+ }
56925
+ }
56926
+ console.log(` contribute: ${pending} pending of ${total} instance node(s)`);
56927
+ } finally {
56928
+ store.close();
56929
+ }
56930
+ } catch {
56931
+ }
56932
+ }
56850
56933
  const lockPath = globalDaemonLock();
56851
56934
  const running = isDaemonAlive(lockPath) ? readDaemonLock(lockPath) : null;
56852
56935
  console.log(
@@ -58520,9 +58603,19 @@ async function cmdSync(arg) {
58520
58603
  const ageMs = typeof body2?.ageMs === "number" ? body2.ageMs : 0;
58521
58604
  const dur = ageMs >= 6e4 ? `${Math.floor(ageMs / 6e4)}m` : `${Math.max(1, Math.round(ageMs / 1e3))}s`;
58522
58605
  const stage = body2?.stage ? ` \u2014 stage: ${body2.stage}` : "";
58606
+ const units = typeof body2?.units === "number" ? body2.units : 0;
58607
+ const batches = typeof body2?.batches === "number" ? body2.batches : 0;
58608
+ const stalledMs = typeof body2?.stalledMs === "number" ? body2.stalledMs : 0;
58609
+ const moved = batches > 0 ? ` \xB7 ${batches} batch(es), ${units} accepted` : "";
58523
58610
  console.log(
58524
- 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."
58611
+ 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."
58525
58612
  );
58613
+ if (stalledMs > 12e4 && batches > 0) {
58614
+ const stalledMin = Math.floor(stalledMs / 6e4);
58615
+ console.log(
58616
+ ` \u26A0 STALLED: ${batches} batch(es) shipped but nothing accepted for ${stalledMin}m \u2014 the lane is looping, not draining. Check \`errata status\` for the backlog depth and daemon.log for the failing lane.`
58617
+ );
58618
+ }
58526
58619
  if (ageMs > 30 * 6e4) {
58527
58620
  console.log(
58528
58621
  " \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.211",
3
+ "version": "2.0.2-dev.220",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {