@inerrata-corporation/errata 2.0.2-dev.876 → 2.0.2-dev.886

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 +133 -70
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -22197,7 +22197,10 @@ function evaluateMechanisms(store, invocations = /* @__PURE__ */ new Map(), mech
22197
22197
  return mechanisms.map((d) => {
22198
22198
  const fed = evaluateFed(store, d);
22199
22199
  const inv = invocations.get(d.pass);
22200
- const effectCount = inv?.totals[d.effectCounter] ?? 0;
22200
+ const effectCount = d.effectIsLevel ? Math.max(
22201
+ 0,
22202
+ (inv?.lastTotals?.[d.effectCounter] ?? 0) - (inv?.firstTotals?.[d.effectCounter] ?? 0)
22203
+ ) : inv?.totals[d.effectCounter] ?? 0;
22201
22204
  const backlog = d.backlogCounter ? inv?.lastTotals?.[d.backlogCounter] : void 0;
22202
22205
  const backlogWas = d.backlogCounter ? inv?.firstTotals?.[d.backlogCounter] : void 0;
22203
22206
  const span = inv?.firstTs === void 0 ? 0 : inv.lastTs - inv.firstTs;
@@ -22394,7 +22397,11 @@ var init_mechanism_liveness = __esm({
22394
22397
  // this store's measured state on 2026-08-09, day one of WM-labels.
22395
22398
  fed: { labels: ["Problem"], attr: "lastReinforceExposure", minFraction: 0 },
22396
22399
  // A LEVEL, not an increment: nonzero once any node ever carried an outcome.
22400
+ // Which is precisely why it must be read as movement — measured across the
22401
+ // 103 retained rows it never left 1, and the summed reading called that
22402
+ // "effect 103, ok".
22397
22403
  effectCounter: "reinforcedNodes",
22404
+ effectIsLevel: true,
22398
22405
  note: "levels ride the wm-calibration row; expect no-effect until outcome data accrues"
22399
22406
  },
22400
22407
  {
@@ -55763,9 +55770,6 @@ function saveWitnessQueue(path2, queue) {
55763
55770
  } catch {
55764
55771
  }
55765
55772
  }
55766
- function pruneWitnessQueue(queue, now) {
55767
- return pruneWitnessQueueWithDispositions(queue, now).queue;
55768
- }
55769
55773
  function pruneWitnessQueueWithDispositions(queue, now) {
55770
55774
  const dropped = { expiredTtl: 0, exhaustedAttempts: 0, cappedOverflow: 0 };
55771
55775
  const live2 = [];
@@ -55780,15 +55784,20 @@ function pruneWitnessQueueWithDispositions(queue, now) {
55780
55784
  }
55781
55785
  return { queue: live2, dropped };
55782
55786
  }
55783
- function enqueueWitnesses(queue, fresh, now) {
55787
+ function enqueueWitnessesWithDispositions(queue, fresh, now) {
55784
55788
  const byKey = new Map(queue.map((w) => [`${w.channel}:${w.witnessKey}`, w]));
55785
55789
  for (const f of fresh) {
55786
55790
  const k = `${f.channel}:${f.witnessKey}`;
55791
+ const counted = f.counted !== false;
55787
55792
  const existing = byKey.get(k);
55788
- if (existing) existing.attempts += 1;
55789
- else byKey.set(k, { ...f, ts: now, attempts: 1 });
55793
+ if (existing) {
55794
+ if (counted) existing.attempts += 1;
55795
+ } else {
55796
+ const { counted: _drop, ...w } = f;
55797
+ byKey.set(k, { ...w, ts: now, attempts: counted ? 1 : 0 });
55798
+ }
55790
55799
  }
55791
- return pruneWitnessQueue([...byKey.values()], now);
55800
+ return pruneWitnessQueueWithDispositions([...byKey.values()], now);
55792
55801
  }
55793
55802
  function pendingFor(queue, channel) {
55794
55803
  return queue.filter((w) => w.channel === channel);
@@ -56034,52 +56043,6 @@ function maybeFlushDigests() {
56034
56043
 
56035
56044
  // src/liveness-watch.ts
56036
56045
  init_src5();
56037
- var LIVENESS_WATCH_INTERVAL_MS = 60 * 60 * 1e3;
56038
- var ALARM_RENOTIFY_MS = 24 * 60 * 60 * 1e3;
56039
- function createLivenessWatch(deps) {
56040
- let lastRunAt = 0;
56041
- const lastAlarmedAt = /* @__PURE__ */ new Map();
56042
- return {
56043
- maybeRun(nowMs = Date.now()) {
56044
- if (nowMs - lastRunAt < LIVENESS_WATCH_INTERVAL_MS) return null;
56045
- lastRunAt = nowMs;
56046
- try {
56047
- const started2 = Date.now();
56048
- const invocations = /* @__PURE__ */ new Map();
56049
- for (const [kind, s] of summarizePassLedger(deps.configDir)) {
56050
- invocations.set(kind, {
56051
- lastTs: s.lastTs,
56052
- firstTs: s.firstTs,
56053
- runs: s.runs,
56054
- totals: s.totals,
56055
- firstTotals: s.firstTotals,
56056
- lastTotals: s.lastTotals
56057
- });
56058
- }
56059
- const rows = evaluateMechanisms(deps.store, invocations);
56060
- const workspaceActive = invocations.has("capture") || invocations.has("render");
56061
- const bad = workspaceActive ? rows.filter((r) => hasStarvedMechanism([r])) : [];
56062
- appendPassLedger(deps.configDir, "liveness-watch", Date.now() - started2, {
56063
- mechanisms: rows.length,
56064
- alarms: bad.length
56065
- });
56066
- if (bad.length > 0) {
56067
- const lines = formatMechanismStatus(rows, nowMs);
56068
- for (const r of bad) {
56069
- const at = lastAlarmedAt.get(r.id) ?? 0;
56070
- if (nowMs - at < ALARM_RENOTIFY_MS) continue;
56071
- lastAlarmedAt.set(r.id, nowMs);
56072
- const line = lines.find((l) => l.includes(r.id)) ?? `${r.verdict} ${r.id}`;
56073
- deps.onAlarm(r.id, `${line.trim()} \u2014 ${r.what}`);
56074
- }
56075
- }
56076
- return rows;
56077
- } catch {
56078
- return null;
56079
- }
56080
- }
56081
- };
56082
- }
56083
56046
 
56084
56047
  // src/loop-lag.ts
56085
56048
  var HEARTBEAT_MS = 500;
@@ -56154,8 +56117,69 @@ function watchCensusLine(c = watchCensus()) {
56154
56117
  return base;
56155
56118
  }
56156
56119
 
56120
+ // src/liveness-watch.ts
56121
+ var LIVENESS_WATCH_INTERVAL_MS = 60 * 60 * 1e3;
56122
+ var ALARM_RENOTIFY_MS = 24 * 60 * 60 * 1e3;
56123
+ function createLivenessWatch(deps) {
56124
+ let lastRunAt = 0;
56125
+ const lastAlarmedAt = /* @__PURE__ */ new Map();
56126
+ return {
56127
+ maybeRun(nowMs = Date.now()) {
56128
+ if (nowMs - lastRunAt < LIVENESS_WATCH_INTERVAL_MS) return null;
56129
+ lastRunAt = nowMs;
56130
+ try {
56131
+ const started2 = Date.now();
56132
+ const invocations = /* @__PURE__ */ new Map();
56133
+ for (const [kind, s] of summarizePassLedger(deps.configDir)) {
56134
+ invocations.set(kind, {
56135
+ lastTs: s.lastTs,
56136
+ firstTs: s.firstTs,
56137
+ runs: s.runs,
56138
+ totals: s.totals,
56139
+ firstTotals: s.firstTotals,
56140
+ lastTotals: s.lastTotals
56141
+ });
56142
+ }
56143
+ const rows = evaluateMechanisms(deps.store, invocations);
56144
+ const workspaceActive = invocations.has("capture") || invocations.has("render");
56145
+ const bad = workspaceActive ? rows.filter((r) => hasStarvedMechanism([r])) : [];
56146
+ const watch2 = watchCensus();
56147
+ const pass = passState();
56148
+ appendPassLedger(deps.configDir, "liveness-watch", Date.now() - started2, {
56149
+ mechanisms: rows.length,
56150
+ alarms: bad.length,
56151
+ ...Object.fromEntries(bad.map((r) => [`starved:${r.id}`, 1])),
56152
+ ...deps.witnessParked ? { witnessParked: deps.witnessParked() } : {},
56153
+ watchDispatched: watch2.dispatched,
56154
+ watchEmitted: watch2.emitted,
56155
+ watchLogged: watch2.logged,
56156
+ watchRejected: watch2.rejected,
56157
+ // Concurrency and the longest single holder — the two readings that
56158
+ // named the 6017s zombie pass. A pass legitimately running for hours
56159
+ // and one wedged forever look identical without a trend.
56160
+ passesActive: pass.active.length,
56161
+ passLongestRunningMs: pass.active.reduce((m, p) => Math.max(m, p.runningMs), 0)
56162
+ });
56163
+ if (bad.length > 0) {
56164
+ const lines = formatMechanismStatus(rows, nowMs);
56165
+ for (const r of bad) {
56166
+ const at = lastAlarmedAt.get(r.id) ?? 0;
56167
+ if (nowMs - at < ALARM_RENOTIFY_MS) continue;
56168
+ lastAlarmedAt.set(r.id, nowMs);
56169
+ const line = lines.find((l) => l.includes(r.id)) ?? `${r.verdict} ${r.id}`;
56170
+ deps.onAlarm(r.id, `${line.trim()} \u2014 ${r.what}`);
56171
+ }
56172
+ }
56173
+ return rows;
56174
+ } catch {
56175
+ return null;
56176
+ }
56177
+ }
56178
+ };
56179
+ }
56180
+
56157
56181
  // src/engine.ts
56158
- var DAEMON_VERSION = true ? "2.0.2-dev.876" : "2.0.0-alpha.0";
56182
+ var DAEMON_VERSION = true ? "2.0.2-dev.886" : "2.0.0-alpha.0";
56159
56183
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
56160
56184
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
56161
56185
  var GIT_OP_MUTE_MS = 4e3;
@@ -56501,7 +56525,11 @@ function createWorkspaceEngine(opts) {
56501
56525
  onAlarm: (id, line) => {
56502
56526
  console.warn(`[errata] mechanism not working: ${line}`);
56503
56527
  notifyEvent("mechanism-stalled", line, { key: id });
56504
- }
56528
+ },
56529
+ // Read through a closure, not captured once: `witnessQueue` is reassigned
56530
+ // on every harvest, so a snapshot would freeze the depth at boot — which is
56531
+ // the same in-memory-only blindness this counter exists to end.
56532
+ witnessParked: () => witnessQueue.length
56505
56533
  });
56506
56534
  const intents = createIntentState();
56507
56535
  const toolRuns = createToolRunState();
@@ -57354,14 +57382,11 @@ function createWorkspaceEngine(opts) {
57354
57382
  const citingSession = sessionOriginKey(sessionId);
57355
57383
  const mintedHere = (nodeId) => store.getNode(nodeId)?.attrs["sources"]?.[0] === sessionId;
57356
57384
  const sendWitnesses = async (channel, fresh, send) => {
57357
- const { replay: queued, waiting } = partitionReplayable(
57358
- witnessQueue,
57359
- channel,
57360
- (nodeId) => {
57361
- const n = store.getNode(nodeId);
57362
- return !n || n.attrs["contributedAtSeq"] != null;
57363
- }
57364
- );
57385
+ const isShipped = (nodeId) => {
57386
+ const n = store.getNode(nodeId);
57387
+ return !n || n.attrs["contributedAtSeq"] != null;
57388
+ };
57389
+ const { replay: queued, waiting } = partitionReplayable(witnessQueue, channel, isShipped);
57365
57390
  if (fresh.length === 0 && queued.length === 0) {
57366
57391
  if (waiting.length > 0) {
57367
57392
  console.log(`[errata] ${channel}: ${waiting.length} parked awaiting node drain`);
@@ -57400,17 +57425,31 @@ function createWorkspaceEngine(opts) {
57400
57425
  selfGated += d.selfGated;
57401
57426
  const missing = new Set(d.unmatchedIds);
57402
57427
  for (const it of items2) {
57403
- if (missing.has(it.nodeId)) stillUnmatched.push({ channel, ...it, sessionKey: session });
57428
+ if (missing.has(it.nodeId)) stillUnmatched.push({ channel, ...it, sessionKey: session, counted: isShipped(it.nodeId) });
57404
57429
  else settled.add(it.witnessKey);
57405
57430
  }
57406
57431
  } catch (err2) {
57407
57432
  console.warn(`[errata] ${channel} transport failed (witnesses kept for retry):`, err2 instanceof Error ? err2.message : err2);
57408
- for (const it of items2) stillUnmatched.push({ channel, ...it, sessionKey: session });
57433
+ for (const it of items2) stillUnmatched.push({ channel, ...it, sessionKey: session, counted: false });
57409
57434
  }
57410
57435
  }
57436
+ const recovered = queued.filter((q) => settled.has(q.witnessKey)).length;
57411
57437
  witnessQueue = retireWitnesses(witnessQueue, channel, settled);
57412
- witnessQueue = enqueueWitnesses(witnessQueue, stillUnmatched, Date.now());
57438
+ const enq = enqueueWitnessesWithDispositions(witnessQueue, stillUnmatched, Date.now());
57439
+ witnessQueue = enq.queue;
57413
57440
  saveWitnessQueue(witnessQueuePath(paths.configDir), witnessQueue);
57441
+ const lost = enq.dropped.expiredTtl + enq.dropped.exhaustedAttempts + enq.dropped.cappedOverflow;
57442
+ if (lost > 0 || recovered > 0) {
57443
+ appendPassLedger(paths.configDir, "witness-queue", 0, {
57444
+ recovered,
57445
+ replayed: queued.length,
57446
+ awaitingDrain: waiting.length,
57447
+ parked: pendingFor(witnessQueue, channel).length,
57448
+ expiredTtl: enq.dropped.expiredTtl,
57449
+ exhaustedAttempts: enq.dropped.exhaustedAttempts,
57450
+ cappedOverflow: enq.dropped.cappedOverflow
57451
+ });
57452
+ }
57414
57453
  console.log(
57415
57454
  `[errata] ${channel}: ${formatDisposition({ recorded, unmatched, duplicate, selfGated })}` + (queued.length > 0 ? ` (incl. ${queued.length} replayed)` : "") + (waiting.length > 0 ? ` (${waiting.length} awaiting drain)` : "")
57416
57455
  );
@@ -60421,7 +60460,11 @@ async function startMultiDaemon(opts = {}) {
60421
60460
  ...compositionCounters(composition),
60422
60461
  accepted: 0,
60423
60462
  rejected: 0,
60424
- blocked: 0
60463
+ blocked: 0,
60464
+ // Same shape as the drained row: a first/last trend reading must not
60465
+ // straddle rows where the counter is merely absent.
60466
+ blockedRepeat: 0,
60467
+ rejectedRepeat: 0
60425
60468
  });
60426
60469
  }
60427
60470
  if (instances) {
@@ -60462,15 +60505,29 @@ async function startMultiDaemon(opts = {}) {
60462
60505
  if (sh) return { s: sharedStore, n: sh };
60463
60506
  return null;
60464
60507
  };
60508
+ let blockedRepeat = 0;
60509
+ let rejectedRepeat = 0;
60510
+ const noteRefused = (id, repeat) => {
60511
+ const owner = owningStore(id);
60512
+ if (!owner) return;
60513
+ if (owner.n.attrs["shipRefusedSinceSeq"] !== void 0) repeat();
60514
+ else
60515
+ owner.s.updateNode(id, {
60516
+ attrs: { ...owner.n.attrs, shipRefusedSinceSeq: seq }
60517
+ });
60518
+ };
60519
+ for (const id of blocked) noteRefused(id, () => blockedRepeat++);
60520
+ for (const id of rejected.keys()) noteRefused(id, () => rejectedRepeat++);
60465
60521
  for (const n of instances.nodes) {
60466
60522
  const owner = owningStore(n.id);
60467
60523
  if (!owner || !["Problem", "Solution", "RootCause"].includes(owner.n.label)) continue;
60468
60524
  if (blocked.has(n.id)) continue;
60469
60525
  if (rejected.has(n.id)) continue;
60470
60526
  const cloudNodeId = cloudIdByLocal.get(n.id);
60527
+ const { shipRefusedSinceSeq: _cleared, ...carried } = owner.n.attrs;
60471
60528
  owner.s.updateNode(n.id, {
60472
60529
  attrs: {
60473
- ...owner.n.attrs,
60530
+ ...carried,
60474
60531
  contributedAtSeq: seq,
60475
60532
  ...cloudNodeId && cloudNodeId !== n.id ? { cloudNodeId } : {}
60476
60533
  }
@@ -60496,7 +60553,13 @@ async function startMultiDaemon(opts = {}) {
60496
60553
  ...compositionCounters(instances.composition),
60497
60554
  accepted: res.accepted,
60498
60555
  rejected: rejected.size,
60499
- blocked: blocked.size
60556
+ blocked: blocked.size,
60557
+ // The half of the refusal count that is NOT news. `blocked` flat
60558
+ // at 1 with `blockedRepeat` also 1 is one permanently stranded
60559
+ // node; flat at 1 with `blockedRepeat` 0 is a different node
60560
+ // failing every pass. Same total, different bug.
60561
+ blockedRepeat,
60562
+ rejectedRepeat
60500
60563
  });
60501
60564
  return res.accepted;
60502
60565
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.2-dev.876",
3
+ "version": "2.0.2-dev.886",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {