instar 1.3.520 → 1.3.522

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +119 -2
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  5. package/dist/config/ConfigDefaults.js +32 -0
  6. package/dist/config/ConfigDefaults.js.map +1 -1
  7. package/dist/core/ReplicatedRecordEnvelope.d.ts +380 -0
  8. package/dist/core/ReplicatedRecordEnvelope.d.ts.map +1 -0
  9. package/dist/core/ReplicatedRecordEnvelope.js +464 -0
  10. package/dist/core/ReplicatedRecordEnvelope.js.map +1 -0
  11. package/dist/core/devGatedFeatures.d.ts.map +1 -1
  12. package/dist/core/devGatedFeatures.js +6 -0
  13. package/dist/core/devGatedFeatures.js.map +1 -1
  14. package/dist/core/stateSyncConfig.d.ts +94 -0
  15. package/dist/core/stateSyncConfig.d.ts.map +1 -0
  16. package/dist/core/stateSyncConfig.js +126 -0
  17. package/dist/core/stateSyncConfig.js.map +1 -0
  18. package/dist/core/types.d.ts +76 -0
  19. package/dist/core/types.d.ts.map +1 -1
  20. package/dist/core/types.js.map +1 -1
  21. package/dist/monitoring/OrphanedWorkSentinel.d.ts +148 -0
  22. package/dist/monitoring/OrphanedWorkSentinel.d.ts.map +1 -0
  23. package/dist/monitoring/OrphanedWorkSentinel.js +177 -0
  24. package/dist/monitoring/OrphanedWorkSentinel.js.map +1 -0
  25. package/dist/monitoring/guardManifest.d.ts.map +1 -1
  26. package/dist/monitoring/guardManifest.js +14 -0
  27. package/dist/monitoring/guardManifest.js.map +1 -1
  28. package/dist/monitoring/orphanedWorkGit.d.ts +30 -0
  29. package/dist/monitoring/orphanedWorkGit.d.ts.map +1 -0
  30. package/dist/monitoring/orphanedWorkGit.js +124 -0
  31. package/dist/monitoring/orphanedWorkGit.js.map +1 -0
  32. package/dist/server/AgentServer.d.ts +3 -0
  33. package/dist/server/AgentServer.d.ts.map +1 -1
  34. package/dist/server/AgentServer.js +1 -0
  35. package/dist/server/AgentServer.js.map +1 -1
  36. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  37. package/dist/server/CapabilityIndex.js +1 -0
  38. package/dist/server/CapabilityIndex.js.map +1 -1
  39. package/dist/server/routes.d.ts +3 -0
  40. package/dist/server/routes.d.ts.map +1 -1
  41. package/dist/server/routes.js +11 -0
  42. package/dist/server/routes.js.map +1 -1
  43. package/package.json +1 -1
  44. package/src/data/builtin-manifest.json +47 -47
  45. package/upgrades/1.3.521.md +69 -0
  46. package/upgrades/1.3.522.md +25 -0
  47. package/upgrades/side-effects/multi-machine-replicated-store-foundation-step2-journal-kind.md +104 -0
  48. package/upgrades/side-effects/orphaned-work-sentinel.md +40 -0
  49. package/upgrades/1.3.520.md +0 -50
@@ -0,0 +1,464 @@
1
+ /**
2
+ * ReplicatedRecordEnvelope — the GENERIC substrate for replicated-store journal
3
+ * kinds (WS2 replicated-store foundation, Component 2 / Build-order step 2).
4
+ *
5
+ * Spec: docs/specs/multi-machine-replicated-store-foundation.md §4 (the
6
+ * replicated-record envelope, flag-gated + flag-coherence-gated emission), §10
7
+ * (config / advert / awareness), §13 build-order item 2, §14 (safety posture).
8
+ *
9
+ * This module is the REUSABLE machinery the concrete stores (WS2.1 preferences,
10
+ * relationships, learnings, …) layer a kind onto. It adds NO concrete store
11
+ * kind itself — the registry ships EMPTY. It provides exactly four pieces:
12
+ *
13
+ * A. The replicated-record envelope type + a parameterizable STRICT validator
14
+ * (`validateReplicatedEnvelope`) that mirrors the CoherenceJournal typed-
15
+ * schema discipline: reject free text, drop unknown fields (counted), jail
16
+ * any path-shaped field, validate `hlc` + (when present) `observed` as
17
+ * well-formed HlcTimestamps. ABSENT `observed` is LEGAL ⇒ "no prior
18
+ * witness" ⇒ flag-on-conflict (the safe direction, §4 / §7.2). The validated
19
+ * envelope fields are AUTHORITATIVE: a store schema can never override a
20
+ * reserved envelope field on the reconstructed `data` (reserved keys the
21
+ * store returns are stripped + counted), and the path-jail is REUSABLE
22
+ * machinery for store fields too — a store declares `pathSensitiveFields`
23
+ * for auto-jailing, or calls the exported `jailStoreStringField` helper.
24
+ * Reserved-key collisions in a store's `knownFields` are rejected at
25
+ * registration time (a wiring-time programmer error), not trusted to a
26
+ * store's self-check.
27
+ *
28
+ * B. A registry of replicated kinds (`ReplicatedKindRegistry`) — the generic
29
+ * registration mechanism a concrete store calls to register its kind +
30
+ * store-specific field schema. Initially empty (no concrete kind).
31
+ *
32
+ * C. Flag-gated emission (`isStoreEmissionEnabled`) — a replicated kind is
33
+ * emitted ONLY when `multiMachine.stateSync.<store>.enabled` is true
34
+ * (default FALSE). When off, strict no-op.
35
+ *
36
+ * D. Flag-coherence-gated emission (`shouldEmitToPeer`,
37
+ * `checkPoolFlagCoherence`) — a replicated kind is exchanged with a peer ONLY
38
+ * when that peer's `seamlessnessFlags` advertises the matching
39
+ * `stateSync.<store>` capability. The applier "silently drops unknown
40
+ * kinds" (forward-compat). The per-peer decision is correct for N peers
41
+ * (never assumes exactly 2); the boot-time pool-flag-coherence check
42
+ * coalesces ALL mixed-flag peers into ONE surfaced result.
43
+ *
44
+ * TRANSPORT IS PULL — `shouldEmitToPeer` is intentionally UNWIRED in Step 2.
45
+ * The real journal-sync transport is RECEIVER-DRIVEN PULL: `PeerPresencePuller`
46
+ * iterates the SENDER's advert (`CoherenceJournal.getOwnAdvert()`, over the static
47
+ * `JOURNAL_KINDS` const) and pulls each kind it is behind on; serve + apply both
48
+ * gate on `JOURNAL_KINDS`. There is NO push-forward step. The named "emit a new
49
+ * kind to an OLD peer → silently dropped" mode therefore manifests as "an old
50
+ * peer NEVER PULLS a kind absent from its own JOURNAL_KINDS" (nothing requested ⇒
51
+ * nothing dropped). On this PULL transport the flag-coherence gate's role is the
52
+ * boot-time pool surface (`checkPoolFlagCoherence`, wired in server.ts) PLUS the
53
+ * serve/pull-decision gate the WS2.1 consumer PR will consult once a concrete
54
+ * kind exists — that is where `shouldEmitToPeer` will be called. In Step 2 it is
55
+ * a pure decision with no concrete kind to serve, hence intentionally unwired
56
+ * (NOT an unintegrated push gate).
57
+ *
58
+ * DUAL-REGISTRY COUPLING — a replicated kind MUST be added to BOTH registries.
59
+ * The new `ReplicatedKindRegistry` (read by the gate + the stateSyncReceive
60
+ * advert) and the static `JOURNAL_KINDS` (in CoherenceJournal.ts, gating serve +
61
+ * apply + enumerated by getOwnAdvert) are TWO parallel kind registries. A WS2.1
62
+ * PR that registers into `ReplicatedKindRegistry` but forgets `JOURNAL_KINDS`
63
+ * yields a store that advertises receive=true yet serves/applies/pulls nothing —
64
+ * a SILENT no-replication. The wiring-integrity test asserts every replicated
65
+ * kind is present in BOTH (a CI ratchet, not a memory item).
66
+ *
67
+ * SAFETY POSTURE (§14): this is MECHANISM, dark by default. NO gate here blocks
68
+ * a user-initiated action. The only refusals are at the RECEIVE door (the
69
+ * validator rejects malformed data) and the EMISSION door (don't forward a kind
70
+ * to a non-advertising peer). Both protect data; neither blocks the user. A
71
+ * single-machine install (no advertising peers) is a strict no-op — emission is
72
+ * gated on having a peer advertising the matching flag.
73
+ *
74
+ * Purity: this module imports ONLY the HLC primitive + node:path (for the
75
+ * path-shape jail's separator detection). No fs, no network, no Date. Every
76
+ * function is a pure function of its inputs.
77
+ */
78
+ import path from 'node:path';
79
+ import { coerceHlc } from './HybridLogicalClock.js';
80
+ /** The envelope's own field names — the union of all reserved keys a store may
81
+ * not reuse for a store-specific field. Exported AND ENFORCED:
82
+ * ReplicatedKindRegistry.register() throws if a schema's `knownFields` claims any
83
+ * of these, and validateReplicatedEnvelope strips any reserved key a store's
84
+ * validate() returns (counting it as a dropped field) so the validated envelope
85
+ * fields are the sole authority for the reconstructed `data`. */
86
+ export const RESERVED_ENVELOPE_FIELDS = Object.freeze([
87
+ 'recordKey',
88
+ 'hlc',
89
+ 'op',
90
+ 'origin',
91
+ 'observed',
92
+ ]);
93
+ /** Caps (mirror the CoherenceJournal length-cap discipline — a value over the
94
+ * cap is rejected, never truncated-and-accepted, so a record is whole or not). */
95
+ export const MAX_RECORD_KEY_LENGTH = 512;
96
+ export const MAX_ORIGIN_LENGTH = 256;
97
+ /**
98
+ * Reusable store-field path-jail (the §4 "jail any path-shaped field" rule, made
99
+ * reusable machinery instead of per-store re-implementation). Returns the value
100
+ * unchanged when it is a NON-path-shaped string; returns null (and bumps the
101
+ * shared jail counter) when it is path-shaped, so a store's validate() can reject
102
+ * the whole record. A non-string is returned unchanged (the store does its own
103
+ * type check) — this helper enforces the PATH discipline only.
104
+ *
105
+ * Usage inside a store's validate():
106
+ * const fp = jailStoreStringField(raw.filePath, ctx);
107
+ * if (fp === null) return null; // path-shaped → reject whole record
108
+ */
109
+ export function jailStoreStringField(value, ctx) {
110
+ if (typeof value === 'string' && isPathShaped(value)) {
111
+ ctx.countJailReject();
112
+ return null;
113
+ }
114
+ return value;
115
+ }
116
+ /**
117
+ * Is a string PATH-SHAPED? (The §4 "jail any path-shaped field" rule, applied
118
+ * to the envelope's string fields — recordKey/origin — so a path can never
119
+ * smuggle in as a primary key.) A value is path-shaped if it contains a path
120
+ * separator, a `..` traversal segment, or is an absolute path. This is a
121
+ * STRUCTURAL reject (the field has no business holding a path), not a
122
+ * filesystem jail — the envelope carries identifiers, never artifact paths.
123
+ */
124
+ export function isPathShaped(value) {
125
+ if (path.isAbsolute(value))
126
+ return true;
127
+ if (value.includes('/') || value.includes('\\'))
128
+ return true;
129
+ // A bare `..` or any `..`-as-segment.
130
+ const segments = value.split(/[/\\]/);
131
+ if (segments.some((s) => s === '..' || s === '.'))
132
+ return true;
133
+ return false;
134
+ }
135
+ /** Validate one HLC field, returning the narrowed value or null (never throws). */
136
+ function tryCoerceHlc(raw) {
137
+ try {
138
+ return coerceHlc(raw);
139
+ }
140
+ catch {
141
+ // @silent-fallback-ok: coerceHlc throws on malformed input by contract; here
142
+ // we are deliberately converting that into a typed rejection (null) so the
143
+ // validator can bump a counter and reject the WHOLE record — a malformed hlc
144
+ // is a schema reject, never a silent default. The reject is counted + surfaced
145
+ // by the caller via EnvelopeValidationCounters; nothing is swallowed.
146
+ return null;
147
+ }
148
+ }
149
+ /**
150
+ * The strict envelope validator (§4). Mirrors the CoherenceJournal typed-schema
151
+ * discipline:
152
+ * - rejects a non-object,
153
+ * - validates recordKey (non-empty, length-capped, NOT path-shaped → jail),
154
+ * - validates hlc as a well-formed HlcTimestamp,
155
+ * - validates op ∈ {'put','delete'},
156
+ * - validates origin (non-empty, length-capped, NOT path-shaped),
157
+ * - validates observed when present (well-formed HlcTimestamp); ABSENT is LEGAL,
158
+ * - delegates store-specific fields to the supplied StoreFieldSchema,
159
+ * - DROPS every field not in (RESERVED_ENVELOPE_FIELDS ∪ schema.knownFields),
160
+ * counting each drop.
161
+ *
162
+ * Returns a typed result. NEVER throws on bad DATA — it bumps a counter and
163
+ * returns `{ ok: false }`. The reconstructed `data` object on success is the
164
+ * validated store fields + the validated envelope fields, in a deterministic
165
+ * shape (only known fields, observed omitted when absent), ready to serialize as
166
+ * the journal entry's `data`. The validated envelope fields are AUTHORITATIVE:
167
+ * any RESERVED_ENVELOPE_FIELDS key the store's validate() returns is stripped
168
+ * (and counted as a dropped field) before merge, so a store can NEVER override a
169
+ * load-bearing envelope field (op/recordKey/hlc/origin/observed) on `data` — the
170
+ * on-disk `data.op/recordKey/hlc/origin/observed` always equal the validated
171
+ * `envelope.*`. `storeFields` on the result is likewise reserved-key-stripped.
172
+ */
173
+ export function validateReplicatedEnvelope(raw, schema, counters) {
174
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
175
+ counters.bumpSchemaReject();
176
+ return { ok: false, reason: 'not-an-object' };
177
+ }
178
+ const obj = raw;
179
+ // recordKey — non-empty, length-capped, not path-shaped (jailed).
180
+ const recordKey = obj.recordKey;
181
+ if (typeof recordKey !== 'string' || recordKey.length === 0 || recordKey.length > MAX_RECORD_KEY_LENGTH) {
182
+ counters.bumpSchemaReject();
183
+ return { ok: false, reason: 'bad-record-key' };
184
+ }
185
+ if (isPathShaped(recordKey)) {
186
+ counters.bumpJailReject();
187
+ counters.bumpSchemaReject();
188
+ return { ok: false, reason: 'record-key-path-shaped' };
189
+ }
190
+ // hlc — well-formed HlcTimestamp (required).
191
+ const hlc = tryCoerceHlc(obj.hlc);
192
+ if (hlc === null) {
193
+ counters.bumpSchemaReject();
194
+ return { ok: false, reason: 'bad-hlc' };
195
+ }
196
+ // op — enum.
197
+ const op = obj.op;
198
+ if (op !== 'put' && op !== 'delete') {
199
+ counters.bumpSchemaReject();
200
+ return { ok: false, reason: 'bad-op' };
201
+ }
202
+ // origin — non-empty, length-capped, not path-shaped.
203
+ const origin = obj.origin;
204
+ if (typeof origin !== 'string' || origin.length === 0 || origin.length > MAX_ORIGIN_LENGTH) {
205
+ counters.bumpSchemaReject();
206
+ return { ok: false, reason: 'bad-origin' };
207
+ }
208
+ if (isPathShaped(origin)) {
209
+ counters.bumpJailReject();
210
+ counters.bumpSchemaReject();
211
+ return { ok: false, reason: 'bad-origin' };
212
+ }
213
+ // observed — well-formed HlcTimestamp WHEN PRESENT; absent is legal.
214
+ let observed;
215
+ if (obj.observed !== undefined && obj.observed !== null) {
216
+ const witness = tryCoerceHlc(obj.observed);
217
+ if (witness === null) {
218
+ counters.bumpSchemaReject();
219
+ return { ok: false, reason: 'bad-observed' };
220
+ }
221
+ observed = witness;
222
+ }
223
+ // Auto-jail every DECLARED path-sensitive store field BEFORE the store's own
224
+ // validate() — the §4 path-jail discipline applied to store fields as reusable
225
+ // machinery (Structure > Willpower), not left to each store to re-implement.
226
+ // A path-shaped declared field rejects the whole record (jail counter bumped),
227
+ // exactly as recordKey/origin do.
228
+ if (schema.pathSensitiveFields) {
229
+ for (const field of schema.pathSensitiveFields) {
230
+ const v = obj[field];
231
+ if (typeof v === 'string' && isPathShaped(v)) {
232
+ counters.bumpJailReject();
233
+ counters.bumpSchemaReject();
234
+ return { ok: false, reason: 'store-field-path-shaped' };
235
+ }
236
+ }
237
+ }
238
+ // Store-specific fields. The store validator may reject the whole record. The
239
+ // context carries BOTH counters so a store-side drop (countDroppedField) and a
240
+ // store-side imperative jail (countJailReject, e.g. via jailStoreStringField)
241
+ // feed the SAME counters CoherenceJournal surfaces — uniform accounting.
242
+ let storeRejected = false;
243
+ const storeCtx = {
244
+ countDroppedField: () => counters.bumpDroppedField(),
245
+ countJailReject: () => counters.bumpJailReject(),
246
+ };
247
+ const storeFields = schema.validate(obj, storeCtx);
248
+ if (storeFields === null) {
249
+ storeRejected = true;
250
+ }
251
+ if (storeRejected || storeFields === null) {
252
+ counters.bumpSchemaReject();
253
+ return { ok: false, reason: 'store-schema-rejected' };
254
+ }
255
+ // Count dropped unknown fields across the WHOLE object: any key not in the
256
+ // reserved envelope fields and not owned by the store schema is dropped.
257
+ const known = new Set([...RESERVED_ENVELOPE_FIELDS, ...schema.knownFields]);
258
+ for (const k of Object.keys(obj)) {
259
+ if (!known.has(k))
260
+ counters.bumpDroppedField();
261
+ }
262
+ const envelope = {
263
+ recordKey,
264
+ hlc,
265
+ op,
266
+ origin,
267
+ ...(observed !== undefined ? { observed } : {}),
268
+ };
269
+ // Defense-in-depth: a store schema's validate() returns the store-specific
270
+ // portion, but a buggy/hostile store COULD echo a RESERVED envelope key (op,
271
+ // recordKey, hlc, origin, observed) in its returned object. Those keys are
272
+ // load-bearing — op decides put/delete tombstone semantics, hlc is the merge
273
+ // total-order, recordKey is the primary key — and the store's copy is the
274
+ // UN-validated, UN-jailed value. We therefore STRIP every reserved key from
275
+ // the store's returned fields (counting each as a dropped field, the same as
276
+ // an unknown field) BEFORE merging, so the VALIDATED envelope fields are the
277
+ // sole authority for the reconstructed `data`. This makes the reserved keys
278
+ // un-overridable by construction — not "spread the envelope last and hope".
279
+ const reserved = new Set(RESERVED_ENVELOPE_FIELDS);
280
+ const storeFieldsSafe = {};
281
+ for (const [k, v] of Object.entries(storeFields)) {
282
+ if (reserved.has(k)) {
283
+ counters.bumpDroppedField();
284
+ continue;
285
+ }
286
+ storeFieldsSafe[k] = v;
287
+ }
288
+ // The reconstructed data shape: validated store fields (reserved keys stripped)
289
+ // FIRST, then the validated envelope fields LAST so a store can never clobber a
290
+ // load-bearing envelope field on a key collision. observed omitted when absent
291
+ // so the on-disk shape is deterministic.
292
+ const data = {
293
+ ...storeFieldsSafe,
294
+ recordKey,
295
+ hlc,
296
+ op,
297
+ origin,
298
+ ...(observed !== undefined ? { observed } : {}),
299
+ };
300
+ return { ok: true, envelope, storeFields: storeFieldsSafe, data };
301
+ }
302
+ /**
303
+ * A registry of replicated kinds. Ships EMPTY — Step 2 adds the GENERIC
304
+ * registration mechanism; the first concrete kind is registered by WS2.1.
305
+ *
306
+ * Why a registry (not a hardcoded union extension): the spec mandates "add the
307
+ * GENERIC registration mechanism" and "do NOT hardcode the future concrete
308
+ * kinds into the union in a way that forces them to exist now". A registry lets
309
+ * each store register independently at wiring time; an unregistered/never-
310
+ * emitted kind is simply absent on read (the reader filters by kind), and the
311
+ * applier already drops unknown kinds forward-compat — so an old peer that does
312
+ * not know a kind poisons nothing.
313
+ */
314
+ export class ReplicatedKindRegistry {
315
+ byKind = new Map();
316
+ byStore = new Map();
317
+ /**
318
+ * Register a replicated kind. Idempotent for an IDENTICAL re-registration of
319
+ * the same (kind, store); THROWS on a CONFLICTING registration (a different
320
+ * store claiming an already-claimed kind, or a store re-registered with a
321
+ * different kind) — a registration conflict is a programmer error at wiring
322
+ * time, surfaced loudly, never a silent overwrite.
323
+ */
324
+ register(reg) {
325
+ if (typeof reg.kind !== 'string' || reg.kind.length === 0) {
326
+ throw new Error('ReplicatedKindRegistry.register: kind must be a non-empty string');
327
+ }
328
+ if (typeof reg.store !== 'string' || reg.store.length === 0) {
329
+ throw new Error('ReplicatedKindRegistry.register: store must be a non-empty string');
330
+ }
331
+ if (!reg.schema || typeof reg.schema.validate !== 'function' || !Array.isArray(reg.schema.knownFields)) {
332
+ throw new Error(`ReplicatedKindRegistry.register: kind "${reg.kind}" needs a StoreFieldSchema with knownFields[] + validate()`);
333
+ }
334
+ // ENFORCE non-collision with the reserved envelope fields (not merely
335
+ // documented for the store author to self-check). A schema that claims a
336
+ // reserved key in knownFields would defeat the unknown-field counter for that
337
+ // key (it'd be treated as store-owned), letting a store quietly opt a
338
+ // load-bearing envelope field into its own control surface. This is a
339
+ // wiring-time programmer error, surfaced loudly — same class as the conflict
340
+ // throws below, never a silent acceptance.
341
+ const reservedSet = new Set(RESERVED_ENVELOPE_FIELDS);
342
+ const claimedReserved = reg.schema.knownFields.filter((f) => reservedSet.has(f));
343
+ if (claimedReserved.length > 0) {
344
+ throw new Error(`ReplicatedKindRegistry.register: kind "${reg.kind}" schema.knownFields claims reserved envelope field(s) [${claimedReserved.join(',')}] — reserved fields are owned by the envelope and may not be store-owned`);
345
+ }
346
+ const existingByKind = this.byKind.get(reg.kind);
347
+ if (existingByKind) {
348
+ if (existingByKind.store !== reg.store) {
349
+ throw new Error(`ReplicatedKindRegistry: kind "${reg.kind}" already registered for store "${existingByKind.store}", cannot reassign to "${reg.store}"`);
350
+ }
351
+ // Same (kind, store) — idempotent; keep the first registration's schema.
352
+ return;
353
+ }
354
+ const existingByStore = this.byStore.get(reg.store);
355
+ if (existingByStore && existingByStore.kind !== reg.kind) {
356
+ throw new Error(`ReplicatedKindRegistry: store "${reg.store}" already owns kind "${existingByStore.kind}", cannot also claim "${reg.kind}"`);
357
+ }
358
+ this.byKind.set(reg.kind, reg);
359
+ this.byStore.set(reg.store, reg);
360
+ }
361
+ /** Is this kind a registered replicated kind? */
362
+ isReplicatedKind(kind) {
363
+ return this.byKind.has(kind);
364
+ }
365
+ /** The registration for a kind, or undefined if unregistered. */
366
+ getByKind(kind) {
367
+ return this.byKind.get(kind);
368
+ }
369
+ /** The registration for a store, or undefined if unregistered. */
370
+ getByStore(store) {
371
+ return this.byStore.get(store);
372
+ }
373
+ /** All registered kinds (the JournalKind strings). Empty until a store registers. */
374
+ kinds() {
375
+ return [...this.byKind.keys()];
376
+ }
377
+ /** All registered store keys. Empty until a store registers. */
378
+ stores() {
379
+ return [...this.byStore.keys()];
380
+ }
381
+ /** Count of registered kinds (0 in the Step-2 substrate-only state). */
382
+ get size() {
383
+ return this.byKind.size;
384
+ }
385
+ }
386
+ /**
387
+ * Is emission ENABLED for a store? (§4 flag-gated emission.) True ONLY when
388
+ * `stateSync[store].enabled === true` (default false). When off, the store
389
+ * NEVER emits its kind — a strict no-op. Nullish/absent ⇒ false.
390
+ */
391
+ export function isStoreEmissionEnabled(stores, store) {
392
+ return stores?.[store]?.enabled === true;
393
+ }
394
+ /** Does THIS peer advertise the matching stateSync.<store> receive capability? */
395
+ export function peerAdvertisesStore(peer, store) {
396
+ return peer.stateSyncReceive?.[store] === true;
397
+ }
398
+ /**
399
+ * The per-peer emission decision (§4 flag-coherence gating). Emit a replicated
400
+ * kind to a peer ONLY when:
401
+ * - emission is enabled for the store locally (isStoreEmissionEnabled), AND
402
+ * - the peer is online, AND
403
+ * - the peer advertises the matching stateSync.<store> capability.
404
+ * Withhold otherwise — emitting to a non-advertising peer is the NAMED data-loss
405
+ * skew mode (the applier silently drops the unknown kind). Returns a typed
406
+ * decision so the caller can surface a withhold.
407
+ *
408
+ * This is PER-PEER and correct for N peers — the caller iterates peers and calls
409
+ * this once each; there is no "exactly 2" assumption anywhere.
410
+ */
411
+ export function shouldEmitToPeer(stores, store, peer) {
412
+ if (!isStoreEmissionEnabled(stores, store))
413
+ return { emit: false, reason: 'store-disabled' };
414
+ if (peer.online === false)
415
+ return { emit: false, reason: 'peer-offline' };
416
+ if (!peerAdvertisesStore(peer, store))
417
+ return { emit: false, reason: 'peer-not-advertising' };
418
+ return { emit: true, reason: 'emit' };
419
+ }
420
+ /**
421
+ * The boot-time pool-flag-coherence check (§4: "A boot-time pool-flag-coherence
422
+ * check surfaces (ONCE) any mixed state"). Iterates ALL registered replicated
423
+ * stores × ALL advertising peers and computes, per store, the advertising vs
424
+ * non-advertising online peers. A store is MIXED when it is locally enabled AND
425
+ * has at least one online peer that does NOT advertise it (that peer would
426
+ * silently drop our kind) alongside at least one online peer that DOES (or there
427
+ * is anyone at all to forward to). The result is ONE coalesced object — the
428
+ * caller surfaces it once (e.g. one Attention item / one log line), never one
429
+ * per peer per tick. Correct for N peers.
430
+ *
431
+ * PURE: no I/O, no surfacing — it computes the verdict; the caller decides how
432
+ * to surface (and dedupes across boots).
433
+ */
434
+ export function checkPoolFlagCoherence(registry, stores, peers) {
435
+ const onlinePeers = peers.filter((p) => p.online !== false);
436
+ const out = [];
437
+ const mixedStores = [];
438
+ const summary = [];
439
+ for (const store of registry.stores()) {
440
+ // Only locally-enabled stores can emit, so only they can suffer the skew.
441
+ if (!isStoreEmissionEnabled(stores, store))
442
+ continue;
443
+ const advertising = [];
444
+ const notAdvertising = [];
445
+ for (const peer of onlinePeers) {
446
+ if (peerAdvertisesStore(peer, store))
447
+ advertising.push(peer.machineId);
448
+ else
449
+ notAdvertising.push(peer.machineId);
450
+ }
451
+ // MIXED = locally enabled (we will emit) AND ≥1 online peer cannot receive
452
+ // it. That non-advertiser is the silent-drop victim — the named skew mode.
453
+ const mixed = notAdvertising.length > 0;
454
+ out.push({ store, advertising, notAdvertising, mixed });
455
+ if (mixed) {
456
+ mixedStores.push(store);
457
+ // Content-free: store key + counts + the non-advertiser ids (machine ids
458
+ // are routing identifiers, not user content).
459
+ summary.push(`stateSync.${store}: ${advertising.length} peer(s) ready, ${notAdvertising.length} peer(s) cannot receive (would silently drop) [${notAdvertising.join(',')}]`);
460
+ }
461
+ }
462
+ return { stores: out, mixedStores, summary };
463
+ }
464
+ //# sourceMappingURL=ReplicatedRecordEnvelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReplicatedRecordEnvelope.js","sourceRoot":"","sources":["../../src/core/ReplicatedRecordEnvelope.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"devGatedFeatures.d.ts","sourceRoot":"","sources":["../../src/core/devGatedFeatures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,oEAAoE;AACpE,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,kBAAkB,EAAE,eAAe,EAiG/C,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,gBAAgB,GACxB,aAAa,GACb,sBAAsB,GACtB,cAAc,GACd,iBAAiB,GACjB,0BAA0B,CAAC;AAE/B,MAAM,WAAW,iBAAiB;IAChC,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,oBAAoB,EAAE,iBAAiB,EAyHnD,CAAC;AAEF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAO5E"}
1
+ {"version":3,"file":"devGatedFeatures.d.ts","sourceRoot":"","sources":["../../src/core/devGatedFeatures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,oEAAoE;AACpE,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,kBAAkB,EAAE,eAAe,EAyG/C,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,gBAAgB,GACxB,aAAa,GACb,sBAAsB,GACtB,cAAc,GACd,iBAAiB,GACjB,0BAA0B,CAAC;AAE/B,MAAM,WAAW,iBAAiB;IAChC,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,oBAAoB,EAAE,iBAAiB,EAyHnD,CAAC;AAEF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAO5E"}
@@ -114,6 +114,12 @@ export const DEV_GATED_FEATURES = [
114
114
  description: 'Threadline single-negotiator lease — one session owns each conversation\'s outbound voice (THREADLINE-SINGLE-NEGOTIATOR-SPEC, CMT-1362).',
115
115
  justification: 'Lives live on a dev agent ONLY in dry-run (dryRun defaults true) — it engages the lease logic and logs every would-hold verdict for the FD-7 false-positive telemetry but withholds NOTHING (a real send is only blocked by an explicit dryRun:false). No egress, no spend, no destructive action; this is exactly the dogfooding posture FD-7 requires before the lease can ever enforce. (Was mis-classified deliberate-fleet-default at ship, which starved the telemetry — corrected here.)',
116
116
  },
117
+ {
118
+ name: 'orphanedWorkSentinel',
119
+ configPath: 'monitoring.orphanedWorkSentinel.enabled',
120
+ description: 'Silent-uncommitted-death backstop — flags agent worktrees with uncommitted work whose owning session died (/orphaned-work).',
121
+ justification: 'Signal-only local recorder + ONE deduped attention item; reads git status/diff + lsof read-only; no egress, no spend, no destructive action. The optional preservation is a NON-destructive patch write (git diff → a state-dir file) behind an off-by-default preserveWork sub-flag — it never mutates the worktree, its index, or any ref.',
122
+ },
117
123
  ];
