@inerrata-corporation/errata 2.0.6-dev.2063 → 2.0.6-dev.2081

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 +80 -11
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -591,6 +591,15 @@ var init_graph_events = __esm({
591
591
  prefixes: ["promotion."],
592
592
  consumer: "graph-health.mjs promotion triggers (receipt-absence floor, kCountErrors > 0) + probe:mechanism-liveness reading the durable pgboss.job.output twin"
593
593
  },
594
+ "graph.mergegate": {
595
+ // The nightly merge gate (reconcile Phase 2 claims + dream-merge), one
596
+ // event per nightly run: mergegate.<lane>.{merged,refused,truncated,...}.
597
+ // refused is the deferred-recovery backlog (pairs above threshold the
598
+ // judge or a veto kept apart), measured so the recovery follow-up starts
599
+ // from a number.
600
+ prefixes: ["mergegate."],
601
+ consumer: "graph-health.mjs 'Graph merge gate' triggers (truncation + refused backlog, pending a live baseline) beside ingest.judge.*"
602
+ },
594
603
  "graph.census": {
595
604
  prefixes: [
596
605
  "nodes.",
@@ -77047,7 +77056,7 @@ function createWatchBreaker(deps) {
77047
77056
  }
77048
77057
 
77049
77058
  // src/engine.ts
77050
- var DAEMON_VERSION = true ? "2.0.6-dev.2063" : "2.0.0-alpha.0";
77059
+ var DAEMON_VERSION = true ? "2.0.6-dev.2081" : "2.0.0-alpha.0";
77051
77060
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
77052
77061
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
77053
77062
  var GIT_OP_MUTE_MS = 4e3;
@@ -79774,6 +79783,13 @@ var SHIP_TERMINAL_REFUSALS = 3;
79774
79783
  function refusalFingerprint(reason, description) {
79775
79784
  return digest(`${reason}\0${description}`);
79776
79785
  }
79786
+ function isRetryableRefusal(reason) {
79787
+ return /\bretry\b/i.test(reason);
79788
+ }
79789
+ function isTerminallyRefused(n) {
79790
+ const reason = String(n.attrs["shipRefusedReason"] ?? "");
79791
+ return typeof n.attrs["shipRefusedCount"] === "number" && n.attrs["shipRefusedCount"] >= SHIP_TERMINAL_REFUSALS && !isRetryableRefusal(reason) && n.attrs["shipRefusedFingerprint"] === refusalFingerprint(reason, n.description);
79792
+ }
79777
79793
  function unshippableReason(canonical, label) {
79778
79794
  void label;
79779
79795
  if (canonical.length < WIRE_CANONICAL_ID_MIN) {
@@ -79926,10 +79942,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
79926
79942
  composition.autoMint++;
79927
79943
  continue;
79928
79944
  }
79929
- if (typeof n.attrs["shipRefusedCount"] === "number" && n.attrs["shipRefusedCount"] >= SHIP_TERMINAL_REFUSALS && n.attrs["shipRefusedFingerprint"] === refusalFingerprint(
79930
- String(n.attrs["shipRefusedReason"] ?? ""),
79931
- n.description
79932
- )) {
79945
+ if (isTerminallyRefused(n)) {
79933
79946
  composition.terminalRefused++;
79934
79947
  continue;
79935
79948
  }
@@ -80013,12 +80026,14 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
80013
80026
  }
80014
80027
  }
80015
80028
  const project = opts.project;
80029
+ const projectAnchorDigests = {};
80016
80030
  if (project) {
80017
80031
  const now = Date.now();
80018
80032
  for (const sourceId of anchorSources) {
80019
80033
  const source = store.getNode(sourceId);
80020
80034
  if (!source) continue;
80021
80035
  const wireFrom = seen.has(sourceId) ? sourceId : contributedWireId(source);
80036
+ const planned = [];
80022
80037
  for (const e of store.outEdges(sourceId, ["ANCHORED_AT"])) {
80023
80038
  const target = store.getNode(e.to);
80024
80039
  if (!target) continue;
@@ -80026,7 +80041,15 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
80026
80041
  const qname = target.attrs["qname"];
80027
80042
  if (typeof relPath !== "string" || typeof qname !== "string") continue;
80028
80043
  if (ignored(relPath) || ignored(qname)) continue;
80029
- const symId = projectSymbolId(project.salt, project.projectId, relPath, qname, target.label);
80044
+ planned.push({ e, target, symId: projectSymbolId(project.salt, project.projectId, relPath, qname, target.label) });
80045
+ }
80046
+ if (planned.length === 0) continue;
80047
+ const dg = digest(
80048
+ planned.map(({ e, symId }) => `ANCHORED_AT>${symId}>${JSON.stringify(anchorVersionAttrs(e.attrs))}`).sort()
80049
+ );
80050
+ if (source.attrs["projectAnchorsContributedDigest"] === dg) continue;
80051
+ projectAnchorDigests[sourceId] = dg;
80052
+ for (const { e, target, symId } of planned) {
80030
80053
  if (!contextSeen.has(symId)) {
80031
80054
  contextSeen.add(symId);
80032
80055
  contextNodes.push({
@@ -80133,6 +80156,11 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
80133
80156
  }
80134
80157
  const shippableNodes = heldOrphans.size === 0 ? nodes : nodes.filter((n) => !heldOrphans.has(n.id));
80135
80158
  const shippableEdges = heldOrphans.size === 0 ? edges : edges.filter((e) => !heldOrphans.has(e.from) && !heldOrphans.has(e.to));
80159
+ for (const id of heldOrphans) {
80160
+ delete anchorDigests[id];
80161
+ delete semanticEdgeDigests[id];
80162
+ delete projectAnchorDigests[id];
80163
+ }
80136
80164
  if (heldOrphans.size > 0) {
80137
80165
  console.warn(
80138
80166
  `[errata] held ${heldOrphans.size} causal-orphan node(s) \u2014 no shippable binding edge this drain (VS-orphan-gate)`
@@ -80162,7 +80190,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
80162
80190
  ...symbolSummaries ? { symbolSummaries } : {}
80163
80191
  };
80164
80192
  opts.onComposition?.(composition);
80165
- return { ...base, payloadDigest: digest(base), anchorDigests, semanticEdgeDigests, composition };
80193
+ return { ...base, payloadDigest: digest(base), anchorDigests, semanticEdgeDigests, projectAnchorDigests, composition };
80166
80194
  }
80167
80195
 
80168
80196
  // src/backfill-edges.ts
@@ -84316,7 +84344,8 @@ async function startMultiDaemon(opts = {}) {
84316
84344
  // Same shape as the drained row: a first/last trend reading must not
84317
84345
  // straddle rows where the counter is merely absent.
84318
84346
  blockedRepeat: 0,
84319
- rejectedRepeat: 0
84347
+ rejectedRepeat: 0,
84348
+ edgesRejected: 0
84320
84349
  });
84321
84350
  }
84322
84351
  if (instances) {
@@ -84344,6 +84373,17 @@ async function startMultiDaemon(opts = {}) {
84344
84373
  for (const rn of res.result.nodes) {
84345
84374
  if (rn.decision === "rejected") rejected.set(rn.canonicalId, rn.reason ?? "no reason given");
84346
84375
  }
84376
+ const rejectedEdges = res.result.edges.filter((re) => re.decision === "rejected");
84377
+ if (rejectedEdges.length > 0) {
84378
+ const byReason = /* @__PURE__ */ new Map();
84379
+ for (const re of rejectedEdges) {
84380
+ const why = `${re.type}: ${re.reason ?? "no reason given"}`;
84381
+ byReason.set(why, (byReason.get(why) ?? 0) + 1);
84382
+ }
84383
+ console.warn(
84384
+ `[sync\u2192cloud] [${projectName}] instances: ${rejectedEdges.length} edge(s) REJECTED by the door: ` + [...byReason.entries()].map(([why, n]) => `${n}\xD7 ${why}`).join(" | ")
84385
+ );
84386
+ }
84347
84387
  if (rejected.size > 0) {
84348
84388
  const sample = [...rejected.entries()].slice(0, 3).map(([id, why]) => `${store.getNode(id)?.description.slice(0, 60) ?? id}: ${why}`);
84349
84389
  console.warn(
@@ -84359,6 +84399,7 @@ async function startMultiDaemon(opts = {}) {
84359
84399
  };
84360
84400
  let blockedRepeat = 0;
84361
84401
  let rejectedRepeat = 0;
84402
+ const nowHeld = [];
84362
84403
  const noteRefused = (id, reason, repeat) => {
84363
84404
  const owner = owningStore(id);
84364
84405
  if (!owner) return;
@@ -84375,9 +84416,17 @@ async function startMultiDaemon(opts = {}) {
84375
84416
  shipRefusedFingerprint: fp
84376
84417
  }
84377
84418
  });
84419
+ if (same && count === SHIP_TERMINAL_REFUSALS && !isRetryableRefusal(reason)) {
84420
+ nowHeld.push({ id, reason });
84421
+ }
84378
84422
  };
84379
84423
  for (const id of blocked) noteRefused(id, "blocked", () => blockedRepeat++);
84380
84424
  for (const [id, why] of rejected) noteRefused(id, why, () => rejectedRepeat++);
84425
+ if (nowHeld.length > 0) {
84426
+ console.warn(
84427
+ `[sync\u2192cloud] [${projectName}] instances: HOLDING ${nowHeld.length} node(s) \u2014 refused identically ${SHIP_TERMINAL_REFUSALS}\xD7 on unchanged text; not re-sent until the text changes: ` + nowHeld.map(({ id, reason }) => `${id} "${owningStore(id)?.n.description.slice(0, 60) ?? ""}": ${reason}`).join(" | ")
84428
+ );
84429
+ }
84381
84430
  for (const n of instances.nodes) {
84382
84431
  const owner = owningStore(n.id);
84383
84432
  if (!owner || !["Problem", "Solution", "RootCause"].includes(owner.n.label)) continue;
@@ -84407,6 +84456,14 @@ async function startMultiDaemon(opts = {}) {
84407
84456
  attrs: { ...owner.n.attrs, anchorsContributedDigest: dg }
84408
84457
  });
84409
84458
  }
84459
+ for (const [localId, dg] of Object.entries(instances.projectAnchorDigests)) {
84460
+ const owner = owningStore(localId);
84461
+ if (!owner) continue;
84462
+ if (rejected.has(localId) || blocked.has(localId)) continue;
84463
+ owner.s.updateNode(localId, {
84464
+ attrs: { ...owner.n.attrs, projectAnchorsContributedDigest: dg }
84465
+ });
84466
+ }
84410
84467
  for (const [localId, dg] of Object.entries(instances.semanticEdgeDigests)) {
84411
84468
  const owner = owningStore(localId);
84412
84469
  if (!owner) continue;
@@ -84425,7 +84482,8 @@ async function startMultiDaemon(opts = {}) {
84425
84482
  // node; flat at 1 with `blockedRepeat` 0 is a different node
84426
84483
  // failing every pass. Same total, different bug.
84427
84484
  blockedRepeat,
84428
- rejectedRepeat
84485
+ rejectedRepeat,
84486
+ edgesRejected: rejectedEdges.length
84429
84487
  });
84430
84488
  return res.accepted;
84431
84489
  });
@@ -85762,15 +85820,26 @@ async function cmdStatus() {
85762
85820
  try {
85763
85821
  let pending = 0;
85764
85822
  let total = 0;
85823
+ let heldRefused = 0;
85824
+ let autoMint = 0;
85765
85825
  for (const label of ["Problem", "Solution", "RootCause"]) {
85766
85826
  for (const n of store.findNodesByLabel(label)) {
85767
85827
  if (n.attrs["source"] === "cloud") continue;
85768
85828
  total++;
85769
85829
  const c = n.attrs["contributedAtSeq"];
85770
- if (typeof c !== "number" || (n.lastReinforcedAtSeq ?? 0) > c) pending++;
85830
+ if (typeof c === "number" && (n.lastReinforcedAtSeq ?? 0) <= c) continue;
85831
+ if (label === "Solution" && n.description.startsWith(AUTO_MINT_PREFIX)) autoMint++;
85832
+ else if (isTerminallyRefused(n)) heldRefused++;
85833
+ else pending++;
85771
85834
  }
85772
85835
  }
85773
- console.log(` contribute: ${pending} pending of ${total} instance node(s)`);
85836
+ const held = [
85837
+ ...heldRefused > 0 ? [`${heldRefused} held after identical door refusals`] : [],
85838
+ ...autoMint > 0 ? [`${autoMint} auto-close marker(s) that never ship`] : []
85839
+ ];
85840
+ console.log(
85841
+ ` contribute: ${pending} pending of ${total} instance node(s)${held.length > 0 ? ` \xB7 ${held.join(" \xB7 ")}` : ""}`
85842
+ );
85774
85843
  const rejections = store.edgeRejections();
85775
85844
  if (rejections.length > 0) {
85776
85845
  const refused = rejections.reduce((n, r) => n + r.count, 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.6-dev.2063",
3
+ "version": "2.0.6-dev.2081",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {