@adhdev/daemon-standalone 1.0.58-rc.26 → 1.0.58-rc.28

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.
package/dist/index.js CHANGED
@@ -31672,9 +31672,9 @@ var require_streamx = __commonJS({
31672
31672
  }
31673
31673
  });
31674
31674
 
31675
- // ../daemon-core/node_modules/b4a/index.js
31675
+ // ../../node_modules/tar-stream/node_modules/b4a/index.js
31676
31676
  var require_b4a2 = __commonJS({
31677
- "../daemon-core/node_modules/b4a/index.js"(exports2, module2) {
31677
+ "../../node_modules/tar-stream/node_modules/b4a/index.js"(exports2, module2) {
31678
31678
  "use strict";
31679
31679
  function isBuffer(value) {
31680
31680
  return Buffer.isBuffer(value) || value instanceof Uint8Array;
@@ -31830,9 +31830,9 @@ var require_b4a2 = __commonJS({
31830
31830
  }
31831
31831
  });
31832
31832
 
31833
- // ../daemon-core/node_modules/tar-stream/headers.js
31833
+ // ../../node_modules/tar-stream/headers.js
31834
31834
  var require_headers = __commonJS({
31835
- "../daemon-core/node_modules/tar-stream/headers.js"(exports2) {
31835
+ "../../node_modules/tar-stream/headers.js"(exports2) {
31836
31836
  "use strict";
31837
31837
  var b4a = require_b4a2();
31838
31838
  var ZEROS = "0000000000000000000";
@@ -32093,9 +32093,9 @@ var require_headers = __commonJS({
32093
32093
  }
32094
32094
  });
32095
32095
 
32096
- // ../daemon-core/node_modules/tar-stream/extract.js
32096
+ // ../../node_modules/tar-stream/extract.js
32097
32097
  var require_extract = __commonJS({
32098
- "../daemon-core/node_modules/tar-stream/extract.js"(exports2, module2) {
32098
+ "../../node_modules/tar-stream/extract.js"(exports2, module2) {
32099
32099
  "use strict";
32100
32100
  var { Writable, Readable, getStreamError } = require_streamx();
32101
32101
  var FIFO = require_fast_fifo();
@@ -32438,9 +32438,9 @@ var require_extract = __commonJS({
32438
32438
  }
32439
32439
  });
32440
32440
 
32441
- // ../daemon-core/node_modules/tar-stream/constants.js
32441
+ // ../../node_modules/tar-stream/constants.js
32442
32442
  var require_constants = __commonJS({
32443
- "../daemon-core/node_modules/tar-stream/constants.js"(exports2, module2) {
32443
+ "../../node_modules/tar-stream/constants.js"(exports2, module2) {
32444
32444
  "use strict";
32445
32445
  var constants = {
32446
32446
  // just for envs without fs
@@ -32459,9 +32459,9 @@ var require_constants = __commonJS({
32459
32459
  }
32460
32460
  });
32461
32461
 
32462
- // ../daemon-core/node_modules/tar-stream/pack.js
32462
+ // ../../node_modules/tar-stream/pack.js
32463
32463
  var require_pack = __commonJS({
32464
- "../daemon-core/node_modules/tar-stream/pack.js"(exports2, module2) {
32464
+ "../../node_modules/tar-stream/pack.js"(exports2, module2) {
32465
32465
  "use strict";
32466
32466
  var { Readable, Writable, getStreamError } = require_streamx();
32467
32467
  var b4a = require_b4a2();
@@ -32694,9 +32694,9 @@ var require_pack = __commonJS({
32694
32694
  }
32695
32695
  });
32696
32696
 
32697
- // ../daemon-core/node_modules/tar-stream/index.js
32697
+ // ../../node_modules/tar-stream/index.js
32698
32698
  var require_tar_stream = __commonJS({
32699
- "../daemon-core/node_modules/tar-stream/index.js"(exports2) {
32699
+ "../../node_modules/tar-stream/index.js"(exports2) {
32700
32700
  "use strict";
32701
32701
  exports2.extract = require_extract();
32702
32702
  exports2.pack = require_pack();
@@ -32939,9 +32939,9 @@ var require_pump = __commonJS({
32939
32939
  }
32940
32940
  });
32941
32941
 
32942
- // ../daemon-core/node_modules/tar-fs/index.js
32942
+ // ../../node_modules/tar-fs/index.js
32943
32943
  var require_tar_fs = __commonJS({
32944
- "../daemon-core/node_modules/tar-fs/index.js"(exports2) {
32944
+ "../../node_modules/tar-fs/index.js"(exports2) {
32945
32945
  "use strict";
32946
32946
  var tar = require_tar_stream();
32947
32947
  var pump = require_pump();
@@ -36885,10 +36885,10 @@ var require_dist3 = __commonJS({
36885
36885
  }
36886
36886
  function getDaemonBuildInfo() {
36887
36887
  if (cached2) return cached2;
36888
- const commit = readInjected(true ? "9d74c9f0f92fde5a57a60e0068576d1868701f35" : void 0) ?? "unknown";
36889
- const commitShort = readInjected(true ? "9d74c9f0" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
36890
- const version2 = readInjected(true ? "1.0.58-rc.26" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
36891
- const builtAt = readInjected(true ? "2026-08-27T06:32:25.171Z" : void 0);
36888
+ const commit = readInjected(true ? "8b689b3eaa810e0c92ffdf0f7317278e496ae782" : void 0) ?? "unknown";
36889
+ const commitShort = readInjected(true ? "8b689b3e" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
36890
+ const version2 = readInjected(true ? "1.0.58-rc.28" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
36891
+ const builtAt = readInjected(true ? "2026-08-27T11:26:23.456Z" : void 0);
36892
36892
  cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
36893
36893
  return cached2;
36894
36894
  }
@@ -51017,7 +51017,7 @@ The instruction it carried was never delivered to anyone. If it still matters, r
51017
51017
  }
51018
51018
  });
51019
51019
  var require_canonicalize = __commonJS2({
51020
- "../../node_modules/canonicalize/lib/canonicalize.js"(exports22, module22) {
51020
+ "../../vendor/seqscribe/node_modules/canonicalize/lib/canonicalize.js"(exports22, module22) {
51021
51021
  "use strict";
51022
51022
  module22.exports = function serialize(object2) {
51023
51023
  if (typeof object2 === "number" && isNaN(object2)) {
@@ -51127,7 +51127,7 @@ The instruction it carried was never delivered to anyone. If it still matters, r
51127
51127
  var hexes;
51128
51128
  var Hash;
51129
51129
  var init_utils = __esm2({
51130
- "../../node_modules/@noble/hashes/esm/utils.js"() {
51130
+ "../../vendor/seqscribe/node_modules/@noble/hashes/esm/utils.js"() {
51131
51131
  "use strict";
51132
51132
  hasHexBuiltin = /* @__PURE__ */ (() => (
51133
51133
  // @ts-ignore
@@ -51159,7 +51159,7 @@ The instruction it carried was never delivered to anyone. If it still matters, r
51159
51159
  var HashMD;
51160
51160
  var SHA256_IV;
51161
51161
  var init_md = __esm2({
51162
- "../../node_modules/@noble/hashes/esm/_md.js"() {
51162
+ "../../vendor/seqscribe/node_modules/@noble/hashes/esm/_md.js"() {
51163
51163
  "use strict";
51164
51164
  init_utils();
51165
51165
  HashMD = class extends Hash {
@@ -51269,7 +51269,7 @@ The instruction it carried was never delivered to anyone. If it still matters, r
51269
51269
  var SHA256;
51270
51270
  var sha256;
51271
51271
  var init_sha2 = __esm2({
51272
- "../../node_modules/@noble/hashes/esm/sha2.js"() {
51272
+ "../../vendor/seqscribe/node_modules/@noble/hashes/esm/sha2.js"() {
51273
51273
  "use strict";
51274
51274
  init_md();
51275
51275
  init_utils();
@@ -51415,7 +51415,7 @@ The instruction it carried was never delivered to anyone. If it still matters, r
51415
51415
  });
51416
51416
  var sha2562;
51417
51417
  var init_sha256 = __esm2({
51418
- "../../node_modules/@noble/hashes/esm/sha256.js"() {
51418
+ "../../vendor/seqscribe/node_modules/@noble/hashes/esm/sha256.js"() {
51419
51419
  "use strict";
51420
51420
  init_sha2();
51421
51421
  sha2562 = sha256;
@@ -52009,9 +52009,20 @@ The instruction it carried was never delivered to anyone. If it still matters, r
52009
52009
  });
52010
52010
  function betterSqlite3Handle(db, lockDb) {
52011
52011
  let owned = false;
52012
+ const stmts = /* @__PURE__ */ new Map();
52013
+ const prep = (sql) => {
52014
+ let s2 = stmts.get(sql);
52015
+ if (s2 === void 0) {
52016
+ if (stmts.size >= 512)
52017
+ stmts.clear();
52018
+ s2 = db.prepare(sql);
52019
+ stmts.set(sql, s2);
52020
+ }
52021
+ return s2;
52022
+ };
52012
52023
  return {
52013
52024
  run(sql, params = []) {
52014
- const stmt = db.prepare(sql);
52025
+ const stmt = prep(sql);
52015
52026
  if (stmt.reader) {
52016
52027
  stmt.all(...params);
52017
52028
  return { changes: 0, lastInsertRowid: 0 };
@@ -52020,10 +52031,10 @@ The instruction it carried was never delivered to anyone. If it still matters, r
52020
52031
  return { changes: r.changes, lastInsertRowid: r.lastInsertRowid };
52021
52032
  },
52022
52033
  get(sql, params = []) {
52023
- return db.prepare(sql).get(...params);
52034
+ return prep(sql).get(...params);
52024
52035
  },
52025
52036
  all(sql, params = []) {
52026
- return db.prepare(sql).all(...params);
52037
+ return prep(sql).all(...params);
52027
52038
  },
52028
52039
  transaction(fn) {
52029
52040
  return db.transaction(fn)();
@@ -52063,7 +52074,7 @@ The instruction it carried was never delivered to anyone. If it still matters, r
52063
52074
  var HMAC;
52064
52075
  var hmac;
52065
52076
  var init_hmac = __esm2({
52066
- "../../node_modules/@noble/hashes/esm/hmac.js"() {
52077
+ "../../vendor/seqscribe/node_modules/@noble/hashes/esm/hmac.js"() {
52067
52078
  "use strict";
52068
52079
  init_utils();
52069
52080
  HMAC = class extends Hash {
@@ -52285,9 +52296,15 @@ The instruction it carried was never delivered to anyone. If it still matters, r
52285
52296
  this.deps.timers.clearTimeout(c.retryTimer);
52286
52297
  c.retryTimer = null;
52287
52298
  }
52299
+ this.rejectWaiters(c, misuse("node closed before catch-up"));
52288
52300
  }
52289
52301
  this.consumers.clear();
52290
52302
  }
52303
+ rejectWaiters(c, err) {
52304
+ const ws = c.waiters.splice(0);
52305
+ for (const w of ws)
52306
+ w.reject(err);
52307
+ }
52291
52308
  onEntry(topic, name, cb) {
52292
52309
  if (this.closed)
52293
52310
  throw misuse("node is closed");
@@ -52307,7 +52324,8 @@ The instruction it carried was never delivered to anyone. If it still matters, r
52307
52324
  rerun: false,
52308
52325
  failures: 0,
52309
52326
  retryTimer: null,
52310
- gone: false
52327
+ gone: false,
52328
+ waiters: []
52311
52329
  };
52312
52330
  this.consumers.set(key2, c);
52313
52331
  this.pump(c);
@@ -52315,9 +52333,102 @@ The instruction it carried was never delivered to anyone. If it still matters, r
52315
52333
  c.gone = true;
52316
52334
  if (c.retryTimer !== null)
52317
52335
  this.deps.timers.clearTimeout(c.retryTimer);
52336
+ this.rejectWaiters(c, misuse(`consumer unsubscribed before catch-up: ${key2}`));
52318
52337
  this.consumers.delete(key2);
52319
52338
  };
52320
52339
  }
52340
+ // ---- durable-consumer lifecycle (proposals-v3.5 P17–P19) ----
52341
+ isActive(topic, name) {
52342
+ return this.consumers.has(`${topic} ${name}`);
52343
+ }
52344
+ assertInactive(topic, name, op) {
52345
+ if (this.closed)
52346
+ throw misuse("node is closed");
52347
+ this.deps.topics.get(topic);
52348
+ if (this.consumers.has(`${topic} ${name}`))
52349
+ throw misuse(`${op} on active consumer ${topic} ${name} \u2014 unsubscribe first`);
52350
+ }
52351
+ // P17: discard-derived-state rebuild for the SAME stable consumer name.
52352
+ // Inactive-only (registration would race the cursor write); the next
52353
+ // onEntry with this name resumes at the installed boundary. NOTE: the reset
52354
+ // materializes a cursor row, which gates §7.6 archiving from this moment —
52355
+ // re-register promptly, or the row ages into consumer_abandoned.
52356
+ resetConsumer(topic, name, o) {
52357
+ this.assertInactive(topic, name, "resetConsumer");
52358
+ const from = o?.from ?? "earliest-retained";
52359
+ const existed = this.deps.store.cursorsForTopic(topic).some((c) => c.consumer === name);
52360
+ const replayFromRowid = from === "head" ? this.deps.store.maxRowid(topic) : 0;
52361
+ this.deps.store.cursorSet(name, topic, replayFromRowid, new Date(this.deps.clock()).toISOString());
52362
+ return {
52363
+ existed,
52364
+ from,
52365
+ replayFromRowid,
52366
+ archivedRows: from === "earliest-retained" ? this.deps.store.archivedCount(topic) : 0
52367
+ };
52368
+ }
52369
+ // P18: explicit durable-cursor deletion — never implicit (forgetting a
52370
+ // cursor changes at-least-once restart semantics). Inactive-only; a deleted
52371
+ // cursor no longer gates archiving, so the archive floor re-evaluates.
52372
+ deleteConsumer(topic, name) {
52373
+ this.assertInactive(topic, name, "deleteConsumer");
52374
+ const existed = this.deps.store.cursorsForTopic(topic).some((c) => c.consumer === name);
52375
+ if (existed) {
52376
+ this.deps.store.cursorDelete(name, topic);
52377
+ this.onAdvance?.(topic);
52378
+ }
52379
+ return { existed };
52380
+ }
52381
+ listConsumers(topic) {
52382
+ if (this.closed)
52383
+ throw misuse("node is closed");
52384
+ this.deps.topics.get(topic);
52385
+ return this.deps.store.cursorsForTopic(topic).map((c) => ({ ...c, active: this.isActive(topic, c.consumer) }));
52386
+ }
52387
+ // P18 GC: deliberate cleanup of versioned/renamed consumers. Only inactive
52388
+ // cursors are eligible; filters compose (prefix AND inactiveBefore).
52389
+ pruneConsumers(topic, o) {
52390
+ if (this.closed)
52391
+ throw misuse("node is closed");
52392
+ this.deps.topics.get(topic);
52393
+ const pruned = [];
52394
+ for (const c of this.deps.store.cursorsForTopic(topic)) {
52395
+ if (this.isActive(topic, c.consumer))
52396
+ continue;
52397
+ if (o?.prefix !== void 0 && !c.consumer.startsWith(o.prefix))
52398
+ continue;
52399
+ if (o?.inactiveBefore !== void 0) {
52400
+ const at = Date.parse(c.updatedAt);
52401
+ if (!Number.isFinite(at) || at >= o.inactiveBefore)
52402
+ continue;
52403
+ }
52404
+ this.deps.store.cursorDelete(c.consumer, topic);
52405
+ pruned.push(c.consumer);
52406
+ }
52407
+ if (pruned.length > 0)
52408
+ this.onAdvance?.(topic);
52409
+ return pruned;
52410
+ }
52411
+ // P19: deterministic caught-up signal. Snapshots the topic head NOW and
52412
+ // resolves once the durable cursor reaches it (i.e. every entry through that
52413
+ // head has completed its callback); later appends start a new interval.
52414
+ // Callback failure/backoff delays resolution by construction (the cursor
52415
+ // does not advance); unsubscribe and node close reject.
52416
+ caughtUp(topic, name) {
52417
+ try {
52418
+ if (this.closed)
52419
+ throw misuse("node is closed");
52420
+ this.deps.topics.get(topic);
52421
+ const c = this.consumers.get(`${topic} ${name}`);
52422
+ if (!c)
52423
+ throw misuse(`caughtUp on unregistered consumer ${topic} ${name}`);
52424
+ const through = this.deps.store.maxRowid(topic);
52425
+ if (this.deps.store.cursorGet(name, topic) >= through)
52426
+ return Promise.resolve({ throughRowid: through });
52427
+ return new Promise((resolve31, reject) => c.waiters.push({ through, resolve: resolve31, reject }));
52428
+ } catch (e) {
52429
+ return Promise.reject(e);
52430
+ }
52431
+ }
52321
52432
  notifyApplied(topic) {
52322
52433
  for (const c of this.consumers.values()) {
52323
52434
  if (c.topic === topic)
@@ -52379,6 +52490,14 @@ The instruction it carried was never delivered to anyone. If it still matters, r
52379
52490
  c.failures = 0;
52380
52491
  this.deps.store.cursorSet(c.name, c.topic, rowid, new Date(this.deps.clock()).toISOString());
52381
52492
  onProgress();
52493
+ if (c.waiters.length > 0) {
52494
+ const done = c.waiters.filter((w) => rowid >= w.through);
52495
+ if (done.length > 0) {
52496
+ c.waiters = c.waiters.filter((w) => rowid < w.through);
52497
+ for (const w of done)
52498
+ w.resolve({ throughRowid: w.through });
52499
+ }
52500
+ }
52382
52501
  }
52383
52502
  }
52384
52503
  }
@@ -55901,6 +56020,10 @@ CREATE TABLE IF NOT EXISTS sq_archive (
55901
56020
  SyncEngine = class {
55902
56021
  o;
55903
56022
  peers = /* @__PURE__ */ new Map();
56023
+ // P22 observability: cumulative non-applied wire-apply outcomes per topic
56024
+ // (rejected_finality, sealed, forked, dropped_overflow, error) — the spin
56025
+ // was invisible precisely because rejects were silently discarded
56026
+ rejects = /* @__PURE__ */ new Map();
55904
56027
  finalityHub = null;
55905
56028
  subHub = null;
55906
56029
  directiveHub = null;
@@ -56004,6 +56127,8 @@ CREATE TABLE IF NOT EXISTS sq_archive (
56004
56127
  wantQueue: [],
56005
56128
  wantedStreams: /* @__PURE__ */ new Set(),
56006
56129
  recoveryProgress: /* @__PURE__ */ new Map(),
56130
+ syncProgress: /* @__PURE__ */ new Map(),
56131
+ stalled: /* @__PURE__ */ new Set(),
56007
56132
  antiEntropyTimer: null,
56008
56133
  probes: /* @__PURE__ */ new Map(),
56009
56134
  known: /* @__PURE__ */ new Map(),
@@ -56030,9 +56155,13 @@ CREATE TABLE IF NOT EXISTS sq_archive (
56030
56155
  peerId: ps.session.peerId,
56031
56156
  state: ps.session.state(),
56032
56157
  dirtyStreams: ps.dirty.size,
56033
- queuedData: ps.session.queuedData()
56158
+ queuedData: ps.session.queuedData(),
56159
+ stalledStreams: ps.stalled.size
56034
56160
  }));
56035
56161
  }
56162
+ rejectStats(topic) {
56163
+ return { ...this.rejects.get(topic) ?? {} };
56164
+ }
56036
56165
  // ---- lifecycle ----
56037
56166
  onReady(s2) {
56038
56167
  const ps = this.peers.get(s2);
@@ -56479,19 +56608,26 @@ CREATE TABLE IF NOT EXISTS sq_archive (
56479
56608
  }
56480
56609
  if (entry.topic !== m.topic || entry.writer !== m.writer)
56481
56610
  continue;
56482
- applies.push(this.o.core.applyExternal(entry, ps.session.peerId).catch(() => void 0));
56611
+ applies.push(this.o.core.applyExternal(entry, ps.session.peerId).catch(() => "error"));
56483
56612
  }
56484
- void Promise.all(applies).then(() => {
56613
+ void Promise.all(applies).then((results) => {
56614
+ for (const r of results) {
56615
+ if (r === "applied" || r === "duplicate" || r === "pending")
56616
+ continue;
56617
+ const rec = this.rejects.get(m.topic) ?? {};
56618
+ rec[r] = (rec[r] ?? 0) + 1;
56619
+ this.rejects.set(m.topic, rec);
56620
+ }
56485
56621
  if (m.req !== void 0 && m.done) {
56486
56622
  const want = ps.activeWants.get(m.req);
56487
56623
  this.finishWant(ps, m.req);
56488
56624
  if (want) {
56489
56625
  const head = this.o.core.getStream(m.topic, m.writer);
56490
56626
  const target = this.o.core.recoveryTarget(m.topic, m.writer);
56627
+ const skey = `${m.topic} ${m.writer}`;
56491
56628
  if (target) {
56492
- const key3 = `${m.topic} ${m.writer}`;
56493
- const before = ps.recoveryProgress.get(key3);
56494
- ps.recoveryProgress.set(key3, head.contigSeq);
56629
+ const before = ps.recoveryProgress.get(skey);
56630
+ ps.recoveryProgress.set(skey, head.contigSeq);
56495
56631
  if (before !== void 0 && before >= head.contigSeq)
56496
56632
  this.o.core.reportCanonicalUnavailable(m.topic, m.writer);
56497
56633
  else
@@ -56499,8 +56635,22 @@ CREATE TABLE IF NOT EXISTS sq_archive (
56499
56635
  } else if (head.sealReason === null && m.toSeq > head.contigSeq) {
56500
56636
  const cert = this.o.core.getCert(m.topic);
56501
56637
  const awaitingSnapshot = cert !== null && (cert.cut[m.writer]?.seq ?? 0) > head.contigSeq;
56502
- if (!awaitingSnapshot)
56503
- this.queueWant(ps, m.topic, m.writer);
56638
+ if (!awaitingSnapshot) {
56639
+ const before = ps.syncProgress.get(skey);
56640
+ ps.syncProgress.set(skey, head.contigSeq);
56641
+ if (before !== void 0 && before >= head.contigSeq) {
56642
+ if (!ps.stalled.has(skey)) {
56643
+ ps.stalled.add(skey);
56644
+ this.o.emitAnomaly({ kind: "sync_stalled" });
56645
+ }
56646
+ } else {
56647
+ ps.stalled.delete(skey);
56648
+ this.queueWant(ps, m.topic, m.writer);
56649
+ }
56650
+ }
56651
+ } else {
56652
+ ps.syncProgress.delete(skey);
56653
+ ps.stalled.delete(skey);
56504
56654
  }
56505
56655
  }
56506
56656
  }
@@ -57211,6 +57361,49 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57211
57361
  store.close();
57212
57362
  }
57213
57363
  };
57364
+ const SCAN_DEFAULT_LIMIT = 500;
57365
+ const SCAN_MAX_LIMIT = 1e4;
57366
+ const scanEntries = (topic, o = {}) => {
57367
+ if (closed)
57368
+ throw misuse("node is closed");
57369
+ topics.get(topic);
57370
+ const limit = Math.min(Math.max(1, Math.floor(o.limit ?? SCAN_DEFAULT_LIMIT)), SCAN_MAX_LIMIT);
57371
+ if (o.writer !== void 0) {
57372
+ if (o.after !== void 0 || o.through !== void 0)
57373
+ throw misuse("scanEntries: writer form takes fromSeq/toSeq, not after/through");
57374
+ const head = core2.getStream(topic, o.writer);
57375
+ const fromSeq = o.fromSeq ?? 1;
57376
+ const toSeq = o.toSeq ?? head.contigSeq;
57377
+ if (toSeq < fromSeq)
57378
+ return { entries: [], complete: true, truncatedBelow: false };
57379
+ const windowEnd = Math.min(toSeq, fromSeq + limit - 1);
57380
+ const bySeq = /* @__PURE__ */ new Map();
57381
+ for (const e of store.archivedEntries(topic, o.writer, fromSeq, windowEnd))
57382
+ bySeq.set(e.seq, e);
57383
+ for (const { entry } of store.entriesRange(topic, o.writer, fromSeq, windowEnd))
57384
+ bySeq.set(entry.seq, entry);
57385
+ const entries2 = [...bySeq.values()].sort((a, b) => a.seq - b.seq);
57386
+ const complete2 = windowEnd >= toSeq;
57387
+ const firstAvail = entries2[0]?.seq;
57388
+ const truncatedBelow2 = firstAvail !== void 0 ? firstAvail > fromSeq : head.contigSeq >= fromSeq;
57389
+ const r2 = { entries: entries2, complete: complete2, truncatedBelow: truncatedBelow2 };
57390
+ if (!complete2)
57391
+ r2.nextFromSeq = windowEnd + 1;
57392
+ return r2;
57393
+ }
57394
+ if (o.fromSeq !== void 0 || o.toSeq !== void 0)
57395
+ throw misuse("scanEntries: fromSeq/toSeq require writer");
57396
+ const fetched = store.entriesAfterOrder(topic, o.after ?? null, limit + 1);
57397
+ const within = o.through ? fetched.filter((e) => orderCompare(orderOf(e), o.through) <= 0) : fetched;
57398
+ const entries = within.slice(0, limit);
57399
+ const complete = within.length <= limit && (within.length < fetched.length || fetched.length <= limit);
57400
+ const cert = core2.getCert(topic);
57401
+ const truncatedBelow = store.archivedCount(topic) > 0 && (o.after === void 0 || cert !== null && orderCompare(o.after, cert.order) < 0);
57402
+ const r = { entries, complete, truncatedBelow };
57403
+ if (!complete && entries.length > 0)
57404
+ r.nextAfter = orderOf(entries[entries.length - 1]);
57405
+ return r;
57406
+ };
57214
57407
  const stats = () => {
57215
57408
  const out = { topics: {}, peers: sync.peerStats() };
57216
57409
  const now = clock();
@@ -57232,13 +57425,26 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57232
57425
  archived: store.archivedCount(topic),
57233
57426
  finalityGeneration: cert?.generation ?? null,
57234
57427
  certOrderAgeMs: cert ? Math.max(0, now - cert.order.l) : null,
57235
- consumers: consumers2
57428
+ consumers: consumers2,
57429
+ applyRejects: sync.rejectStats(topic)
57236
57430
  };
57237
57431
  }
57238
57432
  return out;
57239
57433
  };
57240
57434
  return Object.assign(node, {
57241
57435
  stats,
57436
+ resetConsumer: (topic, consumer, o) => consumers.resetConsumer(topic, consumer, o),
57437
+ deleteConsumer: (topic, consumer) => consumers.deleteConsumer(topic, consumer),
57438
+ listConsumers: (topic) => consumers.listConsumers(topic),
57439
+ pruneConsumers: (topic, o) => consumers.pruneConsumers(topic, o),
57440
+ consumerCaughtUp: (topic, consumer) => consumers.caughtUp(topic, consumer),
57441
+ scanEntries,
57442
+ headOrder: (topic) => {
57443
+ if (closed)
57444
+ throw misuse("node is closed");
57445
+ topics.get(topic);
57446
+ return store.maxOrderUpTo(topic, Number.MAX_SAFE_INTEGER);
57447
+ },
57242
57448
  _core: core2,
57243
57449
  _views: views,
57244
57450
  _registers: registers,
@@ -57254,6 +57460,7 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57254
57460
  init_beacon();
57255
57461
  init_codec();
57256
57462
  init_consume();
57463
+ init_hlc();
57257
57464
  init_constants();
57258
57465
  init_directives();
57259
57466
  init_errors3();
@@ -57452,6 +57659,12 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57452
57659
  function meshEventsTopic(meshId) {
57453
57660
  return `mesh.${safeMeshId(meshId)}.events`;
57454
57661
  }
57662
+ function meshIdFromEventsTopic(topic) {
57663
+ if (!topic.startsWith("mesh.") || !topic.endsWith(".events")) return null;
57664
+ const segment = topic.slice("mesh.".length, -".events".length);
57665
+ if (segment.length === 0 || segment.includes(".")) return null;
57666
+ return segment;
57667
+ }
57455
57668
  function sessionTranscriptTopic(sessionId) {
57456
57669
  return `session.${safeSessionId(sessionId)}.transcript`;
57457
57670
  }
@@ -57544,6 +57757,193 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57544
57757
  FLEET_STATUS_RING = 50;
57545
57758
  }
57546
57759
  });
57760
+ function diffProjected(expected, actual) {
57761
+ const fields = [];
57762
+ if (expected.ledgerKind !== actual.ledgerKind) fields.push("ledgerKind");
57763
+ if (expected.timestamp !== actual.timestamp) fields.push("timestamp");
57764
+ if (expected.nodeId !== actual.nodeId) fields.push("nodeId");
57765
+ if (expected.sessionId !== actual.sessionId) fields.push("sessionId");
57766
+ if (expected.providerType !== actual.providerType) fields.push("providerType");
57767
+ if (expected.taskId !== actual.taskId) fields.push("taskId");
57768
+ const keys = /* @__PURE__ */ new Set([
57769
+ ...Object.keys(expected.payload ?? {}),
57770
+ ...Object.keys(actual.payload ?? {})
57771
+ ]);
57772
+ for (const key2 of keys) {
57773
+ if (expected.payload?.[key2] !== actual.payload?.[key2]) fields.push(`payload.${key2}`);
57774
+ }
57775
+ return fields;
57776
+ }
57777
+ function readProjected(payload) {
57778
+ if (!payload || typeof payload !== "object") return null;
57779
+ const record2 = payload;
57780
+ if (typeof record2.id !== "string" || record2.id.length === 0) return null;
57781
+ return {
57782
+ id: record2.id,
57783
+ timestamp: typeof record2.timestamp === "string" ? record2.timestamp : "",
57784
+ ledgerKind: typeof record2.ledgerKind === "string" ? record2.ledgerKind : "",
57785
+ nodeId: record2.nodeId ?? null,
57786
+ sessionId: record2.sessionId ?? null,
57787
+ providerType: record2.providerType ?? null,
57788
+ taskId: record2.taskId ?? null,
57789
+ payload: record2.payload ?? {}
57790
+ };
57791
+ }
57792
+ function collectOwnShadowRecords(handle, topic, through) {
57793
+ const records = /* @__PURE__ */ new Map();
57794
+ let truncatedBelow = false;
57795
+ let after;
57796
+ for (let page = 0; page < SHADOW_SCAN_MAX_PAGES; page++) {
57797
+ const result = handle.node.scanEntries(topic, {
57798
+ ...after ? { after } : {},
57799
+ through,
57800
+ limit: SHADOW_SCAN_PAGE
57801
+ });
57802
+ if (result.truncatedBelow) truncatedBelow = true;
57803
+ for (const entry of result.entries) {
57804
+ if (entry.kind !== MESH_EVENT_ENTRY_KIND) continue;
57805
+ if (entry.writer !== handle.writerId) continue;
57806
+ const projected = readProjected(entry.payload);
57807
+ if (projected) records.set(projected.id, projected);
57808
+ }
57809
+ if (result.complete || !result.nextAfter) break;
57810
+ after = result.nextAfter;
57811
+ }
57812
+ return { records, through, truncatedBelow };
57813
+ }
57814
+ function shortId(id) {
57815
+ return id.length <= 12 ? id : `${id.slice(0, 12)}\u2026`;
57816
+ }
57817
+ async function runMeshParityCheck(handle, meshId, ledgerEntries, _opts = {}) {
57818
+ const topic = meshEventsTopic(meshId);
57819
+ const result = { meshId, compared: 0, mismatches: [] };
57820
+ if (!handle.topics.some((d) => d.topic === topic)) {
57821
+ return result;
57822
+ }
57823
+ let window;
57824
+ try {
57825
+ const through = handle.node.headOrder(topic);
57826
+ if (through === null) {
57827
+ window = { records: /* @__PURE__ */ new Map(), through: null, truncatedBelow: false };
57828
+ } else {
57829
+ window = collectOwnShadowRecords(handle, topic, through);
57830
+ }
57831
+ } catch (error48) {
57832
+ LOG2.warn(
57833
+ "Seqscribe",
57834
+ `parity read failed mesh=${meshId}: ${error48 instanceof Error ? error48.message : String(error48)}`
57835
+ );
57836
+ return result;
57837
+ }
57838
+ const shadow = window.records;
57839
+ const seen = /* @__PURE__ */ new Set();
57840
+ let oldestComparedMs = Infinity;
57841
+ for (const entry of ledgerEntries) {
57842
+ result.compared++;
57843
+ seen.add(entry.id);
57844
+ const entryMs = new Date(entry.timestamp).getTime();
57845
+ if (Number.isFinite(entryMs) && entryMs < oldestComparedMs) oldestComparedMs = entryMs;
57846
+ const actual = shadow.get(entry.id);
57847
+ if (!actual) {
57848
+ result.mismatches.push({
57849
+ kind: "missing_in_shadow",
57850
+ id: entry.id,
57851
+ ledgerKind: entry.kind
57852
+ });
57853
+ continue;
57854
+ }
57855
+ const fields = diffProjected(projectMeshLedgerEntry(entry), actual);
57856
+ if (fields.length > 0) {
57857
+ result.mismatches.push({
57858
+ kind: "field_mismatch",
57859
+ id: entry.id,
57860
+ ledgerKind: entry.kind,
57861
+ fields
57862
+ });
57863
+ }
57864
+ }
57865
+ for (const [id, record2] of shadow) {
57866
+ if (seen.has(id)) continue;
57867
+ const recordMs = new Date(record2.timestamp).getTime();
57868
+ if (Number.isFinite(recordMs) && recordMs < oldestComparedMs) continue;
57869
+ result.mismatches.push({
57870
+ kind: "extra_in_shadow",
57871
+ id,
57872
+ ledgerKind: record2.ledgerKind || null
57873
+ });
57874
+ }
57875
+ if (window.truncatedBelow) {
57876
+ LOG2.info(
57877
+ "Seqscribe",
57878
+ `parity window mesh=${meshId} reaches below the archive floor \u2014 shadow records under the cut are not visible to this sweep`
57879
+ );
57880
+ }
57881
+ counters.runs++;
57882
+ counters.compared = result.compared;
57883
+ let missing = 0;
57884
+ let extra = 0;
57885
+ let field2 = 0;
57886
+ for (const m of result.mismatches) {
57887
+ if (m.kind === "missing_in_shadow") missing++;
57888
+ else if (m.kind === "extra_in_shadow") extra++;
57889
+ else field2++;
57890
+ }
57891
+ counters.missingInShadow += missing;
57892
+ counters.extraInShadow += extra;
57893
+ counters.fieldMismatch += field2;
57894
+ counters.mismatches += result.mismatches.length;
57895
+ if (result.mismatches.length > 0) {
57896
+ for (const m of result.mismatches.slice(0, MISMATCH_LOG_CAP)) {
57897
+ LOG2.info(
57898
+ "Seqscribe",
57899
+ `parity mismatch kind=${m.kind} mesh=${meshId} entry=${shortId(m.id)} ledgerKind=${m.ledgerKind ?? "unknown"}` + (m.fields?.length ? ` fields=${m.fields.join(",")}` : "")
57900
+ );
57901
+ }
57902
+ if (result.mismatches.length > MISMATCH_LOG_CAP) {
57903
+ LOG2.info(
57904
+ "Seqscribe",
57905
+ `parity mismatch mesh=${meshId} \u2014 ${result.mismatches.length - MISMATCH_LOG_CAP} further mismatches not logged this run`
57906
+ );
57907
+ }
57908
+ }
57909
+ return result;
57910
+ }
57911
+ function meshParityCounters() {
57912
+ return { ...counters };
57913
+ }
57914
+ function __resetMeshParityForTests() {
57915
+ counters.compared = 0;
57916
+ counters.missingInShadow = 0;
57917
+ counters.extraInShadow = 0;
57918
+ counters.fieldMismatch = 0;
57919
+ counters.mismatches = 0;
57920
+ counters.runs = 0;
57921
+ }
57922
+ var PARITY_CONSUMER;
57923
+ var counters;
57924
+ var SHADOW_SCAN_PAGE;
57925
+ var SHADOW_SCAN_MAX_PAGES;
57926
+ var MISMATCH_LOG_CAP;
57927
+ var init_mesh_parity = __esm2({
57928
+ "src/seqscribe/mesh-parity.ts"() {
57929
+ "use strict";
57930
+ init_logger();
57931
+ init_mesh_event_projection();
57932
+ init_topics2();
57933
+ PARITY_CONSUMER = "stage3-mesh-parity";
57934
+ counters = {
57935
+ compared: 0,
57936
+ missingInShadow: 0,
57937
+ extraInShadow: 0,
57938
+ fieldMismatch: 0,
57939
+ mismatches: 0,
57940
+ runs: 0
57941
+ };
57942
+ SHADOW_SCAN_PAGE = 500;
57943
+ SHADOW_SCAN_MAX_PAGES = 200;
57944
+ MISMATCH_LOG_CAP = 20;
57945
+ }
57946
+ });
57547
57947
  function warnOnce(message) {
57548
57948
  if (warnedOnce.has(message)) return;
57549
57949
  warnedOnce.add(message);
@@ -57564,9 +57964,8 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57564
57964
  malformed: 0
57565
57965
  };
57566
57966
  }
57567
- function consumerNameFor(meshId) {
57568
- const generation = rebuildGeneration.get(meshId) ?? 0;
57569
- return generation === 0 ? READ_MODEL_CONSUMER : `${READ_MODEL_CONSUMER}#${generation + 1}`;
57967
+ function meshReadModelRebuildEpoch(meshId) {
57968
+ return rebuildEpoch.get(meshId) ?? 0;
57570
57969
  }
57571
57970
  function addToBucket(map3, key2, id) {
57572
57971
  if (!key2) return;
@@ -57584,7 +57983,7 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57584
57983
  bucket2.delete(id);
57585
57984
  if (bucket2.size === 0) map3.delete(key2);
57586
57985
  }
57587
- function readProjected(payload) {
57986
+ function readProjected2(payload) {
57588
57987
  if (!payload || typeof payload !== "object") return null;
57589
57988
  const record2 = payload;
57590
57989
  if (typeof record2.id !== "string" || record2.id.length === 0) return null;
@@ -57602,7 +58001,7 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57602
58001
  }
57603
58002
  function ingest(index, entry) {
57604
58003
  if (entry.kind !== MESH_EVENT_ENTRY_KIND) return;
57605
- const event = readProjected(entry.payload);
58004
+ const event = readProjected2(entry.payload);
57606
58005
  if (!event) {
57607
58006
  index.malformed++;
57608
58007
  return;
@@ -57641,7 +58040,7 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57641
58040
  if (existing) return existing;
57642
58041
  const topic = meshEventsTopic(meshId);
57643
58042
  if (!node.topics.some((d) => d.topic === topic)) return null;
57644
- const index = emptyIndex(consumerNameFor(meshId));
58043
+ const index = emptyIndex(READ_MODEL_CONSUMER);
57645
58044
  try {
57646
58045
  index.unsub = node.node.onEntry(topic, index.consumerName, (entry) => {
57647
58046
  ingest(index, entry);
@@ -57735,8 +58134,70 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57735
58134
  }
57736
58135
  indexes.delete(meshId);
57737
58136
  }
57738
- rebuildGeneration.set(meshId, (rebuildGeneration.get(meshId) ?? 0) + 1);
58137
+ const node = activeNode;
58138
+ if (!node) return null;
58139
+ const topic = meshEventsTopic(meshId);
58140
+ if (!node.topics.some((d) => d.topic === topic)) return null;
58141
+ rebuildEpoch.set(meshId, (rebuildEpoch.get(meshId) ?? 0) + 1);
58142
+ let coverage = null;
58143
+ try {
58144
+ const reset = node.node.resetConsumer(topic, READ_MODEL_CONSUMER, {
58145
+ from: "earliest-retained"
58146
+ });
58147
+ coverage = {
58148
+ consumerName: READ_MODEL_CONSUMER,
58149
+ existed: reset.existed,
58150
+ replayFromRowid: reset.replayFromRowid,
58151
+ archivedRows: reset.archivedRows
58152
+ };
58153
+ lastRebuildCoverage.set(meshId, {
58154
+ archivedRows: reset.archivedRows,
58155
+ replayFromRowid: reset.replayFromRowid
58156
+ });
58157
+ if (reset.archivedRows > 0) {
58158
+ LOG2.info(
58159
+ "Seqscribe",
58160
+ `read model rebuild mesh=${meshId} replays from the archive floor (rowid=${reset.replayFromRowid}, ${reset.archivedRows} archived rows not replayed)`
58161
+ );
58162
+ }
58163
+ } catch (error48) {
58164
+ warnOnce(
58165
+ `read model rebuild could not reset cursor topic=${topic}: ${error48 instanceof Error ? error48.message : String(error48)}`
58166
+ );
58167
+ return null;
58168
+ }
57739
58169
  ensureIndex(meshId);
58170
+ return coverage;
58171
+ }
58172
+ function pruneStaleConsumersAtBoot() {
58173
+ const node = activeNode;
58174
+ if (!node) return 0;
58175
+ let total = 0;
58176
+ for (const definition of node.topics) {
58177
+ const meshId = meshIdFromEventsTopic(definition.topic);
58178
+ if (meshId === null) continue;
58179
+ total += pruneTopicConsumers(node, definition.topic, meshId).length;
58180
+ }
58181
+ return total;
58182
+ }
58183
+ function pruneTopicConsumers(node, topic, meshId) {
58184
+ const pruned = [];
58185
+ try {
58186
+ pruned.push(...node.node.pruneConsumers(topic, { prefix: `${READ_MODEL_CONSUMER}#` }));
58187
+ pruned.push(...node.node.pruneConsumers(topic, { prefix: `${PARITY_CONSUMER}:` }));
58188
+ } catch (error48) {
58189
+ warnOnce(
58190
+ `consumer prune failed topic=${topic}: ${error48 instanceof Error ? error48.message : String(error48)}`
58191
+ );
58192
+ return pruned;
58193
+ }
58194
+ if (pruned.length > 0) {
58195
+ LOG2.info(
58196
+ "Seqscribe",
58197
+ `pruned ${pruned.length} stale durable consumer(s) mesh=${meshId} \u2014 archive gate released`
58198
+ );
58199
+ }
58200
+ return pruned;
57740
58201
  }
57741
58202
  function meshReadModelConsumerName(meshId) {
57742
58203
  return indexes.get(meshId)?.consumerName ?? null;
@@ -57749,7 +58210,8 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57749
58210
  }
57750
58211
  }
57751
58212
  indexes.clear();
57752
- rebuildGeneration.clear();
58213
+ lastRebuildCoverage.clear();
58214
+ rebuildEpoch.clear();
57753
58215
  activeNode = null;
57754
58216
  warnedOnce.clear();
57755
58217
  }
@@ -57763,7 +58225,8 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57763
58225
  var indexes;
57764
58226
  var activeNode;
57765
58227
  var warnedOnce;
57766
- var rebuildGeneration;
58228
+ var lastRebuildCoverage;
58229
+ var rebuildEpoch;
57767
58230
  var init_mesh_read_model = __esm2({
57768
58231
  "src/seqscribe/mesh-read-model.ts"() {
57769
58232
  "use strict";
@@ -57771,12 +58234,14 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57771
58234
  init_dist();
57772
58235
  init_logger();
57773
58236
  init_mesh_event_projection();
58237
+ init_mesh_parity();
57774
58238
  init_topics2();
57775
58239
  READ_MODEL_CONSUMER = "stage4a-mesh-read-model";
57776
58240
  indexes = /* @__PURE__ */ new Map();
57777
58241
  activeNode = null;
57778
58242
  warnedOnce = /* @__PURE__ */ new Set();
57779
- rebuildGeneration = /* @__PURE__ */ new Map();
58243
+ lastRebuildCoverage = /* @__PURE__ */ new Map();
58244
+ rebuildEpoch = /* @__PURE__ */ new Map();
57780
58245
  }
57781
58246
  });
57782
58247
  function resolveMeshDualWriteMode(env2 = process.env) {
@@ -57861,7 +58326,7 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57861
58326
  return topic;
57862
58327
  } catch (error48) {
57863
58328
  definedTopics.set(topic, false);
57864
- counters.topicErrors++;
58329
+ counters2.topicErrors++;
57865
58330
  LOG2.warn(
57866
58331
  "Seqscribe",
57867
58332
  `mesh dual-write disabled for this mesh \u2014 defineTopic failed topic=${topic}: ${error48 instanceof Error ? error48.message : String(error48)}`
@@ -57882,7 +58347,7 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57882
58347
  const topic = ensureTopic(node, meshId);
57883
58348
  if (!topic) return false;
57884
58349
  if (inflight >= MAX_INFLIGHT) {
57885
- counters.dropped++;
58350
+ counters2.dropped++;
57886
58351
  warnOnce2(
57887
58352
  `mesh dual-write shedding load \u2014 ${MAX_INFLIGHT} appends in flight; records are being dropped from the SHADOW leg only (the ledger is unaffected)`
57888
58353
  );
@@ -57892,7 +58357,7 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57892
58357
  const estimated = estimateProjectedEntryBytes(topic, projected);
57893
58358
  const ceiling = maxEntryBytes();
57894
58359
  if (estimated > ceiling) {
57895
- counters.oversized++;
58360
+ counters2.oversized++;
57896
58361
  warnOnce2(
57897
58362
  `mesh dual-write skipped an oversized record (further occurrences logged once): estimated=${estimated}B ceiling=${ceiling}B ledgerKind=${projected.ledgerKind}. The projection is supposed to bound every field \u2014 this is a projection bug, not a transport one.`
57898
58363
  );
@@ -57902,13 +58367,13 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57902
58367
  void node.node.log(topic).append(MESH_EVENT_ENTRY_KIND, toJsonValue(projected)).then(
57903
58368
  () => {
57904
58369
  inflight--;
57905
- if (origin === "backfill") counters.backfilled++;
57906
- else counters.written++;
58370
+ if (origin === "backfill") counters2.backfilled++;
58371
+ else counters2.written++;
57907
58372
  },
57908
58373
  (error48) => {
57909
58374
  inflight--;
57910
- if (origin === "backfill") counters.backfillFailed++;
57911
- else counters.failed++;
58375
+ if (origin === "backfill") counters2.backfillFailed++;
58376
+ else counters2.failed++;
57912
58377
  warnOnce2(
57913
58378
  `mesh dual-write append failed (further failures logged once): ${error48 instanceof Error ? error48.message : String(error48)}`
57914
58379
  );
@@ -57916,8 +58381,8 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57916
58381
  );
57917
58382
  return true;
57918
58383
  } catch (error48) {
57919
- if (origin === "backfill") counters.backfillFailed++;
57920
- else counters.failed++;
58384
+ if (origin === "backfill") counters2.backfillFailed++;
58385
+ else counters2.failed++;
57921
58386
  warnOnce2(
57922
58387
  `mesh dual-write threw synchronously (further throws logged once): ${error48 instanceof Error ? error48.message : String(error48)}`
57923
58388
  );
@@ -57925,7 +58390,7 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57925
58390
  }
57926
58391
  }
57927
58392
  function meshDualWriteCounters() {
57928
- return { ...counters };
58393
+ return { ...counters2 };
57929
58394
  }
57930
58395
  function isMeshDualWriteActive() {
57931
58396
  return activeNode2 !== null && (activeMode === "shadow" || activeMode === "primary");
@@ -57945,17 +58410,17 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57945
58410
  definedTopics.clear();
57946
58411
  inflight = 0;
57947
58412
  warnedOnce2.clear();
57948
- counters.written = 0;
57949
- counters.failed = 0;
57950
- counters.dropped = 0;
57951
- counters.topicErrors = 0;
57952
- counters.backfilled = 0;
57953
- counters.backfillFailed = 0;
57954
- counters.oversized = 0;
58413
+ counters2.written = 0;
58414
+ counters2.failed = 0;
58415
+ counters2.dropped = 0;
58416
+ counters2.topicErrors = 0;
58417
+ counters2.backfilled = 0;
58418
+ counters2.backfillFailed = 0;
58419
+ counters2.oversized = 0;
57955
58420
  }
57956
58421
  var MESH_DUAL_WRITE_ENV;
57957
58422
  var MAX_INFLIGHT;
57958
- var counters;
58423
+ var counters2;
57959
58424
  var definedTopics;
57960
58425
  var activeNode2;
57961
58426
  var activeMode;
@@ -57970,7 +58435,7 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57970
58435
  init_topics2();
57971
58436
  MESH_DUAL_WRITE_ENV = "ADHDEV_SEQSCRIBE_MESH";
57972
58437
  MAX_INFLIGHT = 512;
57973
- counters = {
58438
+ counters2 = {
57974
58439
  written: 0,
57975
58440
  failed: 0,
57976
58441
  dropped: 0,
@@ -57987,180 +58452,46 @@ CREATE TABLE IF NOT EXISTS sq_archive (
57987
58452
  TOPIC_LISTENERS = /* @__PURE__ */ Symbol.for("adhdev.seqscribe.topicActivatedListeners");
57988
58453
  }
57989
58454
  });
57990
- function diffProjected(expected, actual) {
57991
- const fields = [];
57992
- if (expected.ledgerKind !== actual.ledgerKind) fields.push("ledgerKind");
57993
- if (expected.timestamp !== actual.timestamp) fields.push("timestamp");
57994
- if (expected.nodeId !== actual.nodeId) fields.push("nodeId");
57995
- if (expected.sessionId !== actual.sessionId) fields.push("sessionId");
57996
- if (expected.providerType !== actual.providerType) fields.push("providerType");
57997
- if (expected.taskId !== actual.taskId) fields.push("taskId");
57998
- const keys = /* @__PURE__ */ new Set([
57999
- ...Object.keys(expected.payload ?? {}),
58000
- ...Object.keys(actual.payload ?? {})
58001
- ]);
58002
- for (const key2 of keys) {
58003
- if (expected.payload?.[key2] !== actual.payload?.[key2]) fields.push(`payload.${key2}`);
58004
- }
58005
- return fields;
58006
- }
58007
- function readProjected2(payload) {
58008
- if (!payload || typeof payload !== "object") return null;
58009
- const record2 = payload;
58010
- if (typeof record2.id !== "string" || record2.id.length === 0) return null;
58011
- return {
58012
- id: record2.id,
58013
- timestamp: typeof record2.timestamp === "string" ? record2.timestamp : "",
58014
- ledgerKind: typeof record2.ledgerKind === "string" ? record2.ledgerKind : "",
58015
- nodeId: record2.nodeId ?? null,
58016
- sessionId: record2.sessionId ?? null,
58017
- providerType: record2.providerType ?? null,
58018
- taskId: record2.taskId ?? null,
58019
- payload: record2.payload ?? {}
58020
- };
58021
- }
58022
- async function collectOwnShadowRecords(handle, topic, nonce) {
58023
- const found = /* @__PURE__ */ new Map();
58024
- let unsub = null;
58025
- try {
58026
- unsub = handle.node.onEntry(topic, `${PARITY_CONSUMER}:${nonce}`, (entry) => {
58027
- if (entry.kind !== MESH_EVENT_ENTRY_KIND) return;
58028
- if (entry.writer !== handle.writerId) return;
58029
- const projected = readProjected2(entry.payload);
58030
- if (projected) found.set(projected.id, projected);
58031
- });
58032
- await new Promise((resolve31) => setTimeout(resolve31, 0));
58033
- await new Promise((resolve31) => setTimeout(resolve31, 0));
58034
- } finally {
58035
- try {
58036
- unsub?.();
58037
- } catch {
58038
- }
58039
- }
58040
- return found;
58455
+ function reportMeshTopicGrants(topics) {
58456
+ grantedTopics = topics ? new Set(topics) : null;
58041
58457
  }
58042
- function shortId(id) {
58043
- return id.length <= 12 ? id : `${id.slice(0, 12)}\u2026`;
58458
+ function isTopicGranted(topic) {
58459
+ if (grantedTopics === null) return true;
58460
+ return grantedTopics.has(topic);
58044
58461
  }
58045
- async function runMeshParityCheck(handle, meshId, ledgerEntries, opts = {}) {
58046
- const topic = meshEventsTopic(meshId);
58047
- const result = { meshId, compared: 0, mismatches: [] };
58048
- if (!handle.topics.some((d) => d.topic === topic)) {
58049
- return result;
58462
+ function isConsumerCaughtUp(node, topic, meshId, consumerName) {
58463
+ const epoch = meshReadModelRebuildEpoch(meshId);
58464
+ const existing = caughtUpLatches.get(meshId);
58465
+ if (existing && existing.node === node && existing.consumerName === consumerName && existing.epoch === epoch) {
58466
+ if (existing.caughtUp || existing.pending) return existing.caughtUp;
58050
58467
  }
58051
- let shadow;
58468
+ const latch = { node, consumerName, epoch, caughtUp: false, pending: true };
58469
+ caughtUpLatches.set(meshId, latch);
58052
58470
  try {
58053
- shadow = await collectOwnShadowRecords(
58054
- handle,
58055
- topic,
58056
- opts.nonce ?? `${Date.now()}-${counters2.runs}`
58471
+ void node.node.consumerCaughtUp(topic, consumerName).then(
58472
+ () => {
58473
+ if (caughtUpLatches.get(meshId) !== latch) return;
58474
+ latch.pending = false;
58475
+ latch.caughtUp = true;
58476
+ },
58477
+ (error48) => {
58478
+ if (caughtUpLatches.get(meshId) !== latch) return;
58479
+ latch.pending = false;
58480
+ latch.caughtUp = false;
58481
+ LOG2.info(
58482
+ "Seqscribe",
58483
+ `read model catch-up watch ended mesh=${meshId}: ${error48 instanceof Error ? error48.message : String(error48)}`
58484
+ );
58485
+ }
58057
58486
  );
58058
58487
  } catch (error48) {
58488
+ latch.pending = false;
58059
58489
  LOG2.warn(
58060
58490
  "Seqscribe",
58061
- `parity read failed mesh=${meshId}: ${error48 instanceof Error ? error48.message : String(error48)}`
58491
+ `read model catch-up unavailable mesh=${meshId}: ${error48 instanceof Error ? error48.message : String(error48)}`
58062
58492
  );
58063
- return result;
58064
- }
58065
- const seen = /* @__PURE__ */ new Set();
58066
- for (const entry of ledgerEntries) {
58067
- result.compared++;
58068
- seen.add(entry.id);
58069
- const actual = shadow.get(entry.id);
58070
- if (!actual) {
58071
- result.mismatches.push({
58072
- kind: "missing_in_shadow",
58073
- id: entry.id,
58074
- ledgerKind: entry.kind
58075
- });
58076
- continue;
58077
- }
58078
- const fields = diffProjected(projectMeshLedgerEntry(entry), actual);
58079
- if (fields.length > 0) {
58080
- result.mismatches.push({
58081
- kind: "field_mismatch",
58082
- id: entry.id,
58083
- ledgerKind: entry.kind,
58084
- fields
58085
- });
58086
- }
58087
- }
58088
- for (const [id, record2] of shadow) {
58089
- if (seen.has(id)) continue;
58090
- result.mismatches.push({
58091
- kind: "extra_in_shadow",
58092
- id,
58093
- ledgerKind: record2.ledgerKind || null
58094
- });
58095
- }
58096
- counters2.runs++;
58097
- counters2.compared = result.compared;
58098
- let missing = 0;
58099
- let extra = 0;
58100
- let field2 = 0;
58101
- for (const m of result.mismatches) {
58102
- if (m.kind === "missing_in_shadow") missing++;
58103
- else if (m.kind === "extra_in_shadow") extra++;
58104
- else field2++;
58105
- }
58106
- counters2.missingInShadow += missing;
58107
- counters2.extraInShadow += extra;
58108
- counters2.fieldMismatch += field2;
58109
- counters2.mismatches += result.mismatches.length;
58110
- if (result.mismatches.length > 0) {
58111
- for (const m of result.mismatches.slice(0, MISMATCH_LOG_CAP)) {
58112
- LOG2.info(
58113
- "Seqscribe",
58114
- `parity mismatch kind=${m.kind} mesh=${meshId} entry=${shortId(m.id)} ledgerKind=${m.ledgerKind ?? "unknown"}` + (m.fields?.length ? ` fields=${m.fields.join(",")}` : "")
58115
- );
58116
- }
58117
- if (result.mismatches.length > MISMATCH_LOG_CAP) {
58118
- LOG2.info(
58119
- "Seqscribe",
58120
- `parity mismatch mesh=${meshId} \u2014 ${result.mismatches.length - MISMATCH_LOG_CAP} further mismatches not logged this run`
58121
- );
58122
- }
58123
58493
  }
58124
- return result;
58125
- }
58126
- function meshParityCounters() {
58127
- return { ...counters2 };
58128
- }
58129
- function __resetMeshParityForTests() {
58130
- counters2.compared = 0;
58131
- counters2.missingInShadow = 0;
58132
- counters2.extraInShadow = 0;
58133
- counters2.fieldMismatch = 0;
58134
- counters2.mismatches = 0;
58135
- counters2.runs = 0;
58136
- }
58137
- var PARITY_CONSUMER;
58138
- var counters2;
58139
- var MISMATCH_LOG_CAP;
58140
- var init_mesh_parity = __esm2({
58141
- "src/seqscribe/mesh-parity.ts"() {
58142
- "use strict";
58143
- init_logger();
58144
- init_mesh_event_projection();
58145
- init_topics2();
58146
- PARITY_CONSUMER = "stage3-mesh-parity";
58147
- counters2 = {
58148
- compared: 0,
58149
- missingInShadow: 0,
58150
- extraInShadow: 0,
58151
- fieldMismatch: 0,
58152
- mismatches: 0,
58153
- runs: 0
58154
- };
58155
- MISMATCH_LOG_CAP = 20;
58156
- }
58157
- });
58158
- function reportMeshTopicGrants(topics) {
58159
- grantedTopics = topics ? new Set(topics) : null;
58160
- }
58161
- function isTopicGranted(topic) {
58162
- if (grantedTopics === null) return true;
58163
- return grantedTopics.has(topic);
58494
+ return latch.caughtUp;
58164
58495
  }
58165
58496
  function countFallback(reason) {
58166
58497
  fallbackCounts.set(reason, (fallbackCounts.get(reason) ?? 0) + 1);
@@ -58202,9 +58533,9 @@ CREATE TABLE IF NOT EXISTS sq_archive (
58202
58533
  if (topicStats.quarantined > 0) return { ready: false, reason: "quarantined" };
58203
58534
  const consumerName = meshReadModelConsumerName(meshId);
58204
58535
  if (!consumerName) return { ready: false, reason: "consumer_missing" };
58205
- const consumer = topicStats.consumers[consumerName];
58206
- if (!consumer) return { ready: false, reason: "consumer_missing" };
58207
- if (consumer.lagRows > 0) return { ready: false, reason: "consumer_lag" };
58536
+ if (!isConsumerCaughtUp(node, topic, meshId, consumerName)) {
58537
+ return { ready: false, reason: "consumer_lag" };
58538
+ }
58208
58539
  if (meshParityCounters().mismatches > 0) return { ready: false, reason: "parity_mismatch" };
58209
58540
  return { ready: true, reason: null };
58210
58541
  }
@@ -58232,8 +58563,10 @@ CREATE TABLE IF NOT EXISTS sq_archive (
58232
58563
  readsServedFromReplica = 0;
58233
58564
  readsServedFromLedger = 0;
58234
58565
  loggedTransitions.clear();
58566
+ caughtUpLatches.clear();
58235
58567
  }
58236
58568
  var grantedTopics;
58569
+ var caughtUpLatches;
58237
58570
  var fallbackCounts;
58238
58571
  var readsServedFromReplica;
58239
58572
  var readsServedFromLedger;
@@ -58247,6 +58580,7 @@ CREATE TABLE IF NOT EXISTS sq_archive (
58247
58580
  init_mesh_read_model();
58248
58581
  init_topics2();
58249
58582
  grantedTopics = null;
58583
+ caughtUpLatches = /* @__PURE__ */ new Map();
58250
58584
  fallbackCounts = /* @__PURE__ */ new Map();
58251
58585
  readsServedFromReplica = 0;
58252
58586
  readsServedFromLedger = 0;
@@ -138706,7 +139040,16 @@ ${ptyResult.output.slice(-2e3)}`);
138706
139040
  queueBucket: count(seqscribe.queueBucket),
138707
139041
  fgenAgeBucket: count(seqscribe.fgenAgeBucket),
138708
139042
  quarantined: seqscribe.quarantined === true,
138709
- authority: seqscribe.authority === true
139043
+ authority: seqscribe.authority === true,
139044
+ dualWrite: seqscribe.dualWrite === true,
139045
+ dualWriteFailedBucket: count(seqscribe.dualWriteFailedBucket),
139046
+ dualWriteDroppedBucket: count(seqscribe.dualWriteDroppedBucket),
139047
+ dualWriteBackfilledBucket: count(seqscribe.dualWriteBackfilledBucket),
139048
+ parityMismatchBucket: count(seqscribe.parityMismatchBucket),
139049
+ parityRan: seqscribe.parityRan === true,
139050
+ parityMissingInShadowBucket: count(seqscribe.parityMissingInShadowBucket),
139051
+ parityExtraInShadowBucket: count(seqscribe.parityExtraInShadowBucket),
139052
+ parityFieldMismatchBucket: count(seqscribe.parityFieldMismatchBucket)
138710
139053
  };
138711
139054
  }
138712
139055
  function buildCloudStatusReportPayload(sessions, p2p, timestamp2, seqscribe) {
@@ -147146,7 +147489,10 @@ data: ${JSON.stringify(msg.data)}
147146
147489
  dualWriteDroppedBucket: bucket(opts.dualWrite?.dropped ?? 0, BACKLOG_BUCKETS),
147147
147490
  dualWriteBackfilledBucket: bucket(opts.dualWrite?.backfilled ?? 0, BACKLOG_BUCKETS),
147148
147491
  parityMismatchBucket: bucket(opts.parity?.mismatches ?? 0, BACKLOG_BUCKETS),
147149
- parityRan: (opts.parity?.runs ?? 0) > 0
147492
+ parityRan: (opts.parity?.runs ?? 0) > 0,
147493
+ parityMissingInShadowBucket: bucket(opts.parity?.missingInShadow ?? 0, BACKLOG_BUCKETS),
147494
+ parityExtraInShadowBucket: bucket(opts.parity?.extraInShadow ?? 0, BACKLOG_BUCKETS),
147495
+ parityFieldMismatchBucket: bucket(opts.parity?.fieldMismatch ?? 0, BACKLOG_BUCKETS)
147150
147496
  };
147151
147497
  }
147152
147498
  init_mesh_dual_write();
@@ -147559,7 +147905,13 @@ ${upgradeFailureNotice.notice}${supersededHint}`);
147559
147905
  dropped: dual.dropped,
147560
147906
  backfilled: dual.backfilled
147561
147907
  },
147562
- parity: { runs: parity.runs, mismatches: parity.mismatches }
147908
+ parity: {
147909
+ runs: parity.runs,
147910
+ mismatches: parity.mismatches,
147911
+ missingInShadow: parity.missingInShadow,
147912
+ extraInShadow: parity.extraInShadow,
147913
+ fieldMismatch: parity.fieldMismatch
147914
+ }
147563
147915
  });
147564
147916
  } catch (error48) {
147565
147917
  LOG2.warn(
@@ -147617,6 +147969,7 @@ ${upgradeFailureNotice.notice}${supersededHint}`);
147617
147969
  configureMeshDualWrite(components.seqscribeNode ?? null);
147618
147970
  configureMeshReadModel(components.seqscribeNode ?? null);
147619
147971
  if (components.seqscribeNode) {
147972
+ pruneStaleConsumersAtBoot();
147620
147973
  components.seqscribeParityLoop = startMeshParityLoop(components.seqscribeNode);
147621
147974
  }
147622
147975
  } catch (error48) {