118
124
  export const DARK_GATE_EXCLUSIONS = [
119
125
  // ── destructive — kills/deletes on a heuristic; off + dry-run for EVERYONE ──
@@ -1 +1 @@
1
- {"version":3,"file":"devGatedFeatures.js","sourceRoot":"","sources":["../../src/core/devGatedFeatures.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"devGatedFeatures.js","sourceRoot":"","sources":["../../src/core/devGatedFeatures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAoBH,MAAM,CAAC,MAAM,kBAAkB,GAAsB;IACnD;QACE,IAAI,EAAE,eAAe;QACrB,UAAU,EAAE,kCAAkC;QAC9C,WAAW,EAAE,mDAAmD;QAChE,aAAa,EAAE,uGAAuG;KACvH;IACD;QACE,IAAI,EAAE,kBAAkB;QACxB,UAAU,EAAE,uCAAuC;QACnD,WAAW,EAAE,kCAAkC;QAC/C,aAAa,EAAE,yGAAyG;KACzH;IACD;QACE,IAAI,EAAE,gBAAgB;QACtB,UAAU,EAAE,mCAAmC;QAC/C,WAAW,EAAE,gDAAgD;QAC7D,aAAa,EAAE,sGAAsG;KACtH;IACD;QACE,IAAI,EAAE,YAAY;QAClB,UAAU,EAAE,iCAAiC;QAC7C,WAAW,EAAE,2CAA2C;QACxD,aAAa,EAAE,wGAAwG;KACxH;IACD;QACE,IAAI,EAAE,kBAAkB;QACxB,UAAU,EAAE,6CAA6C;QACzD,WAAW,EAAE,gCAAgC;QAC7C,aAAa,EAAE,sGAAsG;KACtH;IACD;QACE,IAAI,EAAE,qBAAqB;QAC3B,UAAU,EAAE,sCAAsC;QAClD,WAAW,EAAE,mDAAmD;QAChE,aAAa,EAAE,8FAA8F;KAC9G;IACD;QACE,IAAI,EAAE,6BAA6B;QACnC,UAAU,EAAE,sCAAsC;QAClD,WAAW,EAAE,gDAAgD;QAC7D,aAAa,EAAE,yGAAyG;KACzH;IACD;QACE,IAAI,EAAE,cAAc;QACpB,UAAU,EAAE,sBAAsB;QAClC,WAAW,EAAE,iEAAiE;QAC9E,aAAa,EAAE,sDAAsD;KACtE;IACD;QACE,IAAI,EAAE,8BAA8B;QACpC,UAAU,EAAE,uCAAuC;QACnD,WAAW,EAAE,oEAAoE;QACjF,aAAa,EAAE,yDAAyD;KACzE;IACD;QACE,IAAI,EAAE,eAAe;QACrB,UAAU,EAAE,kCAAkC;QAC9C,WAAW,EAAE,8EAA8E;QAC3F,aAAa,EAAE,qNAAqN;KACrO;IACD;QACE,IAAI,EAAE,eAAe;QACrB,UAAU,EAAE,uBAAuB;QACnC,WAAW,EAAE,+EAA+E;QAC5F,aAAa,EAAE,yNAAyN;KACzO;IACD;QACE,IAAI,EAAE,eAAe;QACrB,UAAU,EAAE,yCAAyC;QACrD,WAAW,EAAE,wGAAwG;QACrH,aAAa,EAAE,qRAAqR;KACrS;IACD;QACE,IAAI,EAAE,wCAAwC;QAC9C,UAAU,EAAE,4DAA4D;QACxE,WAAW,EACT,2KAA2K;QAC7K,aAAa,EACX,2OAA2O;KAC9O;IACD;QACE,IAAI,EAAE,2BAA2B;QACjC,UAAU,EAAE,8CAA8C;QAC1D,WAAW,EACT,6IAA6I;QAC/I,aAAa,EACX,uKAAuK;KAC1K;IACD;QACE,IAAI,EAAE,4BAA4B;QAClC,UAAU,EAAE,qCAAqC;QACjD,WAAW,EACT,0IAA0I;QAC5I,aAAa,EACX,ieAAie;KACpe;IACD;QACE,IAAI,EAAE,sBAAsB;QAC5B,UAAU,EAAE,yCAAyC;QACrD,WAAW,EACT,6HAA6H;QAC/H,aAAa,EACX,8UAA8U;KACjV;CACF,CAAC;AA+BF,MAAM,CAAC,MAAM,oBAAoB,GAAwB;IACvD,+EAA+E;IAC/E;QACE,UAAU,EAAE,kCAAkC;QAC9C,QAAQ,EAAE,aAAa;QACvB,MAAM,EAAE,+CAA+C;KACxD;IACD;QACE,UAAU,EAAE,wCAAwC;QACpD,QAAQ,EAAE,aAAa;QACvB,MAAM,EAAE,iDAAiD;KAC1D;IACD;QACE,UAAU,EAAE,qCAAqC;QACjD,QAAQ,EAAE,aAAa;QACvB,MAAM,EAAE,+CAA+C;KACxD;IACD,wEAAwE;IACxE;QACE,UAAU,EAAE,8BAA8B;QAC1C,QAAQ,EAAE,cAAc;QACxB,MAAM,EAAE,mDAAmD;KAC5D;IACD;QACE,UAAU,EAAE,gCAAgC;QAC5C,QAAQ,EAAE,cAAc;QACxB,MAAM,EAAE,iIAAiI;KAC1I;IACD;QACE,UAAU,EAAE,qCAAqC;QACjD,QAAQ,EAAE,cAAc;QACxB,MAAM,EAAE,gGAAgG;KACzG;IACD,uFAAuF;IACvF;QACE,UAAU,EAAE,qDAAqD;QACjE,QAAQ,EAAE,iBAAiB;QAC3B,MAAM,EAAE,kCAAkC;KAC3C;IACD;QACE,UAAU,EAAE,2CAA2C;QACvD,QAAQ,EAAE,iBAAiB;QAC3B,MAAM,EAAE,kCAAkC;KAC3C;IACD;QACE,UAAU,EAAE,+BAA+B;QAC3C,QAAQ,EAAE,iBAAiB;QAC3B,MAAM,EAAE,qDAAqD;KAC9D;IACD,qDAAqD;IACrD;QACE,UAAU,EAAE,kCAAkC;QAC9C,QAAQ,EAAE,sBAAsB;QAChC,MAAM,EAAE,8CAA8C;KACvD;IACD;QACE,UAAU,EAAE,+CAA+C;QAC3D,QAAQ,EAAE,sBAAsB;QAChC,MAAM,EAAE,kGAAkG;KAC3G;IACD;QACE,UAAU,EAAE,mDAAmD;QAC/D,QAAQ,EAAE,sBAAsB;QAChC,MAAM,EAAE,+FAA+F;KACxG;IACD,0EAA0E;IAC1E;QACE,UAAU,EAAE,qCAAqC;QACjD,QAAQ,EAAE,0BAA0B;QACpC,MAAM,EAAE,oWAAoW;KAC7W;IACD;QACE,UAAU,EAAE,qCAAqC;QACjD,QAAQ,EAAE,0BAA0B;QACpC,MAAM,EAAE,2FAA2F;KACpG;IACD;QACE,UAAU,EAAE,yCAAyC;QACrD,QAAQ,EAAE,0BAA0B;QACpC,MAAM,EAAE,+EAA+E;KACxF;IACD;QACE,UAAU,EAAE,oCAAoC;QAChD,QAAQ,EAAE,0BAA0B;QACpC,MAAM,EAAE,mFAAmF;KAC5F;IACD;QACE,UAAU,EAAE,uCAAuC;QACnD,QAAQ,EAAE,0BAA0B;QACpC,MAAM,EAAE,4FAA4F;KACrG;IACD;QACE,UAAU,EAAE,2CAA2C;QACvD,QAAQ,EAAE,0BAA0B;QACpC,MAAM,EAAE,kFAAkF;KAC3F;IACD;QACE,UAAU,EAAE,6CAA6C;QACzD,QAAQ,EAAE,0BAA0B;QACpC,MAAM,EAAE,uFAAuF;KAChG;IACD;QACE,UAAU,EAAE,qCAAqC;QACjD,QAAQ,EAAE,0BAA0B;QACpC,MAAM,EAAE,oGAAoG;KAC7G;IACD;QACE,UAAU,EAAE,+BAA+B;QAC3C,QAAQ,EAAE,0BAA0B;QACpC,MAAM,EAAE,2FAA2F;KACpG;IACD;QACE,UAAU,EAAE,gBAAgB;QAC5B,QAAQ,EAAE,0BAA0B;QACpC,MAAM,EAAE,oGAAoG;KAC7G;IACD;QACE,UAAU,EAAE,gBAAgB;QAC5B,QAAQ,EAAE,0BAA0B;QACpC,MAAM,EAAE,iGAAiG;KAC1G;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,MAAe,EAAE,UAAkB;IACjE,IAAI,GAAG,GAAY,MAAM,CAAC;IAC1B,KAAK,MAAM,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACxC,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QAC7D,GAAG,GAAI,GAA+B,CAAC,GAAG,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * stateSync config resolution + invariant validation (WS2 replicated-store
3
+ * foundation, §10.2).
4
+ *
5
+ * Spec: docs/specs/multi-machine-replicated-store-foundation.md §10 (config
6
+ * schema, validateStateSyncInvariants, rollout posture), §3.4 (the maxDriftMs
7
+ * clamp), §8.2 (snapshot-cache ceilings).
8
+ *
9
+ * Mirrors `seamlessnessConfig.ts`: resolve the optional `multiMachine.stateSync`
10
+ * knobs to concrete values, then enforce the cross-knob invariants. A violating
11
+ * config is REJECTED with a clear message (StateSyncConfigError) rather than
12
+ * silently coerced — the §10.2 contract: "a bad config is REJECTED, not silently
13
+ * degraded". The per-store `enabled` flags are NOT validated here (they are the
14
+ * dark-by-default on-switches, store by store); this module governs the
15
+ * FOUNDATION-LEVEL knobs every store shares (the journal budget, the HLC drift
16
+ * ceiling, the snapshot cache bounds).
17
+ */
18
+ import { DEFAULT_MAX_DRIFT_MS, MIN_MAX_DRIFT_MS, MAX_MAX_DRIFT_MS } from './HybridLogicalClock.js';
19
+ import type { MultiMachineConfig } from './types.js';
20
+ /** Foundation-level stateSync defaults (§10.2). */
21
+ export declare const DEFAULT_AGGREGATE_JOURNAL_BUDGET_BYTES: number;
22
+ export declare const DEFAULT_MAX_CACHED_SNAPSHOTS = 16;
23
+ export declare const DEFAULT_MAX_CACHE_BYTES: number;
24
+ /**
25
+ * The propagation allowance (§10.2): the invariant `maxDriftMs > flush interval +
26
+ * propagation allowance` must hold so the drift bound can never be tighter than
27
+ * the journal's own flush+replicate latency (which would quarantine our own
28
+ * in-flight writes). With the 60s floor this always holds; the allowance is the
29
+ * realistic worst-case mesh propagation lag we account for explicitly.
30
+ */
31
+ export declare const DEFAULT_PROPAGATION_ALLOWANCE_MS: number;
32
+ /** Fully-resolved foundation-level stateSync knobs (every field concrete). */
33
+ export interface ResolvedStateSyncConfig {
34
+ /** Aggregate journal byte budget across all replicated kinds (§10.2). */
35
+ aggregateJournalBudgetBytes: number;
36
+ /** The CLAMPED HLC bounded-drift ceiling the clock enforces (§3.4 / §10.2). */
37
+ maxDriftMs: number;
38
+ /** Snapshot-cache count ceiling (§8.2). */
39
+ maxCachedSnapshots: number;
40
+ /** Snapshot-cache byte ceiling (§8.2). */
41
+ maxCacheBytes: number;
42
+ }
43
+ export declare class StateSyncConfigError extends Error {
44
+ constructor(message: string);
45
+ }
46
+ /** The minimal shape this module reads from config — the per-store flags are a
47
+ * free-form map (a store's key → { enabled, dryRun }); the foundation knobs are
48
+ * optional siblings. Mirrors the spec §10 schema. */
49
+ export interface StateSyncRawConfig {
50
+ aggregateJournalBudgetBytes?: number;
51
+ maxDriftMs?: number;
52
+ maxCachedSnapshots?: number;
53
+ maxCacheBytes?: number;
54
+ /** Per-store on-switches live as sibling keys; this module ignores their
55
+ * shape beyond the foundation knobs. Stores are typed loosely so a new
56
+ * store added later needs no change here. */
57
+ [store: string]: unknown;
58
+ }
59
+ /** Read the stateSync block off a multiMachine config (may be absent). */
60
+ export declare function readStateSyncRaw(mm?: MultiMachineConfig): StateSyncRawConfig | undefined;
61
+ /**
62
+ * Resolve the foundation-level stateSync knobs to concrete values. Does NOT
63
+ * validate — call validateStateSyncInvariants() after (or use
64
+ * assertStateSyncInvariants() for resolve+validate+throw in one step). The
65
+ * maxDriftMs is CLAMPED here via the clock's clampMaxDriftMs so a resolved value
66
+ * is always in-range; the §10.2 invariant additionally REJECTS an out-of-range
67
+ * RAW value (so an operator sees an error, not a silent clamp) — see
68
+ * validateStateSyncInvariants.
69
+ */
70
+ export declare function resolveStateSyncConfig(mm?: MultiMachineConfig): ResolvedStateSyncConfig;
71
+ /**
72
+ * Validate the foundation-level stateSync invariants (§10.2). Returns the list
73
+ * of violations (empty = valid). Operates on the RAW config (not the resolved
74
+ * one) for the maxDriftMs range check, so an out-of-range value is REJECTED
75
+ * rather than silently clamped — the spec's "rejected, not silently coerced".
76
+ *
77
+ * Invariants (§10.2):
78
+ * 1. aggregateJournalBudgetBytes > 0.
79
+ * 2. maxDriftMs within [60_000, 900_000] (the §3.4 clamp) — out-of-range REJECTED.
80
+ * 3. maxDriftMs > flush interval + propagation allowance (NOT the vacuous
81
+ * "> flush window") — so the bound can never be tighter than the journal's
82
+ * own flush+replicate latency.
83
+ * 4. maxCachedSnapshots > 0 and maxCacheBytes > 0 (§8.2).
84
+ */
85
+ export declare function validateStateSyncInvariants(mm?: MultiMachineConfig): string[];
86
+ /**
87
+ * Resolve + validate in one step, throwing StateSyncConfigError on any invariant
88
+ * violation. Use at server startup so a bad config is rejected loudly rather
89
+ * than degrading silently (mirrors assertSeamlessnessInvariants).
90
+ */
91
+ export declare function assertStateSyncInvariants(mm?: MultiMachineConfig): ResolvedStateSyncConfig;
92
+ /** Re-export the drift-clamp constants for callers/tests that assert the range. */
93
+ export { DEFAULT_MAX_DRIFT_MS, MIN_MAX_DRIFT_MS, MAX_MAX_DRIFT_MS };
94
+ //# sourceMappingURL=stateSyncConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stateSyncConfig.d.ts","sourceRoot":"","sources":["../../src/core/stateSyncConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAEL,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,mDAAmD;AACnD,eAAO,MAAM,sCAAsC,QAAmB,CAAC;AACvE,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAC/C,eAAO,MAAM,uBAAuB,QAAmB,CAAC;AACxD;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC,QAAY,CAAC;AAE1D,8EAA8E;AAC9E,MAAM,WAAW,uBAAuB;IACtC,yEAAyE;IACzE,2BAA2B,EAAE,MAAM,CAAC;IACpC,+EAA+E;IAC/E,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,kBAAkB,EAAE,MAAM,CAAC;IAC3B,0CAA0C;IAC1C,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI5B;AAED;;sDAEsD;AACtD,MAAM,WAAW,kBAAkB;IACjC,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;kDAE8C;IAC9C,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAAC,EAAE,CAAC,EAAE,kBAAkB,GAAG,kBAAkB,GAAG,SAAS,CAGxF;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,CAAC,EAAE,kBAAkB,GAAG,uBAAuB,CAQvF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,2BAA2B,CAAC,EAAE,CAAC,EAAE,kBAAkB,GAAG,MAAM,EAAE,CA2C7E;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,CAAC,EAAE,kBAAkB,GAAG,uBAAuB,CAS1F;AAED,mFAAmF;AACnF,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,CAAC"}