@helipod/objectstore-substrate 0.1.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/segment.ts","../src/manifest.ts","../src/object-doc-store.ts","../src/snapshot.ts","../src/fenced-error.ts","../src/consumers.ts","../src/apply-snapshot.ts","../src/sharded-object-doc-store.ts","../src/merge-sorted.ts","../src/reshard.ts","../src/globals.ts","../src/heartbeat-driver.ts","../src/gc-driver.ts","../src/frontier.ts","../src/replica-tailer.ts","../src/replica-wiring.ts"],"sourcesContent":["/**\n * The segment codec (Tier 3 Slice 2, design record §5) — a segment is one immutable object\n * (`s{shard}/seg/{seqno}`) holding a batch's worth of `DocumentLogEntry`/`IndexWrite` rows, JSON-encoded\n * so it survives any `ObjectStore` byte-string backend unchanged.\n *\n * JSON has no native `bigint`/`Uint8Array`, so the wire form tags them explicitly:\n * - `bigint` (ts/prev_ts) → decimal string\n * - `Uint8Array` (id bytes, index keys) → base64\n * A document's own field VALUES (`ResolvedDocument.value`, a `Record<string, Value>`) reuse\n * `@helipod/values`' existing `convexToJson`/`jsonToConvex` — the same tagged-JSON transport the rest\n * of the engine already round-trips `Value` (including nested `bigint`/`ArrayBuffer`) through.\n */\nimport { convexToJson, jsonToConvex, type JSONValue } from \"@helipod/values\";\nimport type {\n DatabaseIndexUpdate,\n DatabaseIndexValue,\n DocumentLogEntry,\n IndexWrite,\n InternalDocumentId,\n ResolvedDocument,\n} from \"@helipod/docstore\";\n\n/** One segment's worth of staged rows — the unit `encodeSegment`/`decodeSegment` round-trip. */\nexport interface SegmentPayload {\n documents: DocumentLogEntry[];\n indexUpdates: IndexWrite[];\n}\n\n/* --- base64 (portable: global btoa/atob, no Node Buffer — matches @helipod/values' json.ts) --- */\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]!);\n return btoa(binary);\n}\n\nfunction base64ToBytes(b64: string): Uint8Array {\n const binary = atob(b64);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);\n return out;\n}\n\n/* --- wire (JSON-safe) shapes --- */\n\ninterface WireInternalDocumentId {\n tableNumber: number;\n internalId: string; // base64\n}\n\ninterface WireResolvedDocument {\n id: WireInternalDocumentId;\n value: JSONValue;\n}\n\n// Exported (not just `SegmentPayload`-internal) so `snapshot.ts` can build a `WireSnapshotPayload`\n// out of the SAME per-row wire shapes — see `encodeDocumentLogEntries`/`encodeIndexWrites` below.\nexport interface WireDocumentLogEntry {\n ts: string; // decimal bigint\n id: WireInternalDocumentId;\n value: WireResolvedDocument | null; // null preserved verbatim (tombstone)\n prev_ts: string | null; // decimal bigint, or null\n}\n\ntype WireDatabaseIndexValue = { type: \"Deleted\" } | { type: \"NonClustered\"; docId: WireInternalDocumentId };\n\ninterface WireDatabaseIndexUpdate {\n indexId: string;\n key: string; // base64\n value: WireDatabaseIndexValue;\n}\n\nexport interface WireIndexWrite {\n ts: string; // decimal bigint\n update: WireDatabaseIndexUpdate;\n}\n\ninterface WireSegmentPayload {\n documents: WireDocumentLogEntry[];\n indexUpdates: WireIndexWrite[];\n}\n\nfunction encodeId(id: InternalDocumentId): WireInternalDocumentId {\n return { tableNumber: id.tableNumber, internalId: bytesToBase64(id.internalId) };\n}\n\nfunction decodeId(id: WireInternalDocumentId): InternalDocumentId {\n return { tableNumber: id.tableNumber, internalId: base64ToBytes(id.internalId) };\n}\n\nfunction encodeResolvedDocument(doc: ResolvedDocument): WireResolvedDocument {\n return { id: encodeId(doc.id), value: convexToJson(doc.value) };\n}\n\nfunction decodeResolvedDocument(doc: WireResolvedDocument): ResolvedDocument {\n // DocumentValue is a Record<string, Value> — jsonToConvex(object JSON) always returns an object.\n return { id: decodeId(doc.id), value: jsonToConvex(doc.value) as ResolvedDocument[\"value\"] };\n}\n\nfunction encodeDocumentLogEntry(entry: DocumentLogEntry): WireDocumentLogEntry {\n return {\n ts: entry.ts.toString(),\n id: encodeId(entry.id),\n value: entry.value === null ? null : encodeResolvedDocument(entry.value),\n prev_ts: entry.prev_ts === null ? null : entry.prev_ts.toString(),\n };\n}\n\nfunction decodeDocumentLogEntry(entry: WireDocumentLogEntry): DocumentLogEntry {\n return {\n ts: BigInt(entry.ts),\n id: decodeId(entry.id),\n value: entry.value === null ? null : decodeResolvedDocument(entry.value),\n prev_ts: entry.prev_ts === null ? null : BigInt(entry.prev_ts),\n };\n}\n\nfunction encodeIndexValue(value: DatabaseIndexValue): WireDatabaseIndexValue {\n return value.type === \"Deleted\" ? { type: \"Deleted\" } : { type: \"NonClustered\", docId: encodeId(value.docId) };\n}\n\nfunction decodeIndexValue(value: WireDatabaseIndexValue): DatabaseIndexValue {\n return value.type === \"Deleted\" ? { type: \"Deleted\" } : { type: \"NonClustered\", docId: decodeId(value.docId) };\n}\n\nfunction encodeIndexUpdate(update: DatabaseIndexUpdate): WireDatabaseIndexUpdate {\n return { indexId: update.indexId, key: bytesToBase64(update.key), value: encodeIndexValue(update.value) };\n}\n\nfunction decodeIndexUpdate(update: WireDatabaseIndexUpdate): DatabaseIndexUpdate {\n return { indexId: update.indexId, key: base64ToBytes(update.key), value: decodeIndexValue(update.value) };\n}\n\nfunction encodeIndexWrite(write: IndexWrite): WireIndexWrite {\n return { ts: write.ts.toString(), update: encodeIndexUpdate(write.update) };\n}\n\nfunction decodeIndexWrite(write: WireIndexWrite): IndexWrite {\n return { ts: BigInt(write.ts), update: decodeIndexUpdate(write.update) };\n}\n\n/** Row-array wrappers around the single-row codecs above — exported so `snapshot.ts` can build a\n * `WireSnapshotPayload` (which carries the SAME `documents`/`indexUpdates` wire arrays plus its own\n * `frontierTs`/`segBase` fields) out of these directly, instead of duplicating the per-row\n * bigint/base64/`Value` tagging logic. `encodeSegment`/`decodeSegment` below are themselves just\n * these applied to a bare `{documents, indexUpdates}` envelope. */\nexport function encodeDocumentLogEntries(documents: readonly DocumentLogEntry[]): WireDocumentLogEntry[] {\n return documents.map(encodeDocumentLogEntry);\n}\nexport function decodeDocumentLogEntries(wire: readonly WireDocumentLogEntry[]): DocumentLogEntry[] {\n return wire.map(decodeDocumentLogEntry);\n}\nexport function encodeIndexWrites(writes: readonly IndexWrite[]): WireIndexWrite[] {\n return writes.map(encodeIndexWrite);\n}\nexport function decodeIndexWrites(wire: readonly WireIndexWrite[]): IndexWrite[] {\n return wire.map(decodeIndexWrite);\n}\n\n/** Encode a segment's rows to bytes (UTF-8 JSON) for `ObjectStore.putImmutable`. */\nexport function encodeSegment(payload: SegmentPayload): Uint8Array {\n const wire: WireSegmentPayload = {\n documents: encodeDocumentLogEntries(payload.documents),\n indexUpdates: encodeIndexWrites(payload.indexUpdates),\n };\n return new TextEncoder().encode(JSON.stringify(wire));\n}\n\n/** Decode segment bytes (as read from `ObjectStore.get`) back to `SegmentPayload`. Inverse of\n * {@link encodeSegment} — round-trips bigints and byte arrays exactly. */\nexport function decodeSegment(bytes: Uint8Array): SegmentPayload {\n const wire = JSON.parse(new TextDecoder().decode(bytes)) as WireSegmentPayload;\n return {\n documents: decodeDocumentLogEntries(wire.documents),\n indexUpdates: decodeIndexWrites(wire.indexUpdates),\n };\n}\n","/**\n * Manifest helpers (Tier 3 Slice 2, design record §5/§7) — the per-shard pointer object\n * (`s{shard}/manifest`) that names the segment chain and the commit frontier. It is the object the\n * commit path CAS's on (`casPut`), so it is the fencing/linearization point for `ObjectStoreDocStore`.\n *\n * `tsCounter` (mirrors `frontierTs` today — Slice 2 has no group-commit-ahead-of-frontier gap yet) is\n * the MONOTONE field the seam doc calls for: a real `ObjectStore`'s etag is content-derived, so an\n * A→B→A content sequence could reuse an etag (ABA) — a manifest that never repeats its content (because\n * this field strictly increases every successful CAS) closes that hole.\n *\n * `epoch`/`writerId`/`leaseExpiresAt` (Tier 3 Slice 4, Task 4.2) turn the manifest into the LEASE: an\n * unowned/fresh manifest has `writerId: \"\"`, `leaseExpiresAt: \"0\"`, `epoch: 0`. `ObjectStoreDocStore`'s\n * `acquire()` CAS's `epoch` upward and stamps `writerId`/`leaseExpiresAt` to claim ownership (the epoch\n * bump is what FENCES any prior owner's cached etag); `heartbeat()` CAS-renews `leaseExpiresAt` alone.\n * See `object-doc-store.ts`'s class doc for the full acquire/heartbeat/commit-gating protocol.\n */\nimport type { ObjectStore } from \"@helipod/objectstore\";\n\n/** The per-shard commit pointer AND lease. `frontierTs`/`tsCounter` are decimal-string bigints (JSON\n * has no native bigint) — the highest committed timestamp and the monotone CAS-content counter,\n * respectively. `segments` is the `seqno` chain of `s{shard}/seg/{seqno}` objects a bootstrap still\n * needs to replay, in order — dense (`[0, 1, 2, …]`) from a fresh manifest, but TRIMMED by\n * `snapshot()` (Tier 3 Slice 3, Task 3.3 fix) to only `seqno > snapshotSegBase` once a snapshot\n * covers the earlier ones, so this array stays BOUNDED (the post-snapshot tail) rather than growing\n * with total commit history. `nextSeqno` (Task 3.3 fix) is the authoritative next-free-segment-seqno\n * cursor — it is NOT derivable from `segments` once `segments` is trimmed (a `Math.max(...segments)`\n * bootstrap computation both breaks on a trimmed/empty array and throws `RangeError` on a large\n * untrimmed one), so it is tracked as its own field and must be read directly by `open()`.\n * `snapshotTs`/`snapshotSegBase` (Tier 3 Slice 3, Task 3.2) are optional and set together: the\n * latest snapshot's key suffix (`s{shard}/snap/{snapshotTs}`) and the highest segment seqno it\n * COVERS — bootstrap restores that snapshot then replays only segments with `seqno >\n * snapshotSegBase`. Unset on a fresh manifest (no snapshot taken yet) — full-replay bootstrap.\n * `writerId`/`leaseExpiresAt` (Tier 3 Slice 4) are the LEASE: `writerId === \"\"` means unowned;\n * otherwise the lease is live while `now <= Number(leaseExpiresAt)` (both are decimal-string\n * ms-epoch, matching the `frontierTs`/`tsCounter` no-native-bigint-in-JSON convention — `now` itself\n * is a plain number, not a bigint, so `leaseExpiresAt` is a decimal STRING of a NUMBER, not a bigint,\n * purely for the same JSON-round-trip-safety reason). `epoch` (present since Slice 2, now\n * load-bearing) is bumped by every successful `acquire()` — the epoch bump is the mechanical fence\n * that invalidates a prior owner's cached etag. */\nexport interface Manifest {\n epoch: number;\n frontierTs: string;\n tsCounter: string;\n segments: number[];\n nextSeqno: number;\n writerId: string;\n leaseExpiresAt: string;\n snapshotTs?: string;\n snapshotSegBase?: number;\n}\n\nfunction manifestKey(shard: string): string {\n return `s${shard}/manifest`;\n}\n\n/** The seeded manifest a fresh shard's `createManifest` writes: no commits yet, unowned\n * (`writerId: \"\"`, `leaseExpiresAt: \"0\"` — any `now >= 0` sees this lease as already-expired, so the\n * first `acquire()` always succeeds without a live-lease refusal). */\nfunction emptyManifest(): Manifest {\n return { epoch: 0, frontierTs: \"0\", tsCounter: \"0\", segments: [], nextSeqno: 0, writerId: \"\", leaseExpiresAt: \"0\" };\n}\n\n/** Read the shard's current manifest + its etag (for a follow-up `casManifest`), or `null` if the\n * shard has never been initialized (no manifest object yet — call `createManifest` first). */\nexport async function readManifest(os: ObjectStore, shard: string): Promise<{ manifest: Manifest; etag: string } | null> {\n const entry = await os.get(manifestKey(shard));\n if (entry === null) return null;\n const manifest = JSON.parse(new TextDecoder().decode(entry.body)) as Manifest;\n return { manifest, etag: entry.etag };\n}\n\n/** Create-only initialization of a shard's manifest (`casPut` with `ifMatch: null`). Throws\n * `CasConflict` (via `@helipod/objectstore`'s `isCasConflict`) if the manifest already exists —\n * callers racing to initialize the same shard must treat that as \"someone else already did it\" and\n * `readManifest` instead. */\nexport async function createManifest(os: ObjectStore, shard: string): Promise<{ manifest: Manifest; etag: string }> {\n const manifest = emptyManifest();\n const { etag } = await os.casPut(manifestKey(shard), new TextEncoder().encode(JSON.stringify(manifest)), null);\n return { manifest, etag };\n}\n\n/** Compare-and-swap the shard's manifest to `next`, conditional on `ifMatch` still being the current\n * etag. Throws `CasConflict` (see `isCasConflict`) if another committer's manifest write already moved\n * the etag — the caller (the commit path) must treat this as a fence: nothing else may be applied. */\nexport async function casManifest(\n os: ObjectStore,\n shard: string,\n next: Manifest,\n ifMatch: string,\n): Promise<{ etag: string }> {\n return os.casPut(manifestKey(shard), new TextEncoder().encode(JSON.stringify(next)), ifMatch);\n}\n","/* Helipod Enterprise. Licensed under the Helipod Commercial License — see ee/LICENSE. */\n/**\n * `ObjectStoreDocStore` (Tier 3 Slice 2, design record §4/§6a/§7) — a `DocStore` DECORATOR over a\n * local `SqliteDocStore` + one shard of an `ObjectStore`. Object storage is the linearization point\n * (the durable log a second process replays); the local SQLite store is a materialized cache that\n * must never get ahead of it.\n *\n * - Every read/non-commit method is a straight FORWARD to the local store (it already has the\n * fully materialized, queryable state — that's the point of local materialization, design §6a).\n * - `commitWrite`/`commitWriteBatch` are INTERCEPTED and made OBJECT-FIRST: allocate ts from the\n * manifest → append an immutable segment → CAS the manifest (the fence) → only on CAS success\n * apply the same rows to the local store via the explicit-ts `write(..., \"Overwrite\")` path (the\n * same primitive `open`'s bootstrap and a replica tailer use — no second explicit-ts path).\n *\n * Commits serialize under an in-process mutex (`runExclusive`) so the cached `{manifest, etag}` this\n * instance holds is always read-then-CAS'd by exactly one commit at a time — required because the\n * manifest CAS is optimistic (etag-conditional) but the LOCAL apply that follows it is not: without\n * the mutex two concurrent `commitWriteBatch` calls from the same process could race the same cached\n * etag into `casManifest` and (since only one wins) leave the loser's local apply inconsistent with\n * which segment its ts's actually landed in.\n *\n * Any `casManifest` failure (fence OR ambiguous/lost-response) POISONS this instance (whole-branch\n * review C1): the cached cursor is then untrustworthy. `putImmutable` is KEEP-FIRST on every adapter\n * (fs/memory/s3 — s3 via a create-only `IfNoneMatch: \"*\"` conditional PUT, Tier 3 Slice 4 review), so a\n * reused segment seqno can never overwrite a durable, manifest-referenced segment written by someone\n * else — it silently no-ops and the writer's OWN manifest CAS fails separately on its stale etag. Poison\n * is therefore not a corruption guard against overwriting live data (there is no such hazard left); it's\n * the correct response to an untrustworthy cached cursor after any CAS failure. Recovery = re-open\n * (re-bootstrap).\n *\n * OWNERSHIP (Tier 3 Slice 4, Task 4.2): `open()` only MATERIALIZES — it bootstraps `local` from the\n * bucket (snapshot + tail replay) but claims NO ownership. A writer must additionally `acquire()`\n * before it may commit; a replica (Slice 5) opens without ever acquiring. `acquire({writerId,\n * leaseTtlMs, now})` re-reads the manifest fresh, refuses if a DIFFERENT writer's lease is still live,\n * else catches this instance's local store up to the manifest frontier (`materializeTo` — the same\n * primitive `open()` bootstraps with) and CAS-bumps `epoch` to claim the lease — the epoch bump is\n * what fences any prior owner's cached etag. `heartbeat({now, leaseTtlMs})` CAS-renews\n * `leaseExpiresAt` alone (epoch/writerId unchanged); ANY heartbeat CAS failure means a challenger\n * fenced this instance — it poisons and throws `FencedError`. `commitWriteBatch` now REQUIRES a held\n * lease (throws before the poisoned check if `acquire()` was never called) and additionally asserts\n * its cached epoch still matches the held epoch before every CAS — defense in depth against serving a\n * commit after having been silently fenced.\n *\n * SLICE-2 BOUNDARIES — follow-ups the failover/replica slices (S4/S5) MUST NOT inherit silently:\n * - **Globals + client-verdict receipts are LOCAL-ONLY** (`writeGlobal*`/`recordClientVerdict`/… forward\n * to the local store; they never enter the segment log). So a from-scratch bootstrap over object\n * storage alone does NOT reconstruct them: `createEmbeddedRuntime` would mint a NEW `deploymentId`\n * (flipping every outbox client to `known:false`) and lose dedup receipts (effectively-once →\n * at-least-once). Single-node with a PERSISTENT local SQLite file survives restarts fine; but the\n * failover slice (a fresh node materializing from the bucket) MUST persist `deploymentId` (e.g. in\n * the manifest) and address receipt durability before it can claim \"byte-identical from object\n * storage alone\" for engine bookkeeping. Effectively-once itself → the manifest idempotency window (deferred).\n * - **Fence/failure paths are exercised at the substrate level only on `objectstore-fs`** (keep-first\n * `putImmutable`, same as every adapter now — `objectstore-s3` was fixed to keep-first too, Tier 3\n * Slice 4 review, closing the overwrite hazard this note used to describe). The `objectstore-s3`\n * package's OWN conformance suite (run against real MinIO under `HELIPOD_OBJECTSTORE_S3=1`) proves\n * the keep-first invariant on S3 directly; a MinIO-gated fence/failure variant AT THIS SUBSTRATE LEVEL\n * (as opposed to a bare adapter conformance check) is still a nice-to-have for the failover slice, not\n * a correctness gap — the adapter-level guarantee is what the fence's safety actually rests on.\n */\nimport type { ObjectStore } from \"@helipod/objectstore\";\nimport { isCasConflict } from \"@helipod/objectstore\";\nimport type {\n ClientVerdictRecord,\n ClientVerdictWrite,\n CommitGuardUnit,\n CommitUnit,\n ConflictStrategy,\n DocStore,\n DocumentLogEntry,\n Interval,\n LatestDocument,\n Order,\n PrevRevQuery,\n SchemaSetupOptions,\n ShardId,\n TimestampRange,\n InternalDocumentId,\n IndexWrite,\n} from \"@helipod/docstore\";\nimport type { JSONValue } from \"@helipod/values\";\nimport type { SqliteDocStore } from \"@helipod/docstore-sqlite\";\nimport { encodeSegment, decodeSegment, type SegmentPayload } from \"./segment\";\nimport { readManifest, createManifest, casManifest, type Manifest } from \"./manifest\";\nimport { writeSnapshot, readSnapshot, type SnapshotPayload } from \"./snapshot\";\nimport { FencedError } from \"./fenced-error\";\nimport { readConsumerWatermarks } from \"./consumers\";\nimport { applySnapshotState } from \"./apply-snapshot\";\n\n/** Exported (Tier 3 Slice 5, Task 5.1) so `replica-tailer.ts` can pull the SAME segment objects this\n * class's own commit/`materializeTo` path writes/reads, without duplicating the key format. */\nexport function segmentKey(shard: string, seqno: number): string {\n return `s${shard}/seg/${seqno}`;\n}\n\n/** Take a snapshot after this many committed segments since the last one (Tier 3 Slice 3, Task\n * 3.2). Small deliberately — tests exercise the cadence without huge commit loops; a real\n * deployment can raise it if snapshot-object churn matters more than bootstrap tail length. */\nconst SNAPSHOT_EVERY = 8;\n\nexport interface ObjectStoreDocStoreOpts {\n objectStore: ObjectStore;\n shard: string;\n local: SqliteDocStore;\n}\n\nexport class ObjectStoreDocStore implements DocStore {\n private readonly objectStore: ObjectStore;\n private readonly shard: string;\n private readonly local: SqliteDocStore;\n\n /** The last manifest state this process successfully CAS'd (or bootstrapped from) + its etag —\n * the read-half of the next `casManifest`'s optimistic-concurrency check. Updated ONLY after a\n * successful CAS (never speculatively), under `mutex`. */\n private cached: { manifest: Manifest; etag: string };\n /** The next free segment seqno for THIS process — advanced by one on every successful commit CAS\n * (dense in the common case), AND durably burned forward by one extra on every successful\n * TAKEOVER `acquire()` (Task 4.6, superseding the earlier in-process-only skip-one of Task 4.5 —\n * see `acquire()`'s doc) to fence out the seqno the immediate predecessor may have orphaned. The\n * burn lives in the DURABLE `manifest.nextSeqno` itself (not just this in-process field) so it\n * survives across a CHAIN of stalled takeovers with no successful commit in between — see\n * `acquire()`. Non-dense-by-one-per-takeover is expected and harmless: never read as `[0..n]` —\n * `materializeTo`/GC always iterate the manifest's own explicit `segments`/`nextSeqno` fields. */\n private nextSeqno: number;\n /** Set when a post-CAS local apply fails: the commit is durable but the local materialization is\n * inconsistent, so further commits are refused until the store is re-opened (re-bootstrapped). */\n private poisoned = false;\n /** The lease this instance currently holds (Tier 3 Slice 4), or `null` if it has never\n * `acquire()`'d (or was fenced — see `heartbeat`/`commitWriteBatch`). `commitWriteBatch` refuses to\n * run without this set; `epoch` is the CAS-bumped value `acquire()` claimed, checked against\n * `this.cached.manifest.epoch` before every commit as a defense-in-depth fence check. */\n private held: { epoch: number; writerId: string } | null = null;\n /** Serializes `commitWrite`/`commitWriteBatch` — see class doc. */\n private mutex: Promise<void> = Promise.resolve();\n /** Committed segments since the last successful snapshot (or since `open`) — `maybeSnapshot`'s\n * cadence trigger. Reset to 0 only on a successful `snapshot()`. */\n private committedSegmentsSinceSnapshot = 0;\n\n private constructor(objectStore: ObjectStore, shard: string, local: SqliteDocStore, cached: { manifest: Manifest; etag: string }, nextSeqno: number) {\n this.objectStore = objectStore;\n this.shard = shard;\n this.local = local;\n this.cached = cached;\n this.nextSeqno = nextSeqno;\n }\n\n /**\n * Open (or initialize) a shard's object-storage-backed store: read-or-create the manifest, then\n * BOOTSTRAP (materialize) the local store — via `materializeTo`, see its doc for the snapshot +\n * tail-replay algorithm. This claims NO ownership (Tier 3 Slice 4): a writer must additionally\n * `acquire()` before it may commit; a replica (Slice 5) opens without ever acquiring. A fresh\n * (empty) bucket bootstraps to an empty local store; a bucket with prior commits reconstructs the\n * IDENTICAL current state either way.\n */\n static async open(opts: ObjectStoreDocStoreOpts): Promise<ObjectStoreDocStore> {\n const { objectStore, shard, local } = opts;\n await local.setupSchema();\n\n let cached = await readManifest(objectStore, shard);\n if (cached === null) {\n try {\n cached = await createManifest(objectStore, shard);\n } catch (e) {\n // Lost a create-only race against another opener of the same shard (manifest.ts's\n // documented contract) — someone else already initialized it; read what they wrote.\n if (!isCasConflict(e)) throw e;\n cached = await readManifest(objectStore, shard);\n if (cached === null) throw e;\n }\n }\n\n // Construct with `nextSeqno: 0` (a fresh `local` has applied nothing yet) and let `materializeTo`\n // do the actual bootstrap work — the SAME primitive `acquire()` uses to catch a re-acquiring\n // instance up to the manifest frontier (Tier 3 Slice 4, Task 4.2 — factored out for that reuse).\n const store = new ObjectStoreDocStore(objectStore, shard, local, cached, 0);\n await store.materializeTo(cached.manifest);\n return store;\n }\n\n /**\n * Materialize `this.local`/`this.nextSeqno` up to `manifest`'s frontier by applying whatever this\n * instance hasn't already applied: if `manifest` references a snapshot this instance hasn't yet\n * covered (`snapshotSegBase` at or beyond `this.nextSeqno`), restore it first\n * (`write(..., \"Overwrite\")` of its current-state dump), then replay every remaining referenced\n * segment in order via the same explicit-ts `write(..., \"Overwrite\")` path — the identical primitive\n * a post-CAS commit applies with, and the one a replica tailer would use (design record §7).\n *\n * Two callers: `open()` on a brand-new instance (`this.nextSeqno === 0`, so this is a full\n * bootstrap — snapshot-if-any + every segment), and `acquire()` on an ALREADY-materialized instance\n * that may be behind the CURRENT manifest (a re-acquiring/challenging writer) — in that case\n * skipping already-applied segments (and skipping a snapshot restore if this instance is already\n * past its `segBase`) matters both for correctness (never double-apply, though `Overwrite` is\n * idempotent) and so a challenger that's only slightly behind doesn't needlessly re-read a snapshot\n * object. Restoring the snapshot when we're behind it is also what makes catch-up correct even if\n * the fencing owner's `gc()` has since deleted the pre-snapshot segments we'd otherwise be missing.\n *\n * Not gated on `runExclusive` itself — both callers already hold it.\n */\n private async materializeTo(manifest: Manifest): Promise<void> {\n let appliedThrough = this.nextSeqno - 1; // highest seqno already applied locally (-1 if none)\n\n if (manifest.snapshotTs !== undefined && manifest.snapshotSegBase !== undefined && manifest.snapshotSegBase > appliedThrough) {\n const snap = await readSnapshot(this.objectStore, this.shard, manifest.snapshotTs);\n if (snap === null) {\n throw new Error(\n `objectstore-substrate: missing snapshot '${manifest.snapshotTs}' referenced by manifest for shard '${this.shard}' (torn state)`,\n );\n }\n // TOMBSTONE-CORRECT RESTORE (Slice 5 re-review fix): this instance's `local` may be NON-EMPTY\n // and STALE here — `open()` always calls this against a fresh, empty local (safe either way),\n // but `acquire()`'s takeover catch-up calls it against an ALREADY-OPEN, possibly-behind writer\n // instance. A snapshot's own `dumpCurrentState` source excludes tombstones, so applying\n // `snap.documents` alone (an overlay `write(..., \"Overwrite\")`, never a replace-all) cannot\n // express \"this doc was deleted in the range this restore jumps over\" — a doc `local` still\n // has LIVE that the snapshot silently dropped would otherwise stay phantom-live and could be\n // RE-COMMITTED by this writer, permanently undoing the delete in the durable log. The shared\n // `applySnapshotState` helper (also used by the replica tailer's `#materializeRound` — see its\n // doc for the full diff+tombstone rationale) diffs+tombstones before applying. On the common\n // fresh-`open()` path `local` is empty, so the diff is a no-op — byte-identical to before this\n // fix. This method has no invalidation sink to feed (the writer's own transactor/tailer own\n // reactivity, not catch-up), so the returned `deletedDocs` are ignored here.\n //\n // BENIGN DIVERGENCE (whole-branch review, Minor #2, carried from the original open()): `dumpCurrentState`\n // (and so this restore) excludes tombstones, so if `snap.frontierTs` is itself a DELETE's ts and\n // there's no tail beyond this snapshot, the restored `documents` table has no row AT that ts — the\n // local store's `maxTimestamp()` (`MAX(ts) FROM documents`) then TRAILS `manifest.frontierTs`. This\n // is SAFE for commit-ts correctness: `commitWriteBatch`'s floor is `max(tsCounter, localMax)` and\n // `tsCounter == frontierTs` here, so no ts is ever reused or regressed. But it means\n // `DocStore.maxTimestamp()`'s \"highest committed timestamp\" contract can diverge post-restore — a\n // consumer needing the AUTHORITATIVE frontier must read the manifest (`frontierTs`), not call\n // `local.maxTimestamp()`. Not fixed here (seeding a fake tombstone row just to keep `maxTimestamp()`\n // honest is worse than documenting the divergence).\n await applySnapshotState(this.local, snap, BigInt(snap.frontierTs));\n appliedThrough = manifest.snapshotSegBase;\n }\n\n for (const seqno of manifest.segments) {\n if (seqno <= appliedThrough) continue; // already applied (or covered by the snapshot just restored)\n const entry = await this.objectStore.get(segmentKey(this.shard, seqno));\n if (entry === null) {\n throw new Error(`objectstore-substrate: missing segment '${segmentKey(this.shard, seqno)}' referenced by manifest for shard '${this.shard}'`);\n }\n const payload = decodeSegment(entry.body);\n await this.local.write(payload.documents, payload.indexUpdates, \"Overwrite\");\n appliedThrough = seqno;\n }\n\n // Read the explicit `nextSeqno` cursor (whole-branch review, Task 3.3 fix) — NEVER derive it from\n // `segments` via `Math.max(...)`: once `snapshot()` trims `segments` to the post-snapshot tail, a\n // snapshot-covers-everything bootstrap (empty tail) would otherwise compute 0 instead of the true\n // cursor, and even pre-trim, `Math.max(...segments)` argument-spreads the WHOLE array onto the\n // call stack and throws `RangeError: Maximum call stack size exceeded` past ~100k segments.\n this.nextSeqno = manifest.nextSeqno;\n }\n\n /**\n * Claim ownership of this shard for `opts.writerId` (Tier 3 Slice 4, Task 4.2) — the manifest CAS\n * IS the fence: a successful acquire bumps `epoch`, which invalidates any prior owner's cached\n * etag, so its next `commitWriteBatch`/`heartbeat` fails loudly (`FencedError` + poison).\n *\n * Re-reads the manifest FRESH (not `this.cached` — this instance may be stale or may never have\n * committed before). If the lease is currently LIVE and held by a DIFFERENT writer\n * (`now <= leaseExpiresAt`), refuses: `{acquired: false, heldBy, expiresAt}` — this is what\n * prevents two live writers ping-ponging (a heartbeating owner's lease never expires). Otherwise\n * (unowned, or the current lease is EXPIRED, or `opts.writerId` already owns it and is re-claiming)\n * catches this instance's local store up to the just-read manifest via `materializeTo` — REQUIRED so\n * a challenger that was behind (or a fresh instance that only `open()`'d) is fully materialized to\n * the manifest's frontier BEFORE it starts committing under the claimed lease — then CAS-bumps\n * `epoch` and stamps `writerId`/`leaseExpiresAt`.\n *\n * A `CasConflict` (lost the acquire race to a concurrent challenger) re-reads and returns\n * `{acquired: false, ...}` — the caller may retry. Any OTHER `casManifest` failure is ambiguous\n * (may have landed despite throwing — same lost-response concern as the commit path's C1 fix) and\n * poisons this instance exactly like a commit/heartbeat CAS failure does (global constraint: lease\n * CAS failures poison like commit CAS failures).\n */\n async acquire(\n opts: { writerId: string; leaseTtlMs: number; now: number },\n ): Promise<{ acquired: true } | { acquired: false; heldBy: string; expiresAt: number }> {\n return this.runExclusive(async () => {\n const fresh = await readManifest(this.objectStore, this.shard);\n if (fresh === null) {\n throw new Error(`objectstore-substrate: acquire() found no manifest for shard '${this.shard}' — open() must initialize it first`);\n }\n const { manifest, etag } = fresh;\n\n if (manifest.writerId !== \"\" && opts.now <= Number(manifest.leaseExpiresAt) && manifest.writerId !== opts.writerId) {\n return { acquired: false as const, heldBy: manifest.writerId, expiresAt: Number(manifest.leaseExpiresAt) };\n }\n\n await this.materializeTo(manifest);\n\n // DURABLE-BURN-ON-ACQUIRE (Task 4.6, superseding Task 4.5's in-process-only skip-one — see the\n // re-review that found it insufficient): a predecessor we're about to fence may have\n // `putImmutable`'d a segment at EXACTLY `manifest.nextSeqno` (its in-flight commit's object PUT)\n // and then stalled/crashed BEFORE its own `casManifest` — that segment is a durable but\n // UNREFERENCED orphan, never covered by `materializeTo` above (it only replays\n // `manifest.segments`). Because `commitWriteBatch` writes EXACTLY ONE segment per flush (the\n // invariant documented at its `putImmutable` call site below), a fenced writer can have orphaned\n // AT MOST ONE seqno — the one it would have referenced next. If a taking-over writer's first\n // commit reused that same seqno, its `putImmutable` would silently no-op (keep-first) against\n // the orphan's bytes while its manifest CAS still succeeds (referencing a segment that holds the\n // OLD writer's data, not the new writer's) — an acknowledged write silently lost.\n //\n // Task 4.5's original fix only advanced THIS PROCESS's in-memory `nextSeqno`, leaving the\n // DURABLE `manifest.nextSeqno` untouched — correct for exactly one stalled predecessor, but a\n // CHAIN of ≥2 writers that each stall BEFORE their own commit CAS (a correlated object-store\n // outage — precisely the failover trigger) each re-read the SAME unmoved durable cursor and\n // recompute the SAME target seqno, so generation N+2 can collide with generation N+1's orphan.\n // The fix: fold the burn into THIS claim CAS so it moves the DURABLE cursor on EVERY takeover,\n // not just this process's view of it — `manifest.nextSeqno` becomes a monotone cursor advanced\n // by BOTH a successful commit AND every takeover-acquire, so no live writer ever targets a seqno\n // a prior generation may have written, no matter how many generations stalled in a row.\n //\n // GATED on `manifest.epoch > 0` (the PRE-bump value just read, i.e. \"has anyone EVER held this\n // shard's lease before\"): a commit can only exist/stall if someone previously held the lease\n // (`commitWriteBatch` requires `this.held`, which only ever comes from a prior successful\n // `acquire()`), so on the FIRST-EVER acquire of a brand-new manifest (`epoch === 0`, `nextSeqno\n // === 0`) there is provably no predecessor and nothing to burn — every commit lands densely\n // from `seg/0`, matching every pre-Slice-4.5 test's assumption. Every acquire AFTER that first\n // one (a genuine takeover OR the same writer's own re-acquire, e.g. after a poisoning event) DID\n // have a predecessor that could have stalled mid-commit, so it always burns — regardless of\n // whether `opts.writerId` matches the previous owner, since a crash-and-restart can resume under\n // the identical writerId.\n const burn = manifest.epoch > 0;\n const next: Manifest = {\n ...manifest,\n epoch: manifest.epoch + 1,\n writerId: opts.writerId,\n leaseExpiresAt: String(opts.now + opts.leaseTtlMs),\n nextSeqno: burn ? manifest.nextSeqno + 1 : manifest.nextSeqno,\n };\n try {\n const { etag: newEtag } = await casManifest(this.objectStore, this.shard, next, etag);\n this.cached = { manifest: next, etag: newEtag };\n this.held = { epoch: next.epoch, writerId: opts.writerId };\n this.poisoned = false;\n // The durable cursor is already correctly advanced (or not) by `next.nextSeqno` above — just\n // adopt it. Segments become non-dense by one integer per takeover (harmless: bootstrap/GC\n // iterate the explicit `manifest.segments` array and read `manifest.nextSeqno` directly, never\n // assume `[0..n]` density — see `materializeTo`'s doc).\n this.nextSeqno = next.nextSeqno;\n return { acquired: true as const };\n } catch (e) {\n if (!isCasConflict(e)) {\n this.poisoned = true;\n throw e;\n }\n // A lost acquire race (CasConflict) is an EXPECTED, retryable outcome — a concurrent\n // challenger simply won first. Do NOT poison: `this.held`/`this.cached` are untouched (this\n // instance never claimed anything), so it remains exactly as usable as before the attempt.\n const reread = await readManifest(this.objectStore, this.shard);\n if (reread === null) throw e;\n return { acquired: false as const, heldBy: reread.manifest.writerId, expiresAt: Number(reread.manifest.leaseExpiresAt) };\n }\n });\n }\n\n /**\n * Renew the currently-held lease's `leaseExpiresAt` (Tier 3 Slice 4, Task 4.2) — `epoch`/`writerId`\n * are carried forward UNCHANGED (a heartbeat is not a re-claim). Requires `this.held` (throws a\n * clear error if `acquire()` was never called) and refuses on an already-`poisoned` instance.\n *\n * ANY `casManifest` failure means the manifest moved out from under this instance — a challenger\n * bumped `epoch` (fenced us) or is mid-fence — so this ALWAYS poisons and throws `FencedError`,\n * unlike the commit path's CasConflict-vs-ambiguous-error distinction: a heartbeat's whole job is to\n * detect exactly this condition, so there is no \"retry\" case to special-case.\n */\n async heartbeat(opts: { now: number; leaseTtlMs: number }): Promise<void> {\n return this.runExclusive(async () => {\n // Both branches below throw `FencedError`, not a bare `Error`: the heartbeat driver is only\n // ever started AFTER a successful `acquire()`, so seeing `held === null` or `poisoned` DURING\n // operation means ownership was already lost by some other path — most commonly `commitWriteBatch`'s\n // own CAS-fail branch, which sets `poisoned = true; held = null` and throws `FencedError` to the\n // TRANSACTOR, never to this heartbeat. If heartbeat instead threw a plain `Error` here, the\n // heartbeat driver (which only treats `instanceof FencedError` as terminal) would misclassify a\n // commit-path fence as a transient heartbeat hiccup and re-arm forever — a zombie node that\n // rejects every write but never calls `onFenced`/stops. See the Slice 6 Task 6.2 review finding.\n if (this.held === null) {\n throw new FencedError(\n `heartbeat on a store with no held lease for shard '${this.shard}' — ownership is lost (never acquired or already fenced)`,\n );\n }\n if (this.poisoned) {\n throw new FencedError(\n `heartbeat on a poisoned store for shard '${this.shard}' (fenced or a prior apply failed) — ownership is lost`,\n );\n }\n const next: Manifest = { ...this.cached.manifest, leaseExpiresAt: String(opts.now + opts.leaseTtlMs) };\n try {\n const { etag } = await casManifest(this.objectStore, this.shard, next, this.cached.etag);\n this.cached = { manifest: next, etag };\n } catch (e) {\n this.poisoned = true;\n this.held = null;\n throw new FencedError(\n `heartbeat fenced: manifest for shard '${this.shard}' moved (stale etag) — this writer is no longer current and is now poisoned ` +\n `(re-acquire to continue). Cause: ${(e as Error)?.message ?? String(e)}`,\n );\n }\n });\n }\n\n /** Voluntarily give up the held lease WITHOUT touching the bucket — the lease simply expires on its\n * own at `leaseExpiresAt` (Tier 3 Slice 4, Task 4.2). After this, `commitWriteBatch` refuses until\n * a fresh `acquire()`. IN-PROCESS ONLY: a challenger's `acquire()` still has to wait out the full\n * remaining TTL, since nothing in the bucket changed. See `relinquish()` — the graceful-shutdown\n * variant that ALSO clears the lease in the bucket itself, so a challenger can take over\n * immediately instead of waiting for expiry (Tier 3 Slice 6, Task 6.5). */\n release(): void {\n this.held = null;\n }\n\n /**\n * Graceful-shutdown variant of `release()` (Tier 3 Slice 6, Task 6.5): best-effort CAS the manifest\n * to clear the lease (`writerId: \"\", leaseExpiresAt: \"0\"`) so a challenger's very next `acquire()`\n * — even at `now: 0` — sees an unowned/already-expired lease and takes over IMMEDIATELY, instead of\n * having to wait out this writer's full remaining `leaseTtlMs`. This closes the production gap the\n * Task 6.4 review found: every graceful rolling-deploy stop/start pair otherwise ate a full-TTL\n * write outage.\n *\n * Deliberately best-effort: a CAS failure here means either (a) we were already fenced — someone\n * else owns the shard now, which is exactly the state we wanted anyway, or (b) a transient blip —\n * the lease still expires naturally, falling back to the pre-6.5 behavior. Neither case should\n * block or fail a clean shutdown, so any CAS error is swallowed. Does NOT bump `epoch` — clearing\n * `writerId`/`leaseExpiresAt` is sufficient for a challenger's refusal check to pass trivially; the\n * challenger's own `acquire()` bumps `epoch` and fences any stale in-process state on ITS claim, the\n * same as an expiry-based takeover always has. Does NOT poison on a swallowed CAS failure — this is\n * a clean voluntary exit, not a fence.\n *\n * Runs under `runExclusive` (serializes with commits/heartbeat/acquire, same as every other\n * lease/commit operation on this instance). A no-op if this instance never held the lease\n * (`this.held === null` — already fenced, or `acquire()` was never called): nothing to relinquish.\n * Always demotes `this.held` to `null` at the end, regardless of the CAS outcome.\n */\n async relinquish(): Promise<void> {\n return this.runExclusive(async () => {\n if (this.held === null) return; // nothing held — already fenced, or never acquired\n const next: Manifest = { ...this.cached.manifest, writerId: \"\", leaseExpiresAt: \"0\" };\n try {\n const { etag } = await casManifest(this.objectStore, this.shard, next, this.cached.etag);\n this.cached = { manifest: next, etag };\n } catch {\n // Swallowed by design — see doc comment above: a clean shutdown must never throw, and a CAS\n // failure here is either \"someone already fenced us\" (fine) or \"transient\" (falls back to\n // natural TTL expiry).\n }\n this.held = null;\n });\n }\n\n /** Chain `fn` onto the mutex so commits from this process serialize (see class doc). */\n private runExclusive<T>(fn: () => Promise<T>): Promise<T> {\n const result = this.mutex.then(fn, fn);\n // Swallow the result's rejection for the CHAIN's sake only — `result` itself (returned below)\n // still carries the real rejection to this call's caller.\n this.mutex = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n }\n\n // ── Commit path (intercepted, object-first) ─────────────────────────────────────────────────\n\n async commitWriteBatch(units: readonly CommitUnit[], shardId?: ShardId): Promise<bigint[]> {\n const tsList = await this.runExclusive(async () => {\n // Tier 3 Slice 4, Task 4.2: commits REQUIRE a held lease — checked BEFORE the poisoned check\n // (and before the empty-batch no-op) so a caller can never commit, not even a no-op, without\n // having `acquire()`'d first.\n if (this.held === null) {\n throw new Error(`ObjectStoreDocStore for shard '${this.shard}': not the lease owner — call acquire() before committing`);\n }\n // Empty batch is a no-op (matches SqliteDocStore.commitWriteBatch) — never write an empty segment.\n if (units.length === 0) return [];\n if (this.poisoned) {\n throw new Error(\n `ObjectStoreDocStore for shard '${this.shard}' is poisoned (a prior post-commit local apply failed); it must be re-opened before further use`,\n );\n }\n // Defense in depth (Task 4.2): the cached manifest's epoch must still match the epoch this\n // instance claimed via `acquire()`. It can only diverge if another `acquire()` on this SAME\n // instance somehow changed `this.held` without updating `this.cached` to match (not possible in\n // the current code paths) OR — the real case — a bug elsewhere let a commit slip through after a\n // fence that `casManifest` itself hasn't yet been given the chance to reject. Either way, a\n // divergence here means we can no longer trust our own bookkeeping enough to attempt the CAS.\n if (this.cached.manifest.epoch !== this.held.epoch) {\n this.poisoned = true;\n throw new FencedError(\n `commit fenced: shard '${this.shard}' cached epoch (${this.cached.manifest.epoch}) diverges from the held lease epoch ` +\n `(${this.held.epoch}) — this writer was fenced by another acquirer and is now poisoned (re-acquire to continue)`,\n );\n }\n const localMax = await this.local.maxTimestamp();\n const cachedTsCounter = BigInt(this.cached.manifest.tsCounter);\n const floor = cachedTsCounter > localMax ? cachedTsCounter : localMax;\n\n const stampedUnits: Array<{ documents: DocumentLogEntry[]; indexUpdates: IndexWrite[] }> = [];\n const allDocuments: DocumentLogEntry[] = [];\n const allIndexUpdates: IndexWrite[] = [];\n const tsList: bigint[] = [];\n\n for (let i = 0; i < units.length; i++) {\n const unit = units[i]!;\n const ts = floor + BigInt(i) + 1n;\n tsList.push(ts);\n const stampedDocs = unit.documents.map((d) => ({ ...d, ts }));\n const stampedIdx = unit.indexUpdates.map((w) => ({ ...w, ts }));\n stampedUnits.push({ documents: stampedDocs, indexUpdates: stampedIdx });\n allDocuments.push(...stampedDocs);\n allIndexUpdates.push(...stampedIdx);\n }\n\n const maxTs = tsList[tsList.length - 1]!;\n const seqno = this.nextSeqno;\n const payload: SegmentPayload = { documents: allDocuments, indexUpdates: allIndexUpdates };\n // INVARIANT (load-bearing for `acquire()`'s durable-burn-on-acquire fence, Task 4.6/4.5): this\n // flush writes EXACTLY ONE segment (one `putImmutable`, one `seqno`). That is what bounds a\n // fenced-mid-commit writer to AT MOST ONE orphaned seqno per takeover — the one it would have\n // referenced here — which is what makes \"burn exactly one seqno per takeover\" airtight for\n // arbitrarily long chains of stalled generations. A future change to batch a flush across\n // MULTIPLE segments (multiple `putImmutable`s / multiple seqnos per `commitWriteBatch` call)\n // would let a mid-flush crash orphan more than one, and MUST revisit `acquire()`'s burn\n // accordingly. Group commit (multiple `CommitUnit`s coalesced into this one call) is fine as-is\n // — it's still one segment.\n await this.objectStore.putImmutable(segmentKey(this.shard, seqno), encodeSegment(payload));\n\n // Spread `this.cached.manifest` FIRST so `snapshotTs`/`snapshotSegBase` (Task 3.2) and\n // `writerId`/`leaseExpiresAt` (Task 4.2 — a commit is not a lease renewal, only `heartbeat` is)\n // carry forward untouched — then override the fields this commit actually advances. `epoch` is\n // set EXPLICITLY from `this.held.epoch` (equal to `this.cached.manifest.epoch` per the assert\n // above, but explicit here as the defense-in-depth belt to that assert's suspenders) rather than\n // left to the spread, so this commit can never accidentally carry a stale epoch forward.\n const next: Manifest = {\n ...this.cached.manifest,\n epoch: this.held.epoch,\n frontierTs: maxTs.toString(),\n tsCounter: maxTs.toString(),\n segments: [...this.cached.manifest.segments, seqno],\n nextSeqno: seqno + 1,\n };\n\n let etag: string;\n try {\n ({ etag } = await casManifest(this.objectStore, this.shard, next, this.cached.etag));\n } catch (e) {\n // ANY casManifest failure POISONS this instance (whole-branch review C1). After a failed CAS\n // our cached `{manifest, etag}` and `nextSeqno` are untrustworthy: a `CasConflict` means we\n // were fenced (the manifest moved); a GENERIC error is AMBIGUOUS — the CAS may have LANDED\n // (a lost response) even though it threw. In both cases, continuing to serve commits on this\n // instance would reuse `seqno` against a stale cursor.\n //\n // CORRECTED (Tier 3 Slice 4, Task 4.2 review): `putImmutable` is KEEP-FIRST on every adapter,\n // including S3 (via a create-only `IfNoneMatch: \"*\"` conditional PUT — see `objectstore-s3`'s\n // `putImmutable`). So this just-PUT `seg/${seqno}` can NEVER overwrite a live, manifest-\n // referenced segment written by whoever actually holds the frontier at that seqno — if a\n // concurrent/challenging writer already committed a DIFFERENT segment at this seqno, our PUT is\n // silently dropped (their bytes win) and OUR manifest CAS below still fails on its own stale\n // etag. The durable log is therefore safe FROM THAT DIRECTION by construction; poisoning here is\n // not a corruption guard against overwrite (there is no such hazard for `seg/${seqno}` itself)\n // but simply the correct response to an untrustworthy cursor after ANY CAS failure — the\n // instance must be RE-OPENED (re-bootstrapped from the true manifest), which resyncs\n // `cached`/`nextSeqno`.\n //\n // REVERSE DIRECTION (whole-branch review, Finding 1, Task 4.5, robustified Task 4.6): the\n // just-PUT `seg/${seqno}` is NOT simply \"an unreferenced orphan of our own, harmless to leave\n // behind\" — if THIS writer is in fact the one being fenced right now (a challenger's\n // `acquire()` just won), that orphan sits at exactly the seqno the challenger's manifest still\n // points at as `nextSeqno`. Keep-first means the challenger's own first commit could otherwise\n // reuse this seqno, have ITS `putImmutable` silently no-op against OUR bytes, and CAS a\n // manifest that references a segment holding OUR (failed) data instead of theirs — an\n // acknowledged write lost. What makes this safe, even across a CHAIN of such stalled\n // generations, is `acquire()`'s durable-burn-on-acquire fence (see its doc comment): every\n // takeover durably advances `manifest.nextSeqno` past the seqno a just-fenced predecessor could\n // have dirtied, so no later generation ever reuses this key. Reclaiming the orphan itself is\n // still GC's concern, not a correctness one.\n this.poisoned = true;\n if (isCasConflict(e)) {\n // Symmetric with `heartbeat()`'s fence path (Finding 2, whole-branch review, Task 4.5): a\n // confirmed fence (not merely an ambiguous error) means this instance is definitively no\n // longer the lease owner — clear `held` too, not just `poisoned`, so the asymmetry can't\n // become a latent trap if some future code path ever checked `held` before `poisoned`.\n // Harmless today (`poisoned` alone already blocks every further commit).\n this.held = null;\n throw new FencedError(\n `commit fenced: manifest for shard '${this.shard}' moved (stale etag) — this writer is no longer current and is now poisoned (re-open to continue)`,\n );\n }\n throw e;\n }\n\n // CAS succeeded: the durable log now includes this commit — it CANNOT be un-committed. Apply the\n // SAME rows to the local store via the explicit-ts path (bootstrap's own primitive) BEFORE\n // advancing the cached cursor, so `cached`/`nextSeqno` never claim \"caught up\" while `local` is\n // behind. If a post-CAS local write throws (a genuine local-store fault), the commit is still\n // DURABLE, so we must NOT surface a retryable-looking failure (which would double-commit under\n // fresh timestamps) — poison this instance (its local materialization is now inconsistent) and\n // throw a distinct, non-retryable error demanding a re-open. A restart re-bootstraps correctly\n // from the object log (which already contains this commit).\n try {\n for (const u of stampedUnits) {\n await this.local.write(u.documents, u.indexUpdates, \"Overwrite\", shardId);\n }\n } catch (e) {\n this.poisoned = true;\n throw new Error(\n `post-commit local apply failed after a DURABLE commit to shard '${this.shard}' (seqno ${seqno}); ` +\n `the commit is durable but this ObjectStoreDocStore's local materialization is inconsistent and must be re-opened. Cause: ${(e as Error)?.message ?? String(e)}`,\n );\n }\n this.cached = { manifest: next, etag };\n this.nextSeqno = seqno + 1;\n this.committedSegmentsSinceSnapshot++;\n\n return tsList;\n });\n // Outside the exclusive block (DEADLOCK HAZARD — see class doc / snapshot()'s own doc): a\n // snapshot itself takes the mutex, so triggering it from inside the commit's own exclusive\n // body would chain a second `runExclusive` onto a mutex this call still holds. Best-effort: a\n // snapshot failure must never fail an already-durable commit.\n await this.#maybeSnapshotBestEffort();\n return tsList;\n }\n\n async commitWrite(\n documents: readonly DocumentLogEntry[],\n indexUpdates: readonly IndexWrite[],\n shardId?: ShardId,\n opts?: { meta?: Record<string, string> },\n ): Promise<bigint> {\n const out = await this.commitWriteBatch([{ documents, indexUpdates, meta: opts?.meta }], shardId);\n return out[0]!;\n }\n\n // ── Snapshots (Tier 3 Slice 3, Task 3.2) — object-first, under the commit mutex ─────────────\n\n /**\n * Take a snapshot of the local store's current state and CAS the manifest to reference it, so a\n * future `open` can bootstrap in O(state + tail) instead of replaying the whole segment log.\n * Serializes against commits via `runExclusive` — same reasoning as `commitWriteBatch`: it reads\n * `cached`/`nextSeqno` and CAS's the manifest, so it must not race a concurrent commit's CAS\n * against the same cached etag.\n *\n * DEADLOCK HAZARD: `runExclusive` is NOT reentrant. Never call `snapshot()`/`maybeSnapshot()` from\n * INSIDE another `runExclusive` body (e.g. the commit path's own exclusive block) — chain it AFTER\n * that block resolves instead (see `commitWriteBatch`'s `#maybeSnapshotBestEffort` call site).\n *\n * BENIGN DIVERGENCE (whole-branch review, Minor #2): if this snapshot's boundary (`frontierTs`) is\n * a DELETE's ts, the dump it captures (`dumpCurrentState` excludes tombstones) has no row at that\n * ts — see the matching note at `open()`'s snapshot-restore site for the full explanation and why\n * it's safe (the `tsCounter` commit floor, not `local.maxTimestamp()`, is what guards ts reuse).\n */\n async snapshot(): Promise<void> {\n return this.runExclusive(async () => {\n if (this.poisoned) {\n throw new Error(\n `ObjectStoreDocStore for shard '${this.shard}' is poisoned (a prior post-commit local apply failed); it must be re-opened before further use`,\n );\n }\n if (this.nextSeqno === 0) return; // no committed segments yet — nothing to snapshot\n\n const dump = await this.local.dumpCurrentState();\n const frontierTs = this.cached.manifest.frontierTs;\n const segBase = this.nextSeqno - 1; // the last committed seqno\n const payload: SnapshotPayload = { frontierTs, segBase, documents: dump.documents, indexUpdates: dump.indexUpdates };\n\n // Object-first (same torn-forward discipline as segments): the snapshot object lands BEFORE\n // the manifest references it, so the manifest can never point at an absent snapshot.\n await writeSnapshot(this.objectStore, this.shard, payload);\n\n // TRIM `segments` to the post-snapshot tail (whole-branch review, Task 3.3 fix): everything\n // <= segBase is now covered by the snapshot itself — bootstrap's `open()` never reads them\n // again (see the segBase skip in the replay loop above) — so keeping them in the manifest only\n // grows this array without bound over the store's history (O(N²) commits, and a\n // `Math.max(...segments)` bootstrap read that would eventually RangeError). `nextSeqno` is\n // preserved untouched by the `{...cached.manifest}` spread below — a snapshot never advances or\n // rewinds the segment cursor, only `commitWriteBatch` does.\n const next: Manifest = {\n ...this.cached.manifest,\n snapshotTs: frontierTs,\n snapshotSegBase: segBase,\n segments: this.cached.manifest.segments.filter((s) => s > segBase),\n };\n try {\n const { etag } = await casManifest(this.objectStore, this.shard, next, this.cached.etag);\n this.cached = { manifest: next, etag };\n } catch (e) {\n // Same C1 discipline as the commit path: ANY casManifest failure (fence or ambiguous lost\n // response) leaves `cached`'s etag untrustworthy for the NEXT commit's CAS — poison rather\n // than risk a stale-etag reuse. Note `nextSeqno` is unaffected either way (a snapshot never\n // consumes a segment seqno), but the poisoned flag still forces a re-open before any further\n // commit or snapshot is served.\n this.poisoned = true;\n if (isCasConflict(e)) {\n throw new FencedError(\n `snapshot fenced: manifest for shard '${this.shard}' moved (stale etag) — this writer is no longer current and is now poisoned (re-open to continue)`,\n );\n }\n throw e;\n }\n this.committedSegmentsSinceSnapshot = 0;\n });\n }\n\n /** Take a snapshot if `SNAPSHOT_EVERY` segments have committed since the last one. No-op\n * (including a poisoned instance — the next commit/explicit `snapshot()` call will surface that\n * loudly) otherwise. */\n async maybeSnapshot(): Promise<void> {\n if (this.poisoned) return;\n if (this.committedSegmentsSinceSnapshot < SNAPSHOT_EVERY) return;\n await this.snapshot();\n }\n\n /** `maybeSnapshot()`, swallowing any error — called from OUTSIDE the commit's exclusive block\n * (see `commitWriteBatch`). A snapshot failure must never fail an already-durable commit; if it\n * poisoned this instance, the next commit's own poisoned-check surfaces that loudly instead. */\n async #maybeSnapshotBestEffort(): Promise<void> {\n try {\n await this.maybeSnapshot();\n } catch {\n // Swallowed by design — see doc comment above.\n }\n }\n\n // ── GC (Tier 3 Slice 3, Task 3.3) — reclaim what the latest snapshot has superseded ─────────\n\n /**\n * Reclaim durable objects the CURRENT manifest's snapshot (and every registered consumer's\n * watermark) has superseded: every segment with `seqno <= min(snapshotSegBase, W_min)` (where\n * `W_min` is the SLOWEST published `s{shard}/consumers/{id}` watermark's `appliedSeqno` — `+Infinity` when\n * no consumers are registered, so the floor collapses back to plain `snapshotSegBase`, byte-for-\n * byte the Slice 3 behavior) and every snapshot object except the current one (`snapshotTs`) — the\n * newest snapshot is always kept regardless of `W_min` (a replica re-materializing after falling\n * behind the tail restores the NEWEST snapshot via `materializeTo`'s fallback path, never an older\n * one, so keeping only the newest is correct independent of any watermark).\n *\n * The watermark floor is the Tier 3 Slice 5, Task 5.2 addition (closing the Slice 3/4 GC-under-\n * replicas deferral): a lagging replica's `ObjectStoreReplicaTailer` publishes its own\n * `appliedSeqno` via `publishConsumerWatermark`; as long as it hasn't fallen behind\n * `snapshotSegBase` itself (its own snapshot-fallback backstop covers that case — see\n * `replica-tailer.ts`), `gc()` here will never delete a segment `(W_min, snapshotSegBase]` that\n * replica still needs to tail.\n *\n * Runs under `runExclusive` — it must not race a concurrent commit/snapshot/acquire on THIS\n * instance. Never touches the manifest itself or `this.cached`/`nextSeqno` (beyond adopting a\n * freshly re-read manifest when the epoch check below passes) — GC is purely subtractive over\n * objects the manifest (and consumers) no longer need.\n *\n * PERFORMANCE NOTE (whole-branch review, Minor #4): because it runs under `runExclusive`, `gc()`\n * blocks `commitWriteBatch`/`snapshot()` on this instance for the ENTIRE list+delete sweep (now\n * also a `consumers/` list+get sweep) — fine for a manual, occasional, single-node call, but revisit\n * the locking granularity if GC's cadence ever needs to tighten.\n *\n * GC-FENCING (Tier 3 Slice 7, Task 7.1 — closes the Slice-4/5/6 deferral above): `gc()` used to\n * trust `this.cached.manifest` — a snapshot pointer that can be ARBITRARILY STALE the instant a\n * challenger fences this writer (bumps `epoch`) without this instance having attempted a\n * commit/heartbeat since. A stale writer's cached `snapshotTs` then names an OLD snapshot, and the\n * pre-Slice-7 \"delete every `snap/*` except my cached `snapshotTs`\" predicate would delete the NEW\n * owner's live snapshot — sinking that owner's next bootstrap. Fixed by two changes, in order:\n * 1. `this.held === null` (never an owner — a replica, or an already-demoted/fenced writer) is a\n * harmless no-op: never GC.\n * 2. RE-READ the manifest fresh (never trust `this.cached`) and compare its `epoch` against\n * `this.held.epoch`. A mismatch means a challenger's `acquire()` landed since we last knew —\n * we've been fenced RIGHT NOW, even though nothing told us yet — so we poison + demote and\n * delete NOTHING. Only once the epoch matches do we adopt the fresh manifest as current truth\n * and compute the delete floor/keepSnap FROM IT (never from a value read before this check).\n * This closes the TOCTOU window at the READ side, but a fence could still land in the gap BETWEEN\n * this re-read and the deletes below (the sweep isn't atomic with the epoch check) — so the delete\n * PREDICATES themselves must independently tolerate a same-gap fence:\n * - Segments: `seqno <= floor` (unchanged) — a new owner's commits only ever land at HIGHER\n * seqnos than the frontier this floor was computed from, so this predicate can never reach one.\n * - Snapshots: **`BigInt(ts) < BigInt(keepSnap)`** (strictly older), not \"every snapshot except\n * keepSnap\" — a new owner racing a snapshot into the gap produces a `ts` NEWER than our\n * `keepSnap` (ts's are monotone `frontierTs` values), which `<` by construction never matches.\n * `keepSnap` itself is also never deleted (not `<` itself). Compared as `bigint`, not string —\n * ts's are decimal-string bigints, and a naive string compare orders `\"9\"` after `\"10\"`.\n * Together: a fenced/stale writer's `gc()` deletes nothing (the epoch check catches the common case,\n * and the TOCTOU-safe predicates catch the rest), while the happy-path owner's behavior is unchanged\n * (its own re-read always matches its own held epoch).\n */\n async gc(): Promise<{ deletedSegments: number; deletedSnapshots: number }> {\n return this.runExclusive(async () => {\n if (this.poisoned) {\n throw new Error(\n `ObjectStoreDocStore for shard '${this.shard}' is poisoned (a prior post-commit local apply failed); it must be re-opened before further use`,\n );\n }\n if (this.held === null) {\n return { deletedSegments: 0, deletedSnapshots: 0 }; // not an owner (replica, or already fenced) — never GC\n }\n\n // RE-READ (never trust `this.cached` — it may be arbitrarily stale if we've been fenced since\n // our last commit/heartbeat). The shard's manifest must exist by now (this instance itself\n // `open()`'d/`acquire()`'d it).\n const fresh = await readManifest(this.objectStore, this.shard);\n if (fresh === null) {\n throw new Error(`objectstore-substrate: gc() found no manifest for shard '${this.shard}' — open() must initialize it first`);\n }\n if (fresh.manifest.epoch !== this.held.epoch) {\n // Fenced: a challenger's acquire() landed since we last knew. Poison + demote, delete\n // NOTHING — the fresh manifest's snapshot/segments belong to whoever holds the epoch now.\n this.poisoned = true;\n this.held = null;\n return { deletedSegments: 0, deletedSnapshots: 0 };\n }\n this.cached = fresh; // adopt current truth — safe, epoch matches what we hold\n\n if (fresh.manifest.snapshotTs === undefined) {\n return { deletedSegments: 0, deletedSnapshots: 0 }; // no snapshot yet — nothing to GC\n }\n const segBase = fresh.manifest.snapshotSegBase!;\n const keepSnap = fresh.manifest.snapshotTs!;\n const keepSnapBig = BigInt(keepSnap);\n\n const watermarks = await readConsumerWatermarks(this.objectStore, this.shard);\n const wMin = watermarks.length > 0 ? Math.min(...watermarks.map((w) => w.appliedSeqno)) : Number.POSITIVE_INFINITY;\n const floor = Math.min(segBase, wMin);\n\n const segPrefix = `s${this.shard}/seg/`;\n const segKeys = await this.objectStore.list(segPrefix);\n let deletedSegments = 0;\n for (const key of segKeys) {\n const suffix = key.slice(key.lastIndexOf(\"/\") + 1);\n const seqno = Number(suffix);\n // Defensive: skip any key whose suffix doesn't parse to an integer — never delete an\n // object we don't understand. NEVER delete seqno > floor (a live segment bootstrap or a\n // still-tailing replica may still need it); the predicate below is deliberately `<=`,\n // never anything looser.\n if (!Number.isInteger(seqno)) continue;\n if (seqno <= floor) {\n await this.objectStore.delete(key);\n deletedSegments++;\n }\n }\n\n const snapPrefix = `s${this.shard}/snap/`;\n const snapKeys = await this.objectStore.list(snapPrefix);\n let deletedSnapshots = 0;\n for (const key of snapKeys) {\n const ts = key.slice(key.lastIndexOf(\"/\") + 1);\n // Defensive parity with the segment loop above: never touch an object whose suffix isn't a\n // valid decimal-bigint ts — a malformed `snap/*` key is unreachable (only `writeSnapshot`\n // writes them, always with a numeric frontierTs), but a bare `BigInt(ts)` throw here would\n // wedge snapshot reclamation past that key on every swallow-driven sweep, so skip it.\n if (!/^\\d+$/.test(ts)) continue;\n // Strictly older than keepSnap (TOCTOU-safe — see the doc comment above): NEVER delete\n // keepSnap itself or anything >= it (a `>=` snapshot can only belong to a new owner that\n // raced ahead of us in the gap between the epoch re-read above and this sweep).\n if (BigInt(ts) < keepSnapBig) {\n await this.objectStore.delete(key);\n deletedSnapshots++;\n }\n }\n\n return { deletedSegments, deletedSnapshots };\n });\n }\n\n // ── Everything else: forward to the local materialized store ───────────────────────────────\n\n setupSchema(options?: SchemaSetupOptions): Promise<void> {\n return this.local.setupSchema(options);\n }\n\n write(\n documents: readonly DocumentLogEntry[],\n indexUpdates: readonly IndexWrite[],\n conflictStrategy: ConflictStrategy,\n shardId?: ShardId,\n ): Promise<void> {\n return this.local.write(documents, indexUpdates, conflictStrategy, shardId);\n }\n\n /** The materialized current state (Slice 5 — migration export). Delegates to the local SQLite\n * store, which already holds the full materialized image the object log reduces to — the same\n * source `snapshot()` dumps from. */\n dumpCurrentState(): Promise<{ documents: DocumentLogEntry[]; indexUpdates: IndexWrite[] }> {\n return this.local.dumpCurrentState();\n }\n\n addCommitGuard(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- store-specific querier type, mirrors DocStore's own signature\n guard: (q: any, units: readonly CommitGuardUnit[], shardId: ShardId) => void | Promise<void>,\n ): () => void {\n // Manifest-atomic guards + effectively-once forwarding are a LATER slice (the whole-arc plan's\n // \"Global constraints\": Slice 2 defers guard atomicity/effectively-once and just delegates to\n // the local store, same as it does for every other non-commit method). A guard registered here\n // runs inside the LOCAL SQLite commit only, AFTER the object-storage commit has already landed\n // durably — it is not part of the fence and cannot abort the durable write.\n return this.local.addCommitGuard(guard);\n }\n\n get(id: InternalDocumentId, readTimestamp?: bigint): Promise<LatestDocument | null> {\n return this.local.get(id, readTimestamp);\n }\n\n index_scan(\n indexId: string,\n tableId: string,\n readTimestamp: bigint,\n interval: Interval,\n order: Order,\n limit?: number,\n ): AsyncGenerator<readonly [Uint8Array, LatestDocument]> {\n return this.local.index_scan(indexId, tableId, readTimestamp, interval, order, limit);\n }\n\n load_documents(range: TimestampRange, order: Order, limit?: number): AsyncGenerator<DocumentLogEntry> {\n return this.local.load_documents(range, order, limit);\n }\n\n previous_revisions(queries: readonly PrevRevQuery[]): Promise<Map<string, DocumentLogEntry>> {\n return this.local.previous_revisions(queries);\n }\n\n scan(tableId: string, readTimestamp?: bigint): Promise<LatestDocument[]> {\n return this.local.scan(tableId, readTimestamp);\n }\n\n count(tableId: string): Promise<number> {\n return this.local.count(tableId);\n }\n\n maxTimestamp(): Promise<bigint> {\n return this.local.maxTimestamp();\n }\n\n getGlobal(key: string): Promise<JSONValue | null> {\n return this.local.getGlobal(key);\n }\n\n writeGlobal(key: string, value: JSONValue): Promise<void> {\n return this.local.writeGlobal(key, value);\n }\n\n writeGlobalIfAbsent(key: string, value: JSONValue): Promise<boolean> {\n return this.local.writeGlobalIfAbsent(key, value);\n }\n\n getClientVerdict(identity: string, clientId: string, seq: number): Promise<ClientVerdictRecord | null> {\n return this.local.getClientVerdict(identity, clientId, seq);\n }\n\n getClientFloor(identity: string, clientId: string): Promise<number | null> {\n return this.local.getClientFloor(identity, clientId);\n }\n\n recordClientVerdict(identity: string, clientId: string, seq: number, record: ClientVerdictWrite): Promise<void> {\n return this.local.recordClientVerdict(identity, clientId, seq, record);\n }\n\n updateClientVerdictValue(identity: string, clientId: string, seq: number, value: JSONValue): Promise<void> {\n return this.local.updateClientVerdictValue(identity, clientId, seq, value);\n }\n\n pruneClientMutations(\n identity: string,\n clientId: string,\n opts: { ackedThrough?: number; ttlBeforeMs?: number },\n ): Promise<{ prunedThroughSeq: number }> {\n return this.local.pruneClientMutations(identity, clientId, opts);\n }\n\n sweepExpiredClientMutations(beforeMs: number): Promise<{ deletedCount: number }> {\n return this.local.sweepExpiredClientMutations(beforeMs);\n }\n\n close(): void | Promise<void> {\n // The object store is a stateless client (an HTTP/fs handle, nothing to release) — only the\n // local store owns a resource (the SQLite connection/file) that needs closing.\n return this.local.close();\n }\n}\n","/**\n * The snapshot codec + object helpers (Tier 3 Slice 3, design record §6b, Task 3.1) — a snapshot\n * `s{shard}/snap/{ts}` is a materialized image of a shard's CURRENT state at `frontierTs = ts`: the\n * current (non-tombstone) revision of every live document at its REAL `ts`/`prev_ts`, plus the\n * current row of every index entry. Produced by `SqliteDocStore.dumpCurrentState()` and restored via\n * the SAME `write(..., \"Overwrite\")` primitive segments use — see that method's doc comment.\n *\n * The wire codec deliberately REUSES `segment.ts`'s per-row `DocumentLogEntry`/`IndexWrite`\n * bigint/base64/`Value` tagging (`encodeDocumentLogEntries`/`decodeDocumentLogEntries`/\n * `encodeIndexWrites`/`decodeIndexWrites`) rather than duplicating it — a snapshot's `documents`/\n * `indexUpdates` are the exact same row shapes a segment carries; only the envelope (`frontierTs`/\n * `segBase` instead of none) differs.\n */\nimport type { ObjectStore } from \"@helipod/objectstore\";\nimport type { DocumentLogEntry, IndexWrite } from \"@helipod/docstore\";\nimport {\n encodeDocumentLogEntries,\n decodeDocumentLogEntries,\n encodeIndexWrites,\n decodeIndexWrites,\n type WireDocumentLogEntry,\n type WireIndexWrite,\n} from \"./segment\";\n\n/** A snapshot's payload — the CURRENT state of a shard's local store at `frontierTs`, covering\n * every committed segment up to and including seqno `segBase` (bootstrap replays only segments\n * with seqno > `segBase` after restoring this). `documents`/`indexUpdates` are the same\n * `DocumentLogEntry`/`IndexWrite` rows `SqliteDocStore.dumpCurrentState()` returns — real `ts`/\n * `prev_ts`, not renumbered. */\nexport interface SnapshotPayload {\n frontierTs: string;\n segBase: number;\n documents: DocumentLogEntry[];\n indexUpdates: IndexWrite[];\n}\n\ninterface WireSnapshotPayload {\n frontierTs: string;\n segBase: number;\n documents: WireDocumentLogEntry[];\n indexUpdates: WireIndexWrite[];\n}\n\n/** Encode a snapshot's payload to bytes (UTF-8 JSON) for `ObjectStore.putImmutable`. */\nexport function encodeSnapshot(payload: SnapshotPayload): Uint8Array {\n const wire: WireSnapshotPayload = {\n frontierTs: payload.frontierTs,\n segBase: payload.segBase,\n documents: encodeDocumentLogEntries(payload.documents),\n indexUpdates: encodeIndexWrites(payload.indexUpdates),\n };\n return new TextEncoder().encode(JSON.stringify(wire));\n}\n\n/** Decode snapshot bytes (as read from `ObjectStore.get`) back to `SnapshotPayload`. Inverse of\n * {@link encodeSnapshot} — round-trips bigints and byte arrays exactly, same as `decodeSegment`. */\nexport function decodeSnapshot(bytes: Uint8Array): SnapshotPayload {\n const wire = JSON.parse(new TextDecoder().decode(bytes)) as WireSnapshotPayload;\n return {\n frontierTs: wire.frontierTs,\n segBase: wire.segBase,\n documents: decodeDocumentLogEntries(wire.documents),\n indexUpdates: decodeIndexWrites(wire.indexUpdates),\n };\n}\n\n/** The object key a shard's snapshot at `ts` (a decimal-string `frontierTs`, matching the manifest's\n * own string-bigint convention) lives at — `s{shard}/snap/{ts}`, parallel to `segmentKey`'s\n * `s{shard}/seg/{seqno}` in `object-doc-store.ts`. */\nexport function snapshotKey(shard: string, ts: string): string {\n return `s${shard}/snap/${ts}`;\n}\n\n/** Write a snapshot object (immutable — one snapshot per `frontierTs`, never overwritten). Callers\n * write the snapshot object FIRST, then CAS the manifest to reference it (Task 3.2) — never the\n * reverse, so the manifest can never point at an absent snapshot (the same torn-forward discipline\n * segments follow). */\nexport async function writeSnapshot(os: ObjectStore, shard: string, payload: SnapshotPayload): Promise<void> {\n await os.putImmutable(snapshotKey(shard, payload.frontierTs), encodeSnapshot(payload));\n}\n\n/** Read + decode a shard's snapshot at `ts`, or `null` if no such snapshot object exists. */\nexport async function readSnapshot(os: ObjectStore, shard: string, ts: string): Promise<SnapshotPayload | null> {\n const entry = await os.get(snapshotKey(shard, ts));\n if (entry === null) return null;\n return decodeSnapshot(entry.body);\n}\n","/* Helipod Enterprise. Licensed under the Helipod Commercial License — see ee/LICENSE. */\n/**\n * Thrown when `ObjectStoreDocStore`'s manifest CAS loses the race — this writer's cached etag no\n * longer matches the shard's `s{shard}/manifest` object, meaning some other committer's write\n * already advanced it. The manifest CAS IS the fence for this substrate (design record §4/§7):\n * unlike `@helipod/fleet`'s Postgres-epoch fence (`FencedError` on a zero-row epoch-predicated\n * UPDATE, see `ee/packages/fleet/src/fenced-error.ts`), here the fence is the object store's own\n * conditional-write primitive (`CasConflict` on `casPut`) rather than a database row.\n *\n * This package deliberately does NOT import `@helipod/fleet`'s `FencedError` — the object-storage\n * substrate is an ALTERNATIVE single-shard store to fleet's Postgres store, not a consumer of it, and\n * the whole-arc plan keeps this package leaf-dependency-free of `ee/packages/fleet`. A later\n * multi-shard/failover slice over this substrate is expected to unify the two error types (or at\n * least their self-demote handling) once fleet-style writer promotion/relinquish lands here too.\n *\n * Same retry contract as fleet's: the transactor only OCC-retries `OccConflictError`. `FencedError`\n * is any other error as far as the transactor is concerned, so it propagates uncaught and is NEVER\n * retried — a fenced writer must stop (its cached manifest state is stale), not blindly resend the\n * same commit against a manifest that has already moved on.\n */\nexport class FencedError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"FencedError\";\n }\n}\n","/* Helipod Enterprise. Licensed under the Helipod Commercial License — see ee/LICENSE. */\n/**\n * Consumer watermarks (Tier 3 Slice 5, Task 5.2, design record §6c) — a per-shard\n * `s{shard}/consumers/{id}` object per registered consumer (a replica's `ObjectStoreReplicaTailer`,\n * keyed by whatever `consumerId` the caller chooses — e.g. a per-`(replica, shard)` id) carrying the\n * seqno it has durably applied. `ObjectStoreDocStore.gc()` floors its deletion at the SLOWEST\n * published watermark ON ITS OWN SHARD so it never reclaims a segment a lagging replica still needs\n * to tail.\n *\n * SHARD-SCOPED (Finding 2, whole-branch review): keys were originally bucket-global (`consumers/{id}`),\n * but `gc()` is per-shard with per-shard seqno spaces (a seqno on shard \"0\" and the SAME numeric\n * seqno on shard \"1\" are unrelated cursors) — a bucket-global watermark set meant a stuck consumer on\n * ANY shard floored GC on EVERY shard, over-retaining bucket-wide instead of just on its own shard.\n * Scoping the key prefix to `s{shard}/consumers/{consumerId}` confines that (already-safe,\n * never-under-retains) over-retention to the shard the lagging consumer is actually on.\n *\n * Unlike a segment (`putImmutable`, keep-first-immutable) or the manifest (`casManifest`, the fence\n * a WRITER contends on), a watermark is a single value that must be OVERWRITABLE as its owning\n * consumer advances — `putImmutable`'s keep-first semantics (Tier 3 Slice 4 fix) make it unusable\n * here. This module instead does a plain read-etag-then-`casPut` upsert: `get()` the current etag (or\n * `null` if the object doesn't exist yet, which makes the `casPut` create-only), then CAS the new\n * value over it. A watermark is single-writer-per-`(shard, consumerId)` in the intended usage (one\n * tailer instance publishes its own watermark), so a lost CAS race is expected only from a genuine\n * concurrent writer under the SAME key (a misconfiguration, or a brief overlap during a consumer's\n * own restart) — handled with a small bounded retry (re-read, re-CAS) rather than treated as a hard\n * error on the first conflict.\n */\nimport { isCasConflict, type ObjectStore } from \"@helipod/objectstore\";\n\nfunction consumersPrefix(shard: string): string {\n return `s${shard}/consumers/`;\n}\n\nfunction consumerKey(shard: string, consumerId: string): string {\n return `${consumersPrefix(shard)}${consumerId}`;\n}\n\ninterface WatermarkBody {\n appliedSeqno: number;\n}\n\n/** Upsert `consumerId`'s watermark to `appliedSeqno` on `shard` — overwritable (see module doc), NOT\n * `putImmutable`. Retries a handful of times on a lost CAS race (re-reading the current etag each\n * time) before giving up loudly; a genuine single-writer-per-`(shard, consumerId)` caller should\n * never exhaust this. */\nexport async function publishConsumerWatermark(\n os: ObjectStore,\n shard: string,\n consumerId: string,\n watermark: { appliedSeqno: number },\n): Promise<void> {\n const key = consumerKey(shard, consumerId);\n const body = new TextEncoder().encode(JSON.stringify({ appliedSeqno: watermark.appliedSeqno } satisfies WatermarkBody));\n\n const MAX_ATTEMPTS = 5;\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const existing = await os.get(key);\n try {\n await os.casPut(key, body, existing === null ? null : existing.etag);\n return;\n } catch (e) {\n if (!isCasConflict(e)) throw e;\n // Lost the CAS race — someone else (expected: another publish from the same consumer,\n // e.g. an overlapping restart) wrote first. Re-read and retry with the fresh etag.\n }\n }\n throw new Error(\n `objectstore-substrate: publishConsumerWatermark exhausted ${MAX_ATTEMPTS} retries for '${key}' — ` +\n `unexpected sustained contention (watermarks are meant to be single-writer-per-(shard, consumerId))`,\n );\n}\n\n/** List every consumer registered on `shard` and its published watermark. Order is whatever\n * `os.list()` returns (unspecified) — callers that need `min(appliedSeqno)` (GC) reduce over the\n * whole set anyway. */\nexport async function readConsumerWatermarks(os: ObjectStore, shard: string): Promise<{ consumerId: string; appliedSeqno: number }[]> {\n const prefix = consumersPrefix(shard);\n const keys = await os.list(prefix);\n const out: { consumerId: string; appliedSeqno: number }[] = [];\n for (const key of keys) {\n const entry = await os.get(key);\n if (entry === null) continue; // raced delete between list() and get() — skip, not fatal\n const parsed = JSON.parse(new TextDecoder().decode(entry.body)) as WatermarkBody;\n out.push({ consumerId: key.slice(prefix.length), appliedSeqno: parsed.appliedSeqno });\n }\n return out;\n}\n\n/** Deregister a departing consumer (e.g. a decommissioned replica) on `shard` so it no longer floors\n * that shard's GC. */\nexport async function removeConsumer(os: ObjectStore, shard: string, consumerId: string): Promise<void> {\n await os.delete(consumerKey(shard, consumerId));\n}\n","/* Helipod Enterprise. Licensed under the Helipod Commercial License — see ee/LICENSE. */\n/**\n * `applySnapshotState` (Tier 3 Slice 5 re-review fix) — the MVCC-safe way to restore a\n * `SnapshotPayload` onto an already-materialized local `SqliteDocStore`: `write(..., \"Overwrite\")`\n * is an OVERLAY (INSERT OR REPLACE), never a replace-all, and a snapshot's `documents` (sourced\n * from `SqliteDocStore.dumpCurrentState()`) EXCLUDES tombstones — so applying a snapshot's rows\n * alone onto a NON-EMPTY store cannot express \"this doc was deleted in the range this restore\n * jumps over.\" A doc the store still has LIVE that the snapshot silently dropped would otherwise\n * stay phantom-live on that store forever.\n *\n * The fix: diff the store's OWN current live docs against the snapshot's live-doc set and APPEND a\n * tombstone (`value: null`) for anything the snapshot dropped, at `frontierTs` (>= every existing\n * revision on a store genuinely behind the snapshot whenever this runs) — append-only, so a\n * concurrent MVCC read against the store never sees rows physically disappear mid-restore. Never\n * truncates the store to fake a \"fresh\" restore.\n *\n * Originally lived inline in `replica-tailer.ts`'s `#materializeRound` (Task 5.1's Finding-1 fix,\n * whole-branch review). Extracted here (Slice 5 re-review) because `object-doc-store.ts`'s\n * `materializeTo()` — the WRITER's own catch-up path, driven from BOTH `open()` (always onto an\n * empty local, safe either way) AND `acquire()`'s takeover catch-up (onto an already-open,\n * POSSIBLY NON-EMPTY, stale writer instance — dangerous without this fix) — runs the identical\n * snapshot-restore-onto-a-possibly-non-empty-store shape and needed the exact same fix: a fenced\n * writer that re-`acquire()`s after another writer deleted a doc + snapshotted + GC'd its\n * pre-snapshot segments could otherwise resurrect the deleted doc in its own local store and\n * RE-COMMIT it, permanently undoing the delete in the durable log. Single implementation, both call\n * sites — see each call site's own comment for how it folds (or ignores) the result.\n *\n * On an EMPTY local store (the common `open()` fresh-bootstrap path, and the replica tailer's own\n * first-ever round) `dumpCurrentState()` returns no documents, so the diff is a no-op and this is\n * byte-identical to a plain snapshot apply — no behavior change for that path.\n */\nimport type { DocumentLogEntry } from \"@helipod/docstore\";\nimport type { SqliteDocStore } from \"@helipod/docstore-sqlite\";\nimport { documentIdKey } from \"@helipod/id-codec\";\nimport type { SnapshotPayload } from \"./snapshot\";\n\n/**\n * Restore `snap` onto `local`, tombstoning (at `frontierTs`) any doc `local` currently holds LIVE\n * that `snap` doesn't mention, THEN applying `snap`'s own rows — both via the same explicit-ts\n * `write(..., \"Overwrite\")` primitive every other materialization path uses.\n *\n * Returns the tombstones actually appended (empty if `local` had nothing to drop) so a caller that\n * tracks reactive invalidation (the replica tailer) can fold them into its own written-docs set; a\n * caller with no invalidation sink to feed (the writer's own catch-up — its transactor/tailer own\n * reactivity, not this method) may simply ignore the return value.\n */\nexport async function applySnapshotState(\n local: SqliteDocStore,\n snap: SnapshotPayload,\n frontierTs: bigint,\n): Promise<{ deletedDocs: DocumentLogEntry[] }> {\n const snapshotIds = new Set(snap.documents.map((d) => documentIdKey(d.id)));\n const currentState = await local.dumpCurrentState();\n const dropped = currentState.documents.filter((d) => !snapshotIds.has(documentIdKey(d.id)));\n\n let deletedDocs: DocumentLogEntry[] = [];\n if (dropped.length > 0) {\n deletedDocs = dropped.map((d) => ({ ts: frontierTs, id: d.id, value: null, prev_ts: d.ts }));\n await local.write(deletedDocs, [], \"Overwrite\");\n }\n\n await local.write(snap.documents, snap.indexUpdates, \"Overwrite\");\n\n return { deletedDocs };\n}\n","/* Helipod Enterprise. Licensed under the Helipod Commercial License — see ee/LICENSE. */\n/**\n * `ShardedObjectStoreDocStore` — object-storage multi-shard SINGLE-NODE write scale-out: one node\n * owns ALL N object-storage lanes (each a full `ObjectStoreDocStore`, design record §5's `s{shard}/…`\n * physically independent prefix) and this class composes them behind ONE `DocStore` the engine's\n * `ShardedTransactor` talks to exactly as it talks to a single store today.\n *\n * OWNERSHIP: this class is a pure DocStore-shaped decorator over an already-open, already-`acquire()`d\n * `Map<ShardId, ObjectStoreDocStore>` — it does NOT open/acquire/heartbeat/gc the lanes itself (the\n * boot layer, `packages/cli/src/boot.ts`, drives each lane's lease/heartbeat/gc independently, so a\n * fence on one lane doesn't wait on another lane's cadence). This mirrors `ObjectStoreDocStore`'s own\n * \"everything else forwards\" shape, just fanned out over N lanes instead of one.\n *\n * ROUTING CONTRACT (confirmed against `packages/transactor/src/shard-writer.ts`): `commitWrite`/\n * `commitWriteBatch`/`write` are ALWAYS called by the transactor with an explicit `shardId` (the\n * `ShardedTransactor`'s own `ShardWriter.shardId`) — that is the ONE input that tells this class which\n * lane owns the commit. `get`/`scan`/`index_scan`/`load_documents`/`previous_revisions` are NEVER\n * called with a shard id (a query spans every shard) — so every read method here MERGES across every\n * lane. This is the asymmetry the whole class is built around: writes route by an explicit key, reads\n * fan out and merge.\n *\n * READ-MERGE CORRECTNESS NOTE (the caveat worth flagging to a reviewer): `get`/`scan`/\n * `previous_revisions` probe EVERY lane (a doc lives in exactly one lane — the one-doc-one-ring\n * invariant B2a established — but the `DocStore` interface gives `get(id)` no shard hint, since the\n * shard key is a document FIELD, not derivable from the internal id alone). This is O(N lanes) local\n * SQLite calls per read — correct, and cheap in practice (every lane is a FULLY MATERIALIZED local\n * store per design §6a, so this is N in-process SQLite lookups, never network I/O), but is a real cost\n * that grows with shard count. A future optimization could route `get`/`previous_revisions` directly\n * when a caller happens to know the shard; out of scope here.\n */\nimport type {\n ClientVerdictRecord,\n ClientVerdictWrite,\n CommitGuardUnit,\n CommitUnit,\n ConflictStrategy,\n DocStore,\n DocumentLogEntry,\n Interval,\n LatestDocument,\n Order,\n PrevRevQuery,\n SchemaSetupOptions,\n ShardId,\n TimestampRange,\n InternalDocumentId,\n IndexWrite,\n} from \"@helipod/docstore\";\nimport { DEFAULT_SHARD } from \"@helipod/id-codec\";\nimport type { JSONValue } from \"@helipod/values\";\nimport { mergeSortedAsyncGenerators, compareBytesLex, compareBigint } from \"./merge-sorted\";\n\nexport interface ShardedObjectStoreDocStoreOpts {\n /** The lane this class routes deployment-level bookkeeping to (globals, client-verdict receipts) —\n * a single source of truth rather than N independently-diverging copies. Unset -> `\"default\"`\n * (`DEFAULT_SHARD`), matching the un-sharded-table convention every other routing seam in the repo\n * uses (`shard.ts`'s `DEFAULT_SHARD`, `jump-hash.ts`'s slot 0). MUST be a key present in `lanes`. */\n defaultShard?: ShardId;\n}\n\nexport class ShardedObjectStoreDocStore implements DocStore {\n private readonly lanes: ReadonlyMap<ShardId, DocStore>;\n private readonly defaultShard: ShardId;\n\n constructor(lanes: ReadonlyMap<ShardId, DocStore>, opts?: ShardedObjectStoreDocStoreOpts) {\n if (lanes.size === 0) {\n throw new Error(\"ShardedObjectStoreDocStore: at least one lane is required\");\n }\n const defaultShard = opts?.defaultShard ?? DEFAULT_SHARD;\n if (!lanes.has(defaultShard)) {\n throw new Error(\n `ShardedObjectStoreDocStore: defaultShard '${defaultShard}' is not one of the composed lanes (${[...lanes.keys()].join(\", \")})`,\n );\n }\n this.lanes = lanes;\n this.defaultShard = defaultShard;\n }\n\n private lane(shardId: ShardId): DocStore {\n const l = this.lanes.get(shardId);\n if (!l) {\n throw new Error(\n `ShardedObjectStoreDocStore: unknown shard '${shardId}' — composed lanes are (${[...this.lanes.keys()].join(\", \")})`,\n );\n }\n return l;\n }\n\n private laneList(): DocStore[] {\n return [...this.lanes.values()];\n }\n\n // ── Writes — route by the caller-supplied shardId (the ShardedTransactor always passes one for\n // commits; `write`'s replica/bootstrap callers do too — see `ObjectStoreDocStore.materializeTo`) ─\n\n setupSchema(options?: SchemaSetupOptions): Promise<void> {\n return Promise.all(this.laneList().map((l) => l.setupSchema(options))).then(() => undefined);\n }\n\n // NOTE: these three are deliberately `async` (not a bare `return this.lane(...)...`) so an\n // unknown-shardId lookup failure — thrown SYNCHRONOUSLY by `this.lane()` — surfaces as a REJECTED\n // promise, matching every other `DocStore` method's async contract, rather than throwing\n // synchronously out of the call before the caller ever gets a promise to await/catch.\n async write(\n documents: readonly DocumentLogEntry[],\n indexUpdates: readonly IndexWrite[],\n conflictStrategy: ConflictStrategy,\n shardId?: ShardId,\n ): Promise<void> {\n return this.lane(shardId ?? this.defaultShard).write(documents, indexUpdates, conflictStrategy, shardId);\n }\n\n async commitWrite(\n documents: readonly DocumentLogEntry[],\n indexUpdates: readonly IndexWrite[],\n shardId?: ShardId,\n opts?: { meta?: Record<string, string> },\n ): Promise<bigint> {\n return this.lane(shardId ?? this.defaultShard).commitWrite(documents, indexUpdates, shardId, opts);\n }\n\n async commitWriteBatch(units: readonly CommitUnit[], shardId?: ShardId): Promise<bigint[]> {\n return this.lane(shardId ?? this.defaultShard).commitWriteBatch(units, shardId);\n }\n\n /**\n * Fan the SAME guard out to every lane (one registration per lane) — a guard for the sharded case\n * therefore fences/effects PER LANE, not atomically across the whole sharded store. This matches\n * `ObjectStoreDocStore`'s own single-lane note (\"guard atomicity + effectively-once forwarding are a\n * LATER slice\") — composing N of them doesn't add a NEW atomicity gap, it just makes the existing\n * per-lane-only scope explicit at the sharded layer too.\n */\n addCommitGuard(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- store-specific querier type, mirrors DocStore's own signature\n guard: (q: any, units: readonly CommitGuardUnit[], shardId: ShardId) => void | Promise<void>,\n ): () => void {\n const unregs = this.laneList().map((l) => l.addCommitGuard(guard));\n return () => {\n for (const unreg of unregs) unreg();\n };\n }\n\n // ── Reads — MERGE across every lane (the transactor never tells us which shard a read is for) ────\n\n async get(id: InternalDocumentId, readTimestamp?: bigint): Promise<LatestDocument | null> {\n const hits = await Promise.all(this.laneList().map((l) => l.get(id, readTimestamp)));\n for (const hit of hits) if (hit !== null) return hit;\n return null;\n }\n\n async *index_scan(\n indexId: string,\n tableId: string,\n readTimestamp: bigint,\n interval: Interval,\n order: Order,\n limit?: number,\n ): AsyncGenerator<readonly [Uint8Array, LatestDocument]> {\n const generators = this.laneList().map((l) => l.index_scan(indexId, tableId, readTimestamp, interval, order, limit));\n yield* mergeSortedAsyncGenerators(generators, (a, b) => compareBytesLex(a[0], b[0]), order, limit);\n }\n\n async *load_documents(range: TimestampRange, order: Order, limit?: number): AsyncGenerator<DocumentLogEntry> {\n const generators = this.laneList().map((l) => l.load_documents(range, order, limit));\n yield* mergeSortedAsyncGenerators(generators, (a, b) => compareBigint(a.ts, b.ts), order, limit);\n }\n\n async previous_revisions(queries: readonly PrevRevQuery[]): Promise<Map<string, DocumentLogEntry>> {\n const perLane = await Promise.all(this.laneList().map((l) => l.previous_revisions(queries)));\n const merged = new Map<string, DocumentLogEntry>();\n for (const laneResult of perLane) {\n for (const [key, entry] of laneResult) merged.set(key, entry);\n }\n return merged;\n }\n\n async scan(tableId: string, readTimestamp?: bigint): Promise<LatestDocument[]> {\n const perLane = await Promise.all(this.laneList().map((l) => l.scan(tableId, readTimestamp)));\n return perLane.flat();\n }\n\n async count(tableId: string): Promise<number> {\n const perLane = await Promise.all(this.laneList().map((l) => l.count(tableId)));\n return perLane.reduce((sum, n) => sum + n, 0);\n }\n\n async maxTimestamp(): Promise<bigint> {\n const perLane = await Promise.all(this.laneList().map((l) => l.maxTimestamp()));\n let max = 0n;\n for (const ts of perLane) if (ts > max) max = ts;\n return max;\n }\n\n // ── Deployment-level bookkeeping — route to ONE lane (the default shard) consistently, never\n // fan out: these are single-source-of-truth concerns (fleet identity, per-client receipts), not\n // per-shard data. Routing every deployment ever to the SAME lane means a client's receipts/floor\n // are never split across lanes regardless of which table its mutations touched. ────────────────\n\n getGlobal(key: string): Promise<JSONValue | null> {\n return this.lane(this.defaultShard).getGlobal(key);\n }\n\n writeGlobal(key: string, value: JSONValue): Promise<void> {\n return this.lane(this.defaultShard).writeGlobal(key, value);\n }\n\n writeGlobalIfAbsent(key: string, value: JSONValue): Promise<boolean> {\n return this.lane(this.defaultShard).writeGlobalIfAbsent(key, value);\n }\n\n getClientVerdict(identity: string, clientId: string, seq: number): Promise<ClientVerdictRecord | null> {\n return this.lane(this.defaultShard).getClientVerdict(identity, clientId, seq);\n }\n\n getClientFloor(identity: string, clientId: string): Promise<number | null> {\n return this.lane(this.defaultShard).getClientFloor(identity, clientId);\n }\n\n recordClientVerdict(identity: string, clientId: string, seq: number, record: ClientVerdictWrite): Promise<void> {\n return this.lane(this.defaultShard).recordClientVerdict(identity, clientId, seq, record);\n }\n\n updateClientVerdictValue(identity: string, clientId: string, seq: number, value: JSONValue): Promise<void> {\n return this.lane(this.defaultShard).updateClientVerdictValue(identity, clientId, seq, value);\n }\n\n pruneClientMutations(\n identity: string,\n clientId: string,\n opts: { ackedThrough?: number; ttlBeforeMs?: number },\n ): Promise<{ prunedThroughSeq: number }> {\n return this.lane(this.defaultShard).pruneClientMutations(identity, clientId, opts);\n }\n\n sweepExpiredClientMutations(beforeMs: number): Promise<{ deletedCount: number }> {\n return this.lane(this.defaultShard).sweepExpiredClientMutations(beforeMs);\n }\n\n async close(): Promise<void> {\n await Promise.all(this.laneList().map((l) => l.close()));\n }\n}\n","/* Helipod Enterprise. Licensed under the Helipod Commercial License — see ee/LICENSE. */\n/**\n * A generic k-way merge over N already-sorted `AsyncGenerator`s, used by\n * `ShardedObjectStoreDocStore` to merge each lane's own `index_scan`/`load_documents` stream into\n * ONE globally-ordered stream without ever buffering a whole lane into memory.\n *\n * Each source generator MUST already yield in the SAME order this merge is told to produce (the\n * caller — `ShardedObjectStoreDocStore` — gets this for free: it calls every lane's `index_scan`/\n * `load_documents` with the identical `order` argument, and each lane's own store already honors\n * that ordering, exactly as `SqliteDocStore.index_scan`'s SQL `ORDER BY i.key ASC|DESC` does). This\n * function does not itself sort anything — it only ever compares the CURRENT head of each source.\n */\n\n/** Byte-lexicographic comparison — matches SQLite's own BLOB ordering (unsigned byte-wise), which\n * `SqliteDocStore.index_scan`'s `ORDER BY i.key` already relies on, so merging N lanes' index-scan\n * streams by this comparator reproduces the SAME total order a single, unsharded store would. */\nexport function compareBytesLex(a: Uint8Array, b: Uint8Array): number {\n const len = Math.min(a.length, b.length);\n for (let i = 0; i < len; i++) {\n const diff = a[i]! - b[i]!;\n if (diff !== 0) return diff;\n }\n return a.length - b.length;\n}\n\n/** bigint comparison — for merging `load_documents`' `ts`-ordered streams. */\nexport function compareBigint(a: bigint, b: bigint): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\n/**\n * Merge `sources` (each already sorted per `keyCompare`/`order`) into one sorted `AsyncGenerator`,\n * stopping after `limit` yielded items (or exhausting every source when `limit` is undefined).\n *\n * Algorithm: prime every source's first item, then repeatedly pick the source whose current head\n * compares smallest (`order: \"asc\"`) or largest (`order: \"desc\"`) per `keyCompare`, yield it, and\n * advance ONLY that source. This is the textbook k-way merge — O(log k) per step with a heap, but a\n * linear scan over k sources here (k = shard count, expected small — tens at most) is simpler and\n * fast enough; a heap can replace the linear scan later if shard counts ever grow large.\n *\n * `limit`, when set, is a HARD cap on the number of items this generator yields — matching every\n * `DocStore.index_scan`/`load_documents` implementation's own `limit` contract. Passing the SAME\n * `limit` down to each individual source (as `ShardedObjectStoreDocStore` does) is always a safe\n * over-fetch: the merged output can never need MORE than `limit` items from any single source, since\n * the whole merged output is capped at `limit`.\n *\n * On early exit (limit reached, or the caller stops iterating this generator before exhaustion — a\n * `for await` `break`), every NOT-YET-EXHAUSTED source generator is `.return()`'d so it can release\n * whatever resources it's holding (mirroring how a single-store `AsyncGenerator` is expected to clean\n * up on an early `.return()` call — the same contract, just fanned out over N sources).\n */\nexport async function* mergeSortedAsyncGenerators<T>(\n sources: readonly AsyncGenerator<T>[],\n keyCompare: (a: T, b: T) => number,\n order: \"asc\" | \"desc\",\n limit?: number,\n): AsyncGenerator<T> {\n interface Cursor {\n gen: AsyncGenerator<T>;\n current: T | undefined;\n done: boolean;\n }\n const cursors: Cursor[] = sources.map((gen) => ({ gen, current: undefined, done: false }));\n\n async function advance(c: Cursor): Promise<void> {\n const r = await c.gen.next();\n if (r.done) {\n c.done = true;\n c.current = undefined;\n } else {\n c.current = r.value;\n }\n }\n\n try {\n await Promise.all(cursors.map((c) => advance(c)));\n\n let yielded = 0;\n for (;;) {\n if (limit !== undefined && yielded >= limit) return;\n\n let bestIdx = -1;\n for (let i = 0; i < cursors.length; i++) {\n const c = cursors[i]!;\n if (c.done) continue;\n if (bestIdx === -1) {\n bestIdx = i;\n continue;\n }\n const cmp = keyCompare(c.current as T, cursors[bestIdx]!.current as T);\n if (order === \"asc\" ? cmp < 0 : cmp > 0) bestIdx = i;\n }\n if (bestIdx === -1) return; // every source exhausted\n\n const winner = cursors[bestIdx]!;\n yield winner.current as T;\n yielded++;\n await advance(winner);\n }\n } finally {\n // Release any source generator this merge never fully drained (limit hit, or the consumer broke\n // out of a `for await` early) — best-effort, never let a cleanup failure mask the real outcome.\n await Promise.all(\n cursors\n .filter((c) => !c.done)\n .map(async (c) => {\n try {\n await c.gen.return(undefined);\n } catch {\n /* best-effort cleanup only */\n }\n }),\n );\n }\n}\n","/* Helipod Enterprise. Licensed under the Helipod Commercial License — see ee/LICENSE. */\n/**\n * Offline object-storage reshard (`docs/superpowers/plans/2026-02-20-objectstore-reshard.md`).\n *\n * Changes a STOPPED object-storage deployment's shard count N→M. Unlike the fleet reshard (logical lanes\n * over one shared store → moves no rows), object-storage lanes each have their own physical log, so a doc\n * whose lane changes (`shardIdForKeyValue(doc[shardKey], N) ≠ …M`) has its current state PHYSICALLY MOVED\n * between lane logs. The operation:\n * 1. GATE — refuse if any source lane has a live lease (an online reshard is out of scope).\n * 2. MATERIALIZE every source lane's current state into memory (`dumpCurrentState`).\n * 3. RE-PARTITION each doc by `shardIdForKeyValue(doc[table.shardKey], M)` (a doc's table with no\n * shardKey → the \"default\" lane), routing each live index entry to the same lane as its doc.\n * 4. REWRITE — delete all objects for every lane in source∪target, then write each target lane fresh\n * (open empty → acquire → commit its re-partitioned docs+index → relinquish).\n * 5. Set `globals.numShards = M` LAST — the linearization point.\n *\n * CRASH-SAFETY, honest: object storage has no cross-object transaction, so step 4 is a NON-ATOMIC full\n * rewrite. A crash mid-rewrite leaves the bucket partially rewritten and is NOT resumable — the contract\n * is OFFLINE, against a BACKED-UP bucket, don't interrupt. (The whole current state is read into memory\n * in step 2 first, so the destructive window is as short as possible and never races the read.)\n */\nimport type { ObjectStore } from \"@helipod/objectstore\";\nimport type { SqliteDocStore } from \"@helipod/docstore-sqlite\";\nimport type { DocumentLogEntry, IndexWrite } from \"@helipod/docstore\";\nimport { shardIdList, shardIdForKeyValue, DEFAULT_SHARD, documentIdKey, type ShardId } from \"@helipod/id-codec\";\nimport { ObjectStoreDocStore } from \"./object-doc-store\";\nimport { readManifest } from \"./manifest\";\nimport { readGlobals, writeGlobals } from \"./globals\";\n\n/** Thrown when reshard is asked to run against a deployment that still has a live lease on any lane. */\nexport class ReshardObjectStoreLiveError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ReshardObjectStoreLiveError\";\n }\n}\n\nexport interface ReshardObjectStoreOpts {\n objectStore: ObjectStore;\n /** The target shard count (M ≥ 1). */\n toShards: number;\n /** Wall-clock ms (caller-supplied — the substrate holds no ambient clock). Used only for the live-lease\n * gate and the transient lease `acquire` while writing each fresh lane. */\n now: number;\n /** The table's shard key field (`schema.ts` `.shardKey(field)`), or null when the table isn't sharded.\n * The reshard's ONLY schema dependency — injected so the core needs no schema loader (the CLI wires the\n * composed catalog's `getTableByNumber(n)?.shardKey`). */\n shardKeyFor: (tableNumber: number) => string | null;\n /** Mint a throwaway local `SqliteDocStore` (`:memory:`) for materialization + each fresh lane's commit —\n * injected so the substrate stays adapter-agnostic. */\n makeLocal: () => SqliteDocStore;\n}\n\nexport interface ReshardObjectStoreResult {\n fromShards: number;\n toShards: number;\n /** Docs whose owning lane changed (physically moved between lane logs). */\n movedDocs: number;\n /** Doc count landed in each target lane. */\n perLaneCounts: Record<string, number>;\n}\n\n/** The lane-id set a deployment of `numShards` uses. A deployment BORN single-shard uses the shipped\n * \"0\" lane; any multi-shard (or resharded) deployment uses the canonical `shardIdList`. */\nfunction laneIdsFor(numShards: number): ShardId[] {\n return numShards === 1 ? [\"0\"] : [...shardIdList(numShards)];\n}\n\nfunction pushTo<K, V>(map: Map<K, V[]>, key: K, value: V): void {\n const arr = map.get(key);\n if (arr) arr.push(value);\n else map.set(key, [value]);\n}\n\nexport async function reshardObjectStore(opts: ReshardObjectStoreOpts): Promise<ReshardObjectStoreResult> {\n const { objectStore: os, toShards, now, shardKeyFor, makeLocal } = opts;\n if (!Number.isInteger(toShards) || toShards < 1) {\n throw new RangeError(`reshardObjectStore: toShards must be a positive integer, got ${toShards}`);\n }\n\n const globals = await readGlobals(os);\n if (globals === null) {\n throw new Error(\"reshardObjectStore: no `globals` object — this bucket is not an object-storage deployment\");\n }\n const fromShards = globals.numShards;\n if (fromShards === toShards) {\n return { fromShards, toShards, movedDocs: 0, perLaneCounts: {} }; // already at M — no-op\n }\n\n const sourceLaneIds = laneIdsFor(fromShards);\n // The BUCKET lane prefixes to write. `numShards === 1` ALWAYS means the single \"0\" lane (whether the\n // deployment was born single-shard or resharded down to 1) — so `numShards` alone unambiguously tells\n // a booting node its layout. `numShards > 1` uses the canonical `shardIdList` prefixes (identity with\n // the engine's routing shardIds), matching `boot.ts`'s multi-shard writer.\n const targetLaneIds = laneIdsFor(toShards);\n\n // Map an engine routing shardId (`shardIdForKeyValue`'s output: \"default\"/\"s1\"/…) to the BUCKET lane\n // prefix a doc's current state is written under. M>1: identity (bucket prefix === engine shardId). M=1:\n // the single \"0\" lane owns everything (its lone `ObjectStoreDocStore` ignores the routing shardId).\n const toBucketLane = (engineShard: ShardId): ShardId => (toShards === 1 ? \"0\" : engineShard);\n\n // 1. GATE — no live lease on any source lane.\n for (const shard of sourceLaneIds) {\n const m = await readManifest(os, shard);\n if (m !== null && m.manifest.writerId !== \"\" && now <= Number(m.manifest.leaseExpiresAt)) {\n throw new ReshardObjectStoreLiveError(\n `reshardObjectStore: refusing — lane '${shard}' has a live lease held by '${m.manifest.writerId}' ` +\n `(expires at ${m.manifest.leaseExpiresAt}, now ${now}). Stop the deployment (scale to zero / let leases expire) first.`,\n );\n }\n }\n\n // 2. MATERIALIZE every source lane's current state into memory.\n const allDocs: DocumentLogEntry[] = [];\n const allIndex: IndexWrite[] = [];\n const oldLaneOfDoc = new Map<string, ShardId>();\n for (const shard of sourceLaneIds) {\n const local = makeLocal();\n await ObjectStoreDocStore.open({ objectStore: os, shard, local }); // materializes `local` (no claim)\n const state = await local.dumpCurrentState();\n for (const d of state.documents) {\n allDocs.push(d);\n oldLaneOfDoc.set(documentIdKey(d.id), shard);\n }\n for (const iw of state.indexUpdates) allIndex.push(iw);\n await local.close();\n }\n\n // 3. RE-PARTITION docs by their M-lane; route live index entries to the same lane as their doc.\n const laneToDocs = new Map<ShardId, DocumentLogEntry[]>();\n const newLaneOfDoc = new Map<string, ShardId>();\n let movedDocs = 0;\n for (const d of allDocs) {\n const shardKey = shardKeyFor(d.id.tableNumber);\n // `d.value` is non-null (dumpCurrentState excludes tombstones); `.value` is the doc's fields.\n const fields = d.value?.value as Record<string, unknown> | undefined;\n const engineShard: ShardId =\n shardKey !== null && fields !== undefined ? shardIdForKeyValue(fields[shardKey], toShards) : DEFAULT_SHARD;\n const newLane = toBucketLane(engineShard);\n const key = documentIdKey(d.id);\n newLaneOfDoc.set(key, newLane);\n if (oldLaneOfDoc.get(key) !== newLane) movedDocs++;\n pushTo(laneToDocs, newLane, d);\n }\n const laneToIndex = new Map<ShardId, IndexWrite[]>();\n for (const iw of allIndex) {\n if (iw.update.value.type !== \"NonClustered\") continue; // drop tombstone markers — fresh lanes start clean\n const lane = newLaneOfDoc.get(documentIdKey(iw.update.value.docId));\n if (lane === undefined) continue; // index entry with no live doc (defensive — shouldn't occur)\n pushTo(laneToIndex, lane, iw);\n }\n\n // 4. REWRITE — delete every object for every lane in source∪target, then write each target lane fresh.\n const lanesToClear = new Set<string>([...sourceLaneIds, ...targetLaneIds]);\n for (const shard of lanesToClear) {\n for (const k of await os.list(`s${shard}/`)) await os.delete(k);\n }\n const perLaneCounts: Record<string, number> = {};\n for (const shard of targetLaneIds) {\n const docs = laneToDocs.get(shard) ?? [];\n const index = laneToIndex.get(shard) ?? [];\n perLaneCounts[shard] = docs.length;\n const local = makeLocal();\n const store = await ObjectStoreDocStore.open({ objectStore: os, shard, local }); // fresh manifest (empty lane)\n await store.acquire({ writerId: `reshard-${shard}`, leaseTtlMs: 60_000, now });\n if (docs.length > 0 || index.length > 0) {\n await store.commitWriteBatch([{ documents: docs, indexUpdates: index }], shard);\n }\n await store.relinquish(); // clear the transient lease so a post-reshard node acquires immediately\n await local.close();\n }\n\n // 5. Set the new shard count LAST — the linearization point of the completed reshard.\n await writeGlobals(os, { deploymentId: globals.deploymentId, numShards: toShards });\n\n return { fromShards, toShards, movedDocs, perLaneCounts };\n}\n","/**\n * Fleet globals (Tier 3 Slice 4, Task 4.1, design record §5 layout / carried note I1) — a persist-once,\n * bucket-root `globals` object carrying the deployment's identity (`deploymentId`, `numShards`). A fresh\n * node materializing from the bucket must ADOPT this existing identity rather than mint a new one — a\n * re-minted `deploymentId` would flip every outbox client to `known:false`. Mirrors `manifest.ts`'s\n * create-only-via-`casPut` + JSON-encode/decode shape, but for a single bucket-wide key instead of a\n * per-shard one.\n */\nimport { isCasConflict, type ObjectStore } from \"@helipod/objectstore\";\n\n/** The deployment-wide identity every node adopts on open. `numShards` is recorded once at deployment\n * creation (Task 4.3 composes `numShards` independent per-shard lanes over the same bucket). */\nexport interface FleetGlobals {\n deploymentId: string;\n numShards: number;\n}\n\nconst GLOBALS_KEY = \"globals\";\n\n/** Read the bucket's fleet globals, or `null` if no node has created them yet. */\nexport async function readGlobals(os: ObjectStore): Promise<FleetGlobals | null> {\n const entry = await os.get(GLOBALS_KEY);\n if (entry === null) return null;\n return JSON.parse(new TextDecoder().decode(entry.body)) as FleetGlobals;\n}\n\n/** Create-only initialization of the bucket's fleet globals (`casPut` with `ifMatch: null`). Throws\n * `CasConflict` (see `isCasConflict`) if another node already wrote them — callers racing to initialize\n * the same bucket must treat that as \"someone else already did it\" and `readGlobals` instead (this is\n * exactly what `ensureGlobals` does below). */\nexport async function createGlobals(os: ObjectStore, globals: FleetGlobals): Promise<FleetGlobals> {\n await os.casPut(GLOBALS_KEY, new TextEncoder().encode(JSON.stringify(globals)), null);\n return globals;\n}\n\n/** Adopt-on-open: read the bucket's existing fleet globals and return them if present — NEVER overwrite\n * an already-established `deploymentId`/`numShards`. Only when the bucket has no globals yet does this\n * create them (create-only). Two nodes racing to initialize a fresh bucket both call this concurrently:\n * the `casPut` one-winner property means exactly one `createGlobals` lands; the loser's `CasConflict` is\n * caught here and resolved by re-reading — so both callers converge on the SAME winning globals. */\n/**\n * SINGLE-DEPLOYMENT-PER-BUCKET (whole-branch review, Finding 3, Task 4.5): Slice 4's object keyspace\n * is bare (`s{shard}/...`, `globals`) — NOT namespaced per deployment (design record §5's\n * `deployment/{id}/...` layout is deferred to Slice 5/6). Consequence: this function has no way to\n * tell \"a fresh deployment pointed at an already-occupied bucket\" apart from \"a node of the SAME\n * deployment reconnecting\" — a misconfigured second deployment aimed at an occupied bucket silently\n * ADOPTS the first's `deploymentId` (and, if it differs, its `numShards` too) rather than erroring.\n * This is a documented boundary, not a bug: fix it by giving each deployment its own bucket/prefix\n * until key-namespacing lands.\n */\nexport async function ensureGlobals(os: ObjectStore, globals: FleetGlobals): Promise<FleetGlobals> {\n const existing = await readGlobals(os);\n if (existing !== null) return existing;\n\n try {\n return await createGlobals(os, globals);\n } catch (e) {\n if (!isCasConflict(e)) throw e;\n // Lost the create race — someone else's globals won. Adopt theirs.\n const winner = await readGlobals(os);\n if (winner === null) {\n // Vanishingly unlikely (the winner would have to be deleted between the CasConflict and this\n // read) but surface loudly rather than silently return the loser's un-persisted globals.\n throw new Error(\"objectstore-substrate: globals CasConflict but re-read found no globals object\");\n }\n return winner;\n }\n}\n\n/**\n * OVERWRITE the bucket's globals (create if absent). Unlike `ensureGlobals` (adopt-if-present), this\n * REPLACES the value — used by the offline reshard tool to set the new `numShards` as the linearization\n * point of a completed reshard. Read the current etag (or null if absent) then `casPut` against it; a\n * `CasConflict` (a concurrent writer moved the globals) is re-tried a few times. Reshard runs against a\n * STOPPED deployment, so contention here is not expected — the retry is belt-and-braces.\n */\nexport async function writeGlobals(os: ObjectStore, globals: FleetGlobals): Promise<void> {\n const bytes = new TextEncoder().encode(JSON.stringify(globals));\n for (let attempt = 0; attempt < 5; attempt++) {\n const existing = await os.get(GLOBALS_KEY);\n try {\n await os.casPut(GLOBALS_KEY, bytes, existing === null ? null : existing.etag);\n return;\n } catch (e) {\n if (!isCasConflict(e)) throw e;\n // Lost a CAS race (unexpected for a stopped deployment) — re-read the fresh etag and retry.\n }\n }\n throw new Error(\"objectstore-substrate: writeGlobals exhausted retries on a moving globals object\");\n}\n","/* Helipod Enterprise. Licensed under the Helipod Commercial License — see ee/LICENSE. */\n/**\n * The object-storage writer's lease-heartbeat driver (Tier 3 Slice 6, Task 6.2) — a recurring\n * `Driver` (the same seam `@helipod/scheduler`/`@helipod/triggers`/`storageReaper`/\n * `receiptsReaper` run on) that keeps this node's `ObjectStoreDocStore` lease alive by calling\n * `store.heartbeat({now, leaseTtlMs})` on a fixed cadence, so a long-running writer never lets its\n * lease lapse just because nobody happened to commit recently.\n *\n * Mirrors `receiptsReaper`'s (`packages/receipts/src/reaper.ts`) single-timer shape — `start`\n * captures `ctx` and calls `wake()`, `wake()` fires the tick fire-and-forget and (on the SUCCESS/\n * transient-failure paths only, see below) re-arms via `.finally`-equivalent branching, `stop()`\n * sets a `stopped` guard before clearing the timer.\n *\n * THE CRITICAL DIFFERENCE FROM EVERY OTHER DRIVER IN THIS CODEBASE: a heartbeat's `FencedError`\n * does not mean \"retry later\" — it means this node has DEFINITIVELY LOST the lease (some other\n * writer's `acquire()` already bumped the manifest epoch past this instance's, per\n * `ObjectStoreDocStore.heartbeat`'s doc comment) and `store` is now `poisoned`: every further\n * `commitWriteBatch` on it will throw immediately anyway. Silently re-arming and retrying would\n * just keep failing forever while the caller believes the node is healthy. So on `FencedError` this\n * driver does NOT re-arm — it logs a loud, unambiguous fatal and calls `opts.onFenced?.(e)`, which\n * the CLI wires to trigger graceful node shutdown (a fenced writer MUST stop serving writes, never\n * keep accepting them against a store that will reject every one). Every OTHER error (a transient\n * object-store blip — a timeout, a 5xx, a network blip) is NOT a fence: the lease may still be alive\n * until `leaseExpiresAt` elapses, so this driver logs and re-arms, keeping up the renewal attempts —\n * exactly the resilience `receiptsReaper`'s \"one bad pass doesn't kill the reaper\" policy embodies,\n * just with the fence case carved out as the one terminal exception.\n */\nimport type { Driver, DriverContext } from \"@helipod/component\";\nimport { FencedError } from \"./fenced-error\";\n\n/** The minimal surface this driver needs from `ObjectStoreDocStore` — kept narrow (rather than\n * importing the whole class as a type) so a test fake doesn't need to construct a real store. */\nexport interface HeartbeatableStore {\n heartbeat(opts: { now: number; leaseTtlMs: number }): Promise<void>;\n}\n\nexport interface LeaseHeartbeatDriverOpts {\n /** The lease TTL to renew to on every successful heartbeat — must match the TTL `acquire()` was\n * called with (the driver does not itself acquire; it only renews an already-held lease). */\n leaseTtlMs: number;\n /** How often to attempt a renewal. Should be comfortably shorter than `leaseTtlMs` (the same\n * \"renew well before expiry\" margin every lease-holding system needs) — this driver does not\n * enforce a ratio; that's the caller's judgment call. */\n heartbeatMs: number;\n /** Called exactly once, synchronously from within `wake()`, the moment a heartbeat surfaces a\n * `FencedError` — i.e. this node has lost the lease. The CLI wires this to trigger graceful\n * shutdown (stop serving writes). Optional so a test can omit it. */\n onFenced?: (e: FencedError) => void;\n}\n\n/** Test/introspection seam mirroring `ReceiptsReaperDriver`'s `__tick`: runs one heartbeat pass and\n * awaits its real completion (propagating any error) rather than the timer path's swallow+log. */\nexport interface LeaseHeartbeatDriver extends Driver {\n __tick: () => Promise<void>;\n}\n\n/**\n * Build the lease-heartbeat driver for `store` (Tier 3 Slice 6, Task 6.2). See the module doc above\n * for the full fence-vs-transient-error policy.\n */\nexport function leaseHeartbeatDriver(store: HeartbeatableStore, opts: LeaseHeartbeatDriverOpts): LeaseHeartbeatDriver {\n const { leaseTtlMs, heartbeatMs, onFenced } = opts;\n // Fail fast rather than silently letting the lease lapse: a beat cadence at or slower than the TTL\n // means a single missed/delayed tick (GC pause, event-loop stall, a slow object-store round trip)\n // can let `leaseExpiresAt` pass with no renewal in flight — exactly the failure this driver exists\n // to prevent. Catch it at construction, not in production telemetry.\n if (heartbeatMs >= leaseTtlMs) {\n throw new Error(\n `objectstore-substrate: leaseHeartbeatDriver requires heartbeatMs (${heartbeatMs}) < leaseTtlMs (${leaseTtlMs}) — ` +\n `a heartbeat cadence at or slower than the lease TTL can let the lease lapse before a renewal ever lands`,\n );\n }\n let ctx: DriverContext;\n let timer: number | null = null;\n // Set the instant a fence is detected (BEFORE calling `onFenced`) OR `stop()` is called — guards\n // every re-entry point (`wake`, `armTimer`) against resurrecting a timer after either terminal\n // condition, mirroring `receiptsReaper`'s `stopped` guard.\n let stopped = false;\n\n async function tick(): Promise<void> {\n if (stopped) return;\n await store.heartbeat({ now: ctx.now(), leaseTtlMs });\n }\n\n function armTimer(): void {\n if (stopped) return;\n if (timer !== null) {\n ctx.clearTimer(timer);\n timer = null;\n }\n timer = ctx.setTimer(ctx.now() + heartbeatMs, wake);\n }\n\n // The timer entry point: fire-and-forget. Unlike `receiptsReaper`'s `wake()` (which\n // unconditionally re-arms in a `.finally`), this one branches on WHY the tick failed: a\n // `FencedError` is terminal (do not re-arm; fire `onFenced`), anything else is transient (log +\n // re-arm, same resilience policy as every other driver in this codebase).\n function wake(): void {\n if (stopped) return;\n tick().then(\n () => {\n armTimer();\n },\n (e: unknown) => {\n // A tick already in flight when `stop()` was called resolves AFTER `stopped` flipped: this is\n // ordinary teardown (graceful shutdown nulls the lease via `relinquish()` — Task 6.5's\n // bucket-clearing variant of `release()` — right after `stop()`, so the in-flight\n // `heartbeat()` legitimately throws `FencedError` — \"no held lease\"), NOT a real ownership\n // loss. Swallow it silently: firing `onFenced` or logging a FATAL here would be a false alarm\n // on every clean shutdown. Only an error surfacing while still running is real.\n if (stopped) return;\n if (e instanceof FencedError) {\n // Terminal: this node has lost the lease. Set `stopped` BEFORE calling `onFenced` (same\n // ordering discipline as `stop()` below) so nothing re-arms even if `onFenced` somehow\n // re-enters this driver synchronously.\n stopped = true;\n console.error(\n `[objectstore-substrate] FATAL: lease heartbeat fenced — this node no longer owns its shard's write lease ` +\n `and must stop serving writes. Cause: ${e.message}`,\n );\n // The fence path is terminal/shutdown — a throwing `onFenced` callback must not escape as an\n // unhandled rejection (this whole branch runs inside a fire-and-forget `.then` rejection\n // handler with no caller to catch it). Log the callback's own failure and swallow it; the\n // driver has already done its job (stopped, logged the fence) regardless of what the\n // callback does.\n try {\n onFenced?.(e);\n } catch (callbackError) {\n console.error(\"[objectstore-substrate] lease heartbeat: onFenced callback threw:\", callbackError);\n }\n return;\n }\n // Transient object-store blip — the lease may still be alive until `leaseExpiresAt`. Log and\n // keep trying on the normal cadence.\n console.error(\"[objectstore-substrate] lease heartbeat: renewal attempt failed (will retry):\", e);\n armTimer();\n },\n );\n }\n\n return {\n name: \"leaseHeartbeat\",\n start(c) {\n ctx = c;\n // Unlike `receiptsReaper`'s `start()` (which fires an immediate sweep via `wake()`), this\n // driver only ARMS the first timer — the lease was just freshly `acquire()`'d by the caller\n // before this driver starts, so an immediate renewal is redundant; the first heartbeat should\n // land on the normal `heartbeatMs` cadence.\n armTimer();\n },\n stop() {\n // Set BEFORE tearing anything down — see the `stopped` doc comment above.\n stopped = true;\n if (timer !== null) {\n ctx.clearTimer(timer);\n timer = null;\n }\n },\n // Test seam: runs one heartbeat pass and awaits its real completion, letting a `FencedError` (or\n // any other error) propagate to the caller — unlike `wake()`, used by the timer path, which\n // catches and branches instead. Does NOT itself set `stopped`/call `onFenced` on a fence; a test\n // exercising that behavior should drive it through the timer callback captured by a fake\n // `DriverContext.setTimer`, same as `receiptsReaper`'s own tests do for its policy.\n __tick: () => tick(),\n };\n}\n","/* Helipod Enterprise. Licensed under the Helipod Commercial License — see ee/LICENSE. */\n/**\n * The object-storage writer's periodic reclamation driver (Tier 3 Slice 7, Task 7.2) — a recurring\n * `Driver` (the same seam `@helipod/scheduler`/`@helipod/triggers`/`storageReaper`/\n * `receiptsReaper`/`leaseHeartbeatDriver` all run on) that calls `store.gc()` on a fixed cadence, so a\n * long-running writer's superseded segments/snapshots get reclaimed automatically instead of only on a\n * manual call.\n *\n * Mirrors `receiptsReaper`'s (`packages/receipts/src/reaper.ts`) single-timer shape — `start` arms the\n * first timer, `wake()` fires the tick fire-and-forget and unconditionally re-arms once it settles\n * (success or failure), `stop()` sets a `stopped` guard before clearing the timer.\n *\n * UNLIKE `leaseHeartbeatDriver` (`./heartbeat-driver.ts`), this driver has NO terminal/fence carve-out:\n * `gc()` is SELF-FENCING by construction (Task 7.1 — it re-reads the manifest and aborts as a harmless\n * no-op if this instance no longer owns the current epoch, or was never an owner at all), so a fenced\n * gc() call simply returns zero counts rather than throwing. Any error `gc()` DOES throw (a transient\n * object-store blip, or the `poisoned` guard's own throw) is therefore never a \"must stop serving\n * writes now\" signal the way a heartbeat's `FencedError` is — it's just \"this sweep didn't complete,\n * try again next cadence.\" So this driver swallows EVERY error (log + re-arm) and never signals\n * shutdown, exactly `receiptsReaper`'s \"one bad pass doesn't kill the reaper\" policy with no exception\n * carved out.\n */\nimport type { Driver, DriverContext } from \"@helipod/component\";\n\n/** The minimal surface this driver needs from `ObjectStoreDocStore` — kept narrow (rather than\n * importing the whole class as a type) so a test fake doesn't need to construct a real store, same\n * spirit as `heartbeat-driver.ts`'s `HeartbeatableStore`. */\nexport interface GcableStore {\n gc(): Promise<{ deletedSegments: number; deletedSnapshots: number }>;\n}\n\nexport interface GcDriverOpts {\n /** How often to run a gc() sweep. gc() is best-effort/idempotent and self-fencing, so there's no\n * correctness ratio to enforce here (unlike the heartbeat driver's heartbeatMs < leaseTtlMs) — pick\n * a cadence that trades reclamation latency against sweep cost (default ~60s, mirroring\n * `storageReaper`'s cadence — see the caller's default). */\n sweepMs: number;\n}\n\n/** Test/introspection seam mirroring `ReceiptsReaperDriver`'s `__tick`: runs one gc() pass and awaits\n * its real completion (propagating any error) rather than the timer path's swallow+log. */\nexport interface GcDriver extends Driver {\n __tick: () => Promise<{ deletedSegments: number; deletedSnapshots: number }>;\n}\n\n/**\n * Build the periodic gc-driver for `store` (Tier 3 Slice 7, Task 7.2). See the module doc above for\n * the full swallow-everything/no-fence-carve-out policy.\n */\nexport function gcDriver(store: GcableStore, opts: GcDriverOpts): GcDriver {\n const { sweepMs } = opts;\n let ctx: DriverContext;\n let timer: number | null = null;\n // Set by `stop()` BEFORE it tears down the timer — guards every re-entry point (`wake`, `tick`,\n // `armTimer`) against resurrecting a timer after `stop()`, mirroring `receiptsReaper`'s same guard.\n let stopped = false;\n\n async function tick(): Promise<{ deletedSegments: number; deletedSnapshots: number }> {\n if (stopped) return { deletedSegments: 0, deletedSnapshots: 0 };\n return store.gc();\n }\n\n function armTimer(): void {\n if (stopped) return;\n if (timer !== null) {\n ctx.clearTimer(timer);\n timer = null;\n }\n timer = ctx.setTimer(ctx.now() + sweepMs, wake);\n }\n\n // The timer entry point: fire-and-forget — swallow+log ANY error (gc() self-fences harmlessly; a\n // transient object-store error should just retry next sweep) rather than let it surface as an\n // unhandled rejection. Always re-arms afterward, success or failure, so one bad pass doesn't\n // silently kill the whole driver — never signals shutdown (contrast `leaseHeartbeatDriver`, which\n // owns fence→shutdown).\n function wake(): void {\n if (stopped) return;\n tick()\n .catch((e: unknown) => {\n console.error(\"[objectstore-substrate] gc-driver: sweep pass failed (will retry):\", e);\n })\n .finally(() => {\n armTimer();\n });\n }\n\n return {\n name: \"objectStoreGc\",\n start(c) {\n ctx = c;\n // Arm-only (no up-front sweep): gc reclaims superseded state, not fresh work — there is nothing\n // urgent to reclaim the instant a node boots, so the first sweep can wait for the normal cadence\n // (mirrors `leaseHeartbeatDriver`'s arm-only `start`, not `receiptsReaper`'s immediate `wake()`).\n armTimer();\n },\n stop() {\n // Set BEFORE tearing anything down — see the `stopped` doc comment above.\n stopped = true;\n if (timer !== null) {\n ctx.clearTimer(timer);\n timer = null;\n }\n },\n // Test seam: runs one gc() pass and awaits its real completion, letting any error propagate\n // (unlike `wake()`, used by the timer path, which swallows+logs) — see the interface doc above.\n __tick: () => tick(),\n };\n}\n","/* Helipod Enterprise. Licensed under the Helipod Commercial License — see ee/LICENSE. */\n/**\n * Cross-shard frontier (Tier 3 Slice 5, Task 5.1, design record §8) — the object-storage analog of\n * the shipped fleet `ReplicaTailer.readFrontier`'s `min(shard_leases.frontier_ts)` — but read\n * straight from each shard's own manifest object instead of a shared Postgres table, since object\n * storage has no cross-shard row to `min()` over server-side.\n *\n * `F = min(manifest.frontierTs)` over every shard in `shards` is the dense prefix the WHOLE fleet has\n * durably committed: a caller may treat state up to `F` as fully replicated everywhere. Mirrors the\n * fleet's partial-lease-set guard (`count < numShards` → not-ready): a shard whose manifest doesn't\n * exist yet (never `open()`'d/initialized) makes the WHOLE frontier `0n`, not merely excluded from the\n * min — a half-initialized fleet must not let the present shards' min fake readiness.\n *\n * Pure read helper: it does not track/assert monotonicity itself (unlike the fleet tailer's own\n * internal `readFrontier`, which owns a `lastF` cache) — a caller that needs the monotone-assertion\n * belt-and-braces should track its own last-observed value and compare across calls, mirroring\n * `ReplicaTailer`'s `lastF` if it wants that defense-in-depth.\n */\nimport type { ObjectStore } from \"@helipod/objectstore\";\nimport { readManifest } from \"./manifest\";\n\n/** `min(frontierTs)` over every shard's manifest — `0n` if any shard's manifest is absent (a\n * partial/not-yet-initialized shard set, the same F1×N hole guard the fleet tailer applies). A\n * single-shard replica passes `[shard]`; an empty `shards` array also returns `0n` (vacuously\n * \"nothing is ready\" rather than a vacuous `+Infinity`-derived value). */\nexport async function readGlobalFrontier(os: ObjectStore, shards: readonly string[]): Promise<bigint> {\n if (shards.length === 0) return 0n;\n let min: bigint | null = null;\n for (const shard of shards) {\n const entry = await readManifest(os, shard);\n if (entry === null) return 0n; // partial/absent manifest set — not ready\n const ts = BigInt(entry.manifest.frontierTs);\n if (min === null || ts < min) min = ts;\n }\n return min!;\n}\n","/* Helipod Enterprise. Licensed under the Helipod Commercial License — see ee/LICENSE. */\n/**\n * `ObjectStoreReplicaTailer` (Tier 3 Slice 5, Task 5.1, design record §7/§8) — the object-storage\n * analog of the shipped fleet `ReplicaTailer` (`ee/packages/fleet/src/replica-tailer.ts`): polls a\n * shard's manifest, pulls whatever segments/snapshot it references that this replica hasn't already\n * applied, materializes them onto a local `SqliteDocStore` via the SAME `write(..., \"Overwrite\")`\n * primitive `ObjectStoreDocStore.open()`'s `materializeTo` uses, derives an `AppliedInvalidation` from\n * the SAME batch it just applied (not a separate query), and advances its watermark only after the\n * caller's `onInvalidation` sink resolves — mirroring the fleet tailer's tick()/`AppliedInvalidation`/\n * watermark-after-sink shape (see that file's module doc for the fuller rationale this class carries\n * over unmodified: verbatim apply, advance-after-sink so a throwing/slow handler can't skip a range).\n *\n * DIVERGES from the fleet tailer in exactly the ways the substrate itself diverges from Postgres:\n * - No LISTEN/NOTIFY wake — object storage has no such primitive, so this tailer is PURELY poll-\n * driven (`start()` arms a `setInterval`; a caller can also drive `tick()` directly, e.g. in tests).\n * - No `batchSize` cap — a round always pulls everything between the last applied point and the\n * manifest's CURRENT frontier in one pass (a segment is already a bounded unit; there is no\n * unbounded single-transaction pull to guard against the way Postgres's per-row `load_documents`\n * needed capping).\n * - No density assertions — object storage's manifest CAS is itself the fence (Slice 2/4); a\n * replica can only ever observe a manifest state that was durably, atomically committed, so\n * there is no \"torn\" row shape to defend against the way Postgres's row-at-a-time replication\n * could theoretically skip a write.\n * - A SNAPSHOT FALLBACK the fleet tailer has no analog for: object storage's `gc()` can delete a\n * segment a lagging replica hasn't pulled yet (there is no `pg_advisory` retention the way a\n * logical-replication slot would give Postgres) — see `#materializeRound`'s doc for how a missing\n * segment falls back to a snapshot restore instead of failing outright. Task 5.2's watermark-aware\n * `gc()` is the production mitigation (never GC below a lagging consumer's watermark); this\n * fallback is the correctness backstop for the eventually-consistent object-store window regardless.\n *\n * BOOTSTRAP: unlike the fleet tailer (which bootstraps itself via a bounded catch-up loop inside\n * `start()`), this tailer does NOT bootstrap `local` — the caller is expected to have already\n * materialized it (typically via `ObjectStoreDocStore.open({objectStore, shard, local})`, or by\n * simply handing over a bare, empty `SqliteDocStore` and letting this tailer's OWN first `tick()`\n * perform the full catch-up, since `#materializeRound`'s snapshot-fallback + tail-pull IS the same\n * algorithm `materializeTo` runs). The tailer discovers where `local` actually stands lazily, on its\n * first `tick()`, by reading `local.maxTimestamp()` — see `#ensureInitialized`'s doc for why this must\n * happen lazily (on first tick, not in the constructor) and how it seeds `appliedSeqno` safely. Either\n * way, the caller MUST have already run `local.setupSchema()` before handing it over (`open()`'s own\n * first step) — this tailer never creates the schema itself, only applies rows into it.\n */\nimport type { ObjectStore } from \"@helipod/objectstore\";\nimport type { DocumentLogEntry, IndexWrite, InternalDocumentId } from \"@helipod/docstore\";\nimport type { SqliteDocStore } from \"@helipod/docstore-sqlite\";\nimport { encodeStorageTableId, internalIdToHex } from \"@helipod/id-codec\";\nimport { readManifest, type Manifest } from \"./manifest\";\nimport { readSnapshot } from \"./snapshot\";\nimport { decodeSegment } from \"./segment\";\nimport { segmentKey } from \"./object-doc-store\";\nimport { applySnapshotState } from \"./apply-snapshot\";\n\n/** Bounds the missing-segment retry loop in `#materializeRound` (Finding 4, whole-branch review):\n * on the shipped strongly-consistent adapters (fs/S3) a raced-GC restart converges in one or two\n * iterations; this cap turns a hypothetically eventually-consistent store's non-convergence into a\n * loud, clear error instead of an infinite spin. The normal convergence path is unaffected. */\nconst MAX_MISSING_SEGMENT_RETRIES = 8;\n\n/** Mirrors the fleet tailer's `AppliedInvalidation` shape byte-for-byte (see that file's doc for why\n * this is a deliberate parallel type, not a shared import — the substrate must not depend on\n * `@helipod/fleet`). `newMaxTs` is the ts THROUGH which this round applied — the manifest's\n * `frontierTs` at the moment this round finished (see `#tickOnce`'s doc for why that, not a\n * row-derived max, is the authoritative value here). */\nexport interface AppliedInvalidation {\n newMaxTs: bigint;\n /** DISTINCT storage-encoded table ids touched, derived from BOTH the applied documents' own ids\n * AND any applied index write whose value is a live (`NonClustered`) entry — a `Deleted` index\n * entry carries no docId to derive a table from, so it contributes nothing here (its owning\n * document's own entry in the SAME round already does). */\n writtenTables: string[];\n /** Raw written index keys — point invalidation input, NOT yet point ranges. */\n writtenKeys: Array<{ indexId: string; key: Uint8Array }>;\n /** DISTINCT `(tableId, internalId)` pairs written this round, deduped from the applied\n * `DocumentLogEntry` rows (one entry per doc regardless of how many revisions accompanied it).\n * Point invalidation input for the DOCUMENT keyspace, NOT yet point ranges — same split as\n * `writtenKeys` above. */\n writtenDocs: Array<{ tableId: string; internalId: Uint8Array }>;\n}\n\nexport interface ObjectStoreReplicaTailerOptions {\n objectStore: ObjectStore;\n shard: string;\n /** The replica's materialize target. The CALLER is responsible for it existing (typically via\n * `ObjectStoreDocStore.open()`'s bootstrap, or a bare fresh `SqliteDocStore` — see the class doc). */\n local: SqliteDocStore;\n /** Invoked once per non-empty applied round, AFTER the round has already been written to `local`.\n * The watermark only advances after this resolves — a throwing/slow handler must not cause a\n * round to be silently skipped on the next tick. */\n onInvalidation: (inv: AppliedInvalidation) => Promise<void>;\n /** Wall-clock poll interval for `start()`, in ms. Default 1000. */\n pollMs?: number;\n}\n\ninterface Waiter {\n ts: bigint;\n resolve: () => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\n/** Poll-driven object-storage replica tailer — see the module doc for the full contract. */\nexport class ObjectStoreReplicaTailer {\n private readonly objectStore: ObjectStore;\n private readonly shard: string;\n private readonly local: SqliteDocStore;\n private readonly onInvalidation: (inv: AppliedInvalidation) => Promise<void>;\n private readonly pollMs: number;\n\n /** The highest segment seqno this tailer has itself applied (or correlated `local` to — see\n * `#ensureInitialized`/the \"nothing new\" opportunistic-seed note in `#tickOnce`). `-1` means\n * \"not yet correlated to any manifest state\" — the sentinel that makes `#materializeRound`'s\n * snapshot-fallback check and tail-pull loop naturally perform a FULL catch-up (every segment,\n * or the newest snapshot + its tail) the first time this tailer actually has new work to do,\n * which is the correct, safe behavior for a `local` this tailer hasn't yet correlated to a\n * known-caught-up point (re-applying already-present rows via `write(..., \"Overwrite\")` is an\n * idempotent no-op on `local`'s actual state; the only cost is a possibly-oversized first\n * invalidation batch, never a missed one). */\n private appliedSeqnoValue = -1;\n /** The ts through which `local` is known to be caught up. Lazily seeded from\n * `local.maxTimestamp()` on the first `tick()` — see `#ensureInitialized`. */\n private appliedMaxTsValue = 0n;\n private initialized = false;\n\n private timer: ReturnType<typeof setInterval> | undefined;\n /** Reentrancy guard — a manual `tick()` call racing the `start()`-armed poll timer must not run\n * two overlapping apply rounds against the same `local`/cursor state. */\n private draining = false;\n private readonly waiters = new Set<Waiter>();\n\n constructor(opts: ObjectStoreReplicaTailerOptions) {\n this.objectStore = opts.objectStore;\n this.shard = opts.shard;\n this.local = opts.local;\n this.onInvalidation = opts.onInvalidation;\n this.pollMs = opts.pollMs ?? 1000;\n }\n\n get appliedSeqno(): number {\n return this.appliedSeqnoValue;\n }\n\n get appliedMaxTs(): bigint {\n return this.appliedMaxTsValue;\n }\n\n /** Arms a `setInterval(tick, pollMs)`. Tick errors are swallowed (logged) — mirrors the fleet\n * tailer's fire-and-forget poll posture: a transient failure must not crash the caller's process,\n * and the NEXT tick retries from the same (unadvanced) watermark regardless. No-op if already\n * started. */\n start(): void {\n if (this.timer !== undefined) return;\n this.timer = setInterval(() => {\n void this.tick().catch((e: unknown) => {\n console.error(`objectstore-substrate: replica tailer tick failed for shard '${this.shard}'`, e);\n });\n }, this.pollMs);\n }\n\n stop(): void {\n if (this.timer !== undefined) {\n clearInterval(this.timer);\n this.timer = undefined;\n }\n }\n\n /** Resolves when `appliedMaxTs >= ts` (immediately if already true). Poll-driven: nothing here\n * itself advances the watermark — either `start()` must be armed, or the caller must drive\n * `tick()` itself (e.g. in a test loop). Rejects on `timeoutMs` elapsing first. */\n waitFor(ts: bigint, timeoutMs: number): Promise<void> {\n if (this.appliedMaxTsValue >= ts) return Promise.resolve();\n return new Promise<void>((resolve, reject) => {\n const waiter: Waiter = {\n ts,\n resolve: () => {\n clearTimeout(waiter.timer);\n this.waiters.delete(waiter);\n resolve();\n },\n timer: setTimeout(() => {\n this.waiters.delete(waiter);\n reject(\n new Error(\n `ObjectStoreReplicaTailer.waitFor timed out after ${timeoutMs}ms waiting for ts >= ${ts} ` +\n `(currently at ${this.appliedMaxTsValue})`,\n ),\n );\n }, timeoutMs),\n };\n this.waiters.add(waiter);\n });\n }\n\n #wakeSatisfiedWaiters(): void {\n for (const w of [...this.waiters]) {\n if (this.appliedMaxTsValue >= w.ts) w.resolve();\n }\n }\n\n /** One poll round: returns `true` if anything was applied (or the watermark otherwise advanced),\n * `false` if there was nothing new. Reentrancy-guarded (see `draining`'s doc) — a call landing\n * while another is already in flight is a no-op `false`, not queued; the next timer tick (or the\n * caller's own retry) picks up whatever was missed. */\n async tick(): Promise<boolean> {\n if (this.draining) return false;\n this.draining = true;\n try {\n return await this.#tickOnce();\n } finally {\n this.draining = false;\n }\n }\n\n /** Seeds `appliedMaxTsValue` from `local.maxTimestamp()` on the FIRST tick only — this must be\n * lazy (not done in the constructor) because `DocStore.maxTimestamp()` is async and constructors\n * can't await. Deliberately does NOT try to also seed `appliedSeqnoValue` here: at this point we\n * have no manifest read yet to correlate a ts to a seqno cursor against, so `appliedSeqnoValue`\n * stays at its conservative `-1` sentinel — `#tickOnce`'s \"nothing new\" branch opportunistically\n * seeds it instead, the first time it reads a manifest whose `frontierTs` this exact\n * `appliedMaxTsValue` already covers (see that branch's doc for why that correlation is safe). */\n async #ensureInitialized(): Promise<void> {\n if (this.initialized) return;\n this.appliedMaxTsValue = await this.local.maxTimestamp();\n this.initialized = true;\n }\n\n async #tickOnce(): Promise<boolean> {\n await this.#ensureInitialized();\n\n const fresh = await readManifest(this.objectStore, this.shard);\n if (fresh === null) return false; // shard never initialized (no manifest yet) — nothing to tail\n const manifest = fresh.manifest;\n const frontierTs = BigInt(manifest.frontierTs);\n\n if (frontierTs <= this.appliedMaxTsValue) {\n // Nothing new since our last apply (or since `local`'s own external bootstrap, on a first\n // tick against an already-caught-up `local`). Opportunistically correlate our seqno cursor to\n // THIS manifest read if we haven't yet: we've just confirmed `local` already reflects\n // everything through `frontierTs`, i.e. through this exact manifest's `nextSeqno - 1` — so a\n // LATER tick that finds genuinely new segments only needs to pull the ones past this point,\n // not redo the whole history. Never move it BACKWARD (a later, larger correlation always wins).\n if (this.appliedSeqnoValue < manifest.nextSeqno - 1) this.appliedSeqnoValue = manifest.nextSeqno - 1;\n return false;\n }\n\n const round = await this.#materializeRound(manifest);\n // Authoritative ts: the FINAL manifest revision `#materializeRound` actually caught up to (it may\n // have re-read a fresher manifest mid-round on a GC race) — NOT a max derived from the applied\n // rows themselves. Mirrors `object-doc-store.ts`'s own documented divergence note: a snapshot's\n // dump excludes tombstones, so if the round's boundary commit was a delete with no tail beyond\n // it, a row-derived max could trail the true frontier. Reading it straight from the manifest we\n // just caught up to sidesteps that entirely and keeps `appliedMaxTs` — the value `tick()`'s own\n // \"nothing new\" check and `waitFor()` both trust — always exactly accurate.\n const newMaxTs = BigInt(round.manifest.frontierTs);\n\n if (round.documents.length === 0 && round.indexUpdates.length === 0) {\n // The manifest's frontier advanced but this round applied nothing new (can only happen if a\n // GC-race refresh mid-round landed us exactly back at a fully-caught-up state) — advance both\n // cursors without an invalidation call (nothing to invalidate). Safe to advance appliedSeqno\n // here too: an empty round means `#materializeRound` didn't skip delivering anything to a sink.\n this.appliedSeqnoValue = round.appliedSeqno;\n this.appliedMaxTsValue = newMaxTs;\n this.#wakeSatisfiedWaiters();\n return true;\n }\n\n const inv = this.#buildInvalidation(round.documents, round.indexUpdates, newMaxTs);\n await this.onInvalidation(inv);\n // Advance BOTH cursors together, ONLY after onInvalidation resolves — mirrors the fleet tailer's\n // single-`wm`-advanced-after-sink discipline. If onInvalidation throws, this line never runs and\n // BOTH appliedSeqnoValue/appliedMaxTsValue stay put: the next tick re-reads the same manifest\n // state, `#materializeRound` re-pulls the identical segments/snapshot from the still-unadvanced\n // appliedSeqnoValue, re-applies them via idempotent `write(..., \"Overwrite\")`, and rebuilds the\n // SAME invalidation to redeliver — no missed range, at the cost of a redundant (harmless) re-pull.\n this.appliedSeqnoValue = round.appliedSeqno;\n this.appliedMaxTsValue = newMaxTs;\n this.#wakeSatisfiedWaiters();\n return true;\n }\n\n /**\n * Pulls + applies everything between `this.appliedSeqnoValue` and `manifest`'s frontier, mutating\n * `local` as it goes (idempotent — a segment/snapshot already reflected in `local` is a safe\n * no-op re-`Overwrite`), and returns every row applied THIS round (for `#buildInvalidation`), the\n * final manifest revision actually reached, and the seqno cursor the round advanced to.\n *\n * Deliberately does NOT mutate `this.appliedSeqnoValue` — only `#tickOnce` may do that, and only\n * AFTER its `onInvalidation` sink resolves (see that method's doc). This method runs entirely\n * against a LOCAL `appliedSeqno` variable seeded from the instance field, so a caller that discards\n * this round's result (sink threw) leaves the instance cursor exactly where it started, and a retry\n * re-runs this same method from the same starting point — re-applying idempotently and rebuilding\n * the identical returned batch.\n *\n * Mirrors `object-doc-store.ts`'s `materializeTo` — same snapshot-restore-then-replay-tail shape —\n * with one addition `materializeTo` doesn't need: a MISSING segment (`objectStore.get` returns\n * `null`) means a lagging replica lost a race against `gc()`, which only ever deletes a segment\n * once a NEWER snapshot supersedes it. The safe recovery is therefore always available: re-read the\n * manifest (it must now reference a snapshot covering the missing seqno, or GC could not have\n * deleted it) and restart the round against that fresher manifest — looping rather than recursing,\n * so partial progress already applied (and already reflected in the local `appliedSeqno`) is never\n * discarded, and the accumulated `documents`/`indexUpdates` carry across the restart intact.\n */\n async #materializeRound(\n initialManifest: Manifest,\n ): Promise<{ documents: DocumentLogEntry[]; indexUpdates: IndexWrite[]; manifest: Manifest; appliedSeqno: number }> {\n const documents: DocumentLogEntry[] = [];\n const indexUpdates: IndexWrite[] = [];\n let manifest = initialManifest;\n let appliedSeqno = this.appliedSeqnoValue;\n let missingSegmentRetries = 0;\n\n for (;;) {\n // `>` (not `>=`, Finding 3, whole-branch review): when `snapshotSegBase === appliedSeqno` the\n // replica has already applied THROUGH the snapshot base — re-restoring the whole snapshot would\n // be a redundant (if idempotent) full restore instead of just replaying the tail. Also narrows\n // Finding 1's blast radius to rounds that actually jump the replica forward past the snapshot.\n if (\n manifest.snapshotTs !== undefined &&\n manifest.snapshotSegBase !== undefined &&\n manifest.snapshotSegBase > appliedSeqno\n ) {\n const snap = await readSnapshot(this.objectStore, this.shard, manifest.snapshotTs);\n if (snap === null) {\n throw new Error(\n `objectstore-substrate: replica tailer for shard '${this.shard}' — missing snapshot ` +\n `'${manifest.snapshotTs}' referenced by the manifest (torn state)`,\n );\n }\n\n // Finding 1 (CRITICAL, whole-branch review): `write(..., \"Overwrite\")` is an OVERLAY\n // (INSERT OR REPLACE), never a replace-all, and `dumpCurrentState` (the snapshot's own\n // source) EXCLUDES tombstones — so the snapshot alone cannot express \"this doc was deleted\n // in the range this restore jumps over.\" Left unhandled, a doc the replica still has LIVE\n // that the snapshot silently dropped stays phantom-live on the replica forever, and\n // `#buildInvalidation` (deriving `writtenDocs` from `snap.documents`, which never mentions\n // it) would emit no invalidation for it either.\n //\n // Fix extracted into the shared `applySnapshotState` helper (Slice 5 re-review) — see its\n // doc for the full diff+tombstone rationale; `object-doc-store.ts`'s `materializeTo` (the\n // writer's own catch-up path) calls the SAME helper for the identical hazard.\n const snapFrontierTs = BigInt(snap.frontierTs);\n const { deletedDocs } = await applySnapshotState(this.local, snap, snapFrontierTs);\n if (deletedDocs.length > 0) {\n // Fold the tombstones into this round's `documents` too — NOT just applied to `local` —\n // so `#buildInvalidation`'s `writtenDocs` covers the deletion and a `db.get(id)`\n // subscription on the deleted doc actually re-runs.\n documents.push(...deletedDocs);\n }\n documents.push(...snap.documents);\n indexUpdates.push(...snap.indexUpdates);\n appliedSeqno = manifest.snapshotSegBase;\n }\n\n let missedSegment = false;\n for (const seqno of manifest.segments) {\n if (seqno <= appliedSeqno) continue; // already applied (or covered by the snapshot just restored)\n const entry = await this.objectStore.get(segmentKey(this.shard, seqno));\n if (entry === null) {\n // Raced GC — fall back to the snapshot path against a FRESH manifest read and restart.\n // Bounded (Finding 4, whole-branch review): the shipped strongly-consistent adapters\n // (fs/S3) converge in one or two restarts; this cap turns a hypothetically\n // eventually-consistent store's non-convergence into a loud error, not an infinite spin.\n if (++missingSegmentRetries > MAX_MISSING_SEGMENT_RETRIES) {\n throw new Error(\n `objectstore-substrate: replica tailer for shard '${this.shard}' — missing segment ` +\n `'${segmentKey(this.shard, seqno)}' did not resolve via snapshot fallback after ` +\n `${MAX_MISSING_SEGMENT_RETRIES} retries`,\n );\n }\n const refreshed = await readManifest(this.objectStore, this.shard);\n if (refreshed === null) {\n throw new Error(\n `objectstore-substrate: replica tailer for shard '${this.shard}' — manifest disappeared mid-round`,\n );\n }\n manifest = refreshed.manifest;\n missedSegment = true;\n break; // restart the outer loop against `manifest` (re-checks the snapshot-fallback condition)\n }\n const payload = decodeSegment(entry.body);\n await this.local.write(payload.documents, payload.indexUpdates, \"Overwrite\");\n documents.push(...payload.documents);\n indexUpdates.push(...payload.indexUpdates);\n appliedSeqno = seqno;\n }\n if (!missedSegment) return { documents, indexUpdates, manifest, appliedSeqno };\n }\n }\n\n /** `${tableId}|${internalIdHex}` — the doc-identity key `#buildInvalidation`'s dedupe uses to\n * distinguish applied documents by `(tableId, internalId)`. (The snapshot-restore diff this\n * method used to also serve, Finding 1, now lives in the shared `applySnapshotState` helper,\n * keyed by `@helipod/id-codec`'s own `documentIdKey` — see `apply-snapshot.ts`.) */\n #docKey(id: InternalDocumentId): string {\n return `${encodeStorageTableId(id.tableNumber)}|${internalIdToHex(id.internalId)}`;\n }\n\n /** Builds the round's `AppliedInvalidation` — mirrors the fleet tailer's derivation\n * (`replica-tailer.ts` ~:476-488): `writtenDocs` DISTINCT-deduped by `(tableId, internalId)` from\n * the applied documents, `writtenKeys` straight from the applied index writes, `writtenTables`\n * DISTINCT across both sources. */\n #buildInvalidation(\n documents: readonly DocumentLogEntry[],\n indexUpdates: readonly IndexWrite[],\n newMaxTs: bigint,\n ): AppliedInvalidation {\n const tables = new Set<string>();\n const seenDocs = new Set<string>();\n const writtenDocs: Array<{ tableId: string; internalId: Uint8Array }> = [];\n for (const d of documents) {\n const tableId = encodeStorageTableId(d.id.tableNumber);\n tables.add(tableId);\n const dedupeKey = this.#docKey(d.id);\n if (seenDocs.has(dedupeKey)) continue;\n seenDocs.add(dedupeKey);\n writtenDocs.push({ tableId, internalId: d.id.internalId });\n }\n\n const writtenKeys = indexUpdates.map((w) => ({ indexId: w.update.indexId, key: w.update.key }));\n for (const w of indexUpdates) {\n if (w.update.value.type === \"NonClustered\") tables.add(encodeStorageTableId(w.update.value.docId.tableNumber));\n }\n\n return { newMaxTs, writtenTables: [...tables], writtenKeys, writtenDocs };\n }\n}\n","/* Helipod Enterprise. Licensed under the Helipod Commercial License — see ee/LICENSE. */\n/**\n * `startReplicaReactiveTailer` (Tier 3 Slice 8, Task 8.1, design record §7/§8) — extracts the\n * reactive-tailer wiring the Slice-5 cross-node E2E (`test/cross-node-reactivity.e2e.test.ts`) built\n * inline into a reusable production helper: a caller (typically `helipod serve --replica`'s boot\n * path, Task 8.2) hands over a REPLICA-side runtime + its materialized `local` store + the bucket,\n * and this helper drives the runtime's reactive fan-out from the writer's committed segments, and\n * publishes the replica's consumer watermark (so the writer's `gcDriver`, Slice 7, never reclaims a\n * segment this replica hasn't tailed yet).\n *\n * The sink mirrors the Slice-5 E2E's `invalidationSink` — itself mirroring the shipped fleet\n * `invalidationSink` (`ee/packages/fleet/src/node.ts` ~:1358) — byte-for-byte:\n * 1. `runtime.observeTimestamp(inv.newMaxTs)` — BEFORE fanning ranges into the sync handler, so the\n * query oracle's re-run actually reads the newly-applied rows.\n * 2. Convert the round's raw `writtenKeys`/`writtenDocs` into point ranges via the canonical\n * `keyToPointRange`/`docKeyToPointRange` (`@helipod/id-codec`, Task 8.1's other half of this\n * extraction).\n * 3. `await runtime.handler.notifyWrites(...)` — the live-subscription re-run/re-push path.\n * 4. `runtime.notifyExternalCommit(...)` — wakes any driver `onCommit` listener (e.g. a composed\n * component's own reactive hook) on this replica process.\n * The sink is REACTIVITY ONLY — it does NOT publish the consumer watermark. `ObjectStoreReplicaTailer`\n * advances `appliedSeqno` only AFTER `onInvalidation` resolves (the Slice-5 redelivery-safety\n * discipline: if the sink throws, the next tick must redeliver the identical round), so reading\n * `tailer.appliedSeqno` FROM INSIDE the sink observes the PRE-advance value (`-1` on the very first\n * round) — one round stale, and permanently stuck-stale for a replica that goes idle after its last\n * commit (no further tick ever republishes the true position). Under-reporting is GC-safe (a stale-low\n * watermark only makes `gc()` over-retain), but stuck-stale defeats the watermark's whole purpose for a\n * mostly-idle replica. Instead, THIS HELPER owns its own poll loop (`#pump`, below): each pass calls\n * `tailer.tick()` (which drives the sink above AND THEN advances `appliedSeqno`), and only afterward —\n * if `appliedSeqno` actually advanced — publishes the accurate POST-advance value. A `__pump()` test\n * seam (mirroring `gc-driver.ts`'s `__tick`) drives one round deterministically, awaiting completion\n * and propagating errors, for tests that don't want to wait on a real timer.\n *\n * `ReplicaReactiveRuntime` is a deliberately NARROW structural type — only the three members this\n * sink actually calls — rather than an import of `@helipod/runtime-embedded`'s `EmbeddedRuntime`.\n * `objectstore-substrate` must not take a dependency on `runtime-embedded` just for this helper's\n * type signature; the real `EmbeddedRuntime` already satisfies this shape structurally, so the CLI's\n * boot path (Task 8.2) can pass one straight through with no adapter.\n */\nimport type { ObjectStore } from \"@helipod/objectstore\";\nimport type { SqliteDocStore } from \"@helipod/docstore-sqlite\";\nimport type { SerializedKeyRange } from \"@helipod/index-key-codec\";\nimport { keyToPointRange, docKeyToPointRange } from \"@helipod/id-codec\";\nimport { ObjectStoreReplicaTailer, type AppliedInvalidation } from \"./replica-tailer\";\nimport { publishConsumerWatermark } from \"./consumers\";\n\n/** The shape a runtime's reactive tier must expose for this sink to drive it — satisfied\n * structurally by `@helipod/runtime-embedded`'s `EmbeddedRuntime` (see module doc). */\nexport interface ReplicaReactiveRuntime {\n /** Advances the runtime's own observed timestamp so a re-run oracle sees rows through `ts`. */\n observeTimestamp(ts: bigint): void;\n handler: {\n /** Re-runs/re-pushes every live subscription whose recorded read set intersects `ranges`. */\n notifyWrites(inv: { tables: string[]; ranges: SerializedKeyRange[]; commitTs: number }): Promise<void>;\n };\n /** Wakes any driver `onCommit` listener composed into this runtime. */\n notifyExternalCommit(inv: { tables: string[]; ranges: SerializedKeyRange[]; commitTs: number }): void;\n}\n\nexport interface StartReplicaReactiveTailerOptions {\n /** The replica-side runtime whose reactive tier this helper drives. */\n runtime: ReplicaReactiveRuntime;\n objectStore: ObjectStore;\n shard: string;\n /** The SAME `local` the replica's runtime/store reads from — the tailer applies the writer's\n * segments directly onto it. */\n local: SqliteDocStore;\n /** This replica's consumer-watermark identity (shard-scoped key `s{shard}/consumers/{id}`, Slice\n * 5 Task 5.2) — a per-process id the caller mints. */\n consumerId: string;\n /** Wall-clock poll interval, ms. Default (`ObjectStoreReplicaTailer`'s own default): 1000. */\n pollMs?: number;\n}\n\n/** Returned by `startReplicaReactiveTailer` — see that function's doc. */\nexport interface ReplicaReactiveTailerHandle {\n /** Halts this helper's own poll loop (idempotent, no re-arm after). Does NOT deregister the\n * consumer watermark (`removeConsumer` is a boot-path/shutdown concern for the caller, not this\n * helper — see Task 8.2). Defensively also calls `tailer.stop()`, though this helper never calls\n * `tailer.start()` itself (no self-timer of the tailer's own to clear). */\n stop(): Promise<void>;\n /** Test/introspection seam mirroring `gc-driver.ts`'s `__tick`: runs exactly one\n * `tailer.tick()` + the conditional watermark publish, awaiting real completion and propagating\n * any error (unlike the timer path, which swallows + logs + re-arms). Lets a test drive\n * deterministic rounds without waiting on a real timer. */\n __pump(): Promise<void>;\n}\n\n/**\n * Builds an `ObjectStoreReplicaTailer` over `opts.local`/`opts.objectStore`/`opts.shard` whose\n * `onInvalidation` sink drives `opts.runtime`'s reactive fan-out, then arms this helper's OWN poll\n * loop (see the module doc for why the watermark publish must live HERE, post-`tick()`, rather than\n * inside the sink). `stop()` halts the loop.\n */\nexport function startReplicaReactiveTailer(opts: StartReplicaReactiveTailerOptions): ReplicaReactiveTailerHandle {\n const { runtime, objectStore, shard, local, consumerId, pollMs } = opts;\n const interval = pollMs ?? 1000;\n\n const onInvalidation = async (inv: AppliedInvalidation): Promise<void> => {\n runtime.observeTimestamp(inv.newMaxTs);\n const ranges: SerializedKeyRange[] = [\n ...inv.writtenKeys.map((k) => keyToPointRange(k.indexId, k.key)),\n ...inv.writtenDocs.map((d) => docKeyToPointRange(d.tableId, d.internalId)),\n ];\n const commitTs = Number(inv.newMaxTs);\n await runtime.handler.notifyWrites({ tables: inv.writtenTables, ranges, commitTs });\n runtime.notifyExternalCommit({ tables: inv.writtenTables, ranges, commitTs });\n };\n\n const tailer = new ObjectStoreReplicaTailer({ objectStore, shard, local, onInvalidation, pollMs });\n\n // The last `appliedSeqno` this helper has itself published, so an idle/no-op tick (nothing new)\n // never issues a redundant bucket write. `-1` matches the tailer's own \"not yet correlated\"\n // sentinel, so a tick that only opportunistically correlates the cursor (see\n // `ObjectStoreReplicaTailer#tickOnce`'s \"nothing new\" branch) without genuinely advancing past `-1`\n // still correctly skips the publish.\n let lastPublishedSeqno = -1;\n let timer: ReturnType<typeof setTimeout> | undefined;\n // Set by `stop()` BEFORE clearing the timer — guards every re-entry point against resurrecting a\n // timer after `stop()`, mirroring `gc-driver.ts`'s same `stopped` guard.\n let stopped = false;\n // The round currently in flight (if any), tracked so `stop()` can AWAIT it before returning — see\n // `stop()`. Without this, a `pump()` already past its top guard (mid-`tailer.tick()` or mid-publish)\n // could complete AFTER `stop()` returns and the boot layer's `removeConsumer()` deletes this\n // consumer's watermark — RE-CREATING it with a never-reused per-process consumerId, permanently\n // pinning the writer's gc floor (a storage leak on every routine replica restart).\n let inflight: Promise<void> | undefined;\n\n // One round: tick the tailer (drives the reactivity sink AND advances `appliedSeqno`), then publish\n // the watermark ONLY if it actually advanced since our last publish — the accurate POST-advance\n // value, never the stale pre-tick one.\n async function pump(): Promise<void> {\n // The single guarded chokepoint for BOTH drive paths (the timer's `wake()` and the `__pump()` test\n // seam): once `stop()` has run, a round already in flight or a manual `__pump()` is a no-op — the\n // replica has been halted and must not apply/publish further.\n if (stopped) return;\n await tailer.tick();\n // Re-check AFTER the tick's awaits: if `stop()` landed while we were ticking, skip the publish\n // entirely (nothing to resurrect). The stop-during-publish window is closed separately by\n // `stop()` awaiting `inflight`.\n if (stopped) return;\n if (tailer.appliedSeqno !== lastPublishedSeqno) {\n await publishConsumerWatermark(objectStore, shard, consumerId, { appliedSeqno: tailer.appliedSeqno });\n lastPublishedSeqno = tailer.appliedSeqno;\n }\n }\n\n function armTimer(): void {\n if (stopped) return;\n timer = setTimeout(wake, interval);\n }\n\n // The timer entry point: fire-and-forget — swallow + log any error (a transient object-store blip\n // shouldn't kill the replica's tailing loop) and always re-arm afterward, success or failure,\n // mirroring `gc-driver.ts`'s `wake()`.\n function wake(): void {\n if (stopped) return;\n const round = pump().catch((e: unknown) => {\n console.error(`objectstore-substrate: replica reactive tailer wiring pump failed for shard '${shard}'`, e);\n });\n inflight = round;\n void round.finally(() => {\n if (inflight === round) inflight = undefined;\n armTimer();\n });\n }\n\n armTimer();\n\n return {\n async stop(): Promise<void> {\n // Set BEFORE tearing anything down — see the `stopped` doc comment above.\n stopped = true;\n if (timer !== undefined) {\n clearTimeout(timer);\n timer = undefined;\n }\n // Await any round already in flight (incl. its watermark publish) so the caller's shutdown\n // `removeConsumer()` runs strictly AFTER the last possible publish — otherwise a mid-publish\n // round would resurrect the watermark just deleted (Slice-8 whole-branch review). `stopped` is\n // already set, so no NEW round can start; this only drains the one that may already be running.\n await inflight;\n tailer.stop();\n },\n __pump: pump,\n };\n}\n"],"mappings":"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","names":["isCasConflict","isCasConflict","tsList","DEFAULT_SHARD","documentIdKey","isCasConflict","documentIdKey","DEFAULT_SHARD"]}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@helipod/objectstore-substrate",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "license": "SEE LICENSE IN LICENSE",
6
+ "sideEffects": false,
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "LICENSE",
18
+ "dist"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsup",
22
+ "test": "vitest run --exclude '**/*.e2e.test.ts' --exclude 'test/gc-driver.test.ts' --exclude 'test/heartbeat-driver.test.ts'",
23
+ "test:e2e": "vitest run test/*.e2e.test.ts test/gc-driver.test.ts test/heartbeat-driver.test.ts",
24
+ "typecheck": "tsc --noEmit",
25
+ "clean": "rm -rf dist .turbo"
26
+ },
27
+ "dependencies": {
28
+ "@helipod/component": "0.1.0",
29
+ "@helipod/docstore": "0.1.0",
30
+ "@helipod/docstore-sqlite": "0.1.0",
31
+ "@helipod/id-codec": "0.1.0",
32
+ "@helipod/index-key-codec": "0.1.0",
33
+ "@helipod/objectstore": "0.1.0",
34
+ "@helipod/values": "0.1.0"
35
+ },
36
+ "devDependencies": {
37
+ "@aws-sdk/client-s3": "^3.1079.0",
38
+ "@helipod/executor": "0.1.0",
39
+ "@helipod/objectstore-fs": "0.1.0",
40
+ "@helipod/objectstore-s3": "0.1.0",
41
+ "@helipod/runtime-embedded": "0.1.0",
42
+ "@types/node": "^22.10.5",
43
+ "tsup": "^8.3.5",
44
+ "typescript": "^5.7.2",
45
+ "vitest": "^2.1.8"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public"
49
+ },
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/helipod-sh/helipod.git",
53
+ "directory": "ee/packages/objectstore-substrate"
54
+ }
55
+ }