@ibgib/core-gib 0.1.23 → 0.1.25

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 (67) hide show
  1. package/dist/common/other/graph-helper.d.mts +17 -0
  2. package/dist/common/other/graph-helper.d.mts.map +1 -1
  3. package/dist/common/other/graph-helper.mjs +44 -0
  4. package/dist/common/other/graph-helper.mjs.map +1 -1
  5. package/dist/sync/graft-info/graft-info-helpers.mjs +2 -2
  6. package/dist/sync/graft-info/graft-info-helpers.mjs.map +1 -1
  7. package/dist/sync/sync-conflict.respec.mjs +10 -15
  8. package/dist/sync/sync-conflict.respec.mjs.map +1 -1
  9. package/dist/sync/sync-innerspace-constants.respec.mjs +10 -12
  10. package/dist/sync/sync-innerspace-constants.respec.mjs.map +1 -1
  11. package/dist/sync/sync-innerspace-deep-updates.respec.mjs +10 -12
  12. package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
  13. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs +10 -12
  14. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs.map +1 -1
  15. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs +9 -12
  16. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
  17. package/dist/sync/sync-innerspace-partial-update.respec.mjs +9 -14
  18. package/dist/sync/sync-innerspace-partial-update.respec.mjs.map +1 -1
  19. package/dist/sync/sync-innerspace.respec.mjs +9 -12
  20. package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
  21. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.d.mts +2 -0
  22. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.d.mts.map +1 -1
  23. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs +4 -0
  24. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs.map +1 -1
  25. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts +24 -11
  26. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts.map +1 -1
  27. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +58 -44
  28. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
  29. package/dist/sync/sync-peer/sync-peer-types.d.mts +29 -6
  30. package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
  31. package/dist/sync/sync-peer/sync-peer-v1.d.mts +29 -30
  32. package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
  33. package/dist/sync/sync-peer/sync-peer-v1.mjs +105 -198
  34. package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
  35. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +19 -2
  36. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts.map +1 -1
  37. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs +37 -4
  38. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -1
  39. package/dist/sync/sync-saga-coordinator.d.mts +12 -12
  40. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  41. package/dist/sync/sync-saga-coordinator.mjs +281 -218
  42. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  43. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +51 -6
  44. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
  45. package/dist/sync/sync-types.d.mts +36 -9
  46. package/dist/sync/sync-types.d.mts.map +1 -1
  47. package/dist/sync/sync-types.mjs +1 -2
  48. package/dist/sync/sync-types.mjs.map +1 -1
  49. package/package.json +1 -1
  50. package/src/common/other/graph-helper.mts +53 -0
  51. package/src/sync/graft-info/graft-info-helpers.mts +3 -3
  52. package/src/sync/sync-conflict.respec.mts +10 -17
  53. package/src/sync/sync-helpers.mts +6 -6
  54. package/src/sync/sync-innerspace-constants.respec.mts +10 -12
  55. package/src/sync/sync-innerspace-deep-updates.respec.mts +10 -12
  56. package/src/sync/sync-innerspace-dest-ahead.respec.mts +10 -12
  57. package/src/sync/sync-innerspace-multiple-timelines.respec.mts +9 -12
  58. package/src/sync/sync-innerspace-partial-update.respec.mts +9 -14
  59. package/src/sync/sync-innerspace.respec.mts +9 -12
  60. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mts +7 -0
  61. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +75 -50
  62. package/src/sync/sync-peer/sync-peer-types.mts +35 -11
  63. package/src/sync/sync-peer/sync-peer-v1.mts +136 -196
  64. package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +37 -8
  65. package/src/sync/sync-saga-coordinator.mts +318 -260
  66. package/src/sync/sync-saga-message/sync-saga-message-types.mts +56 -4
  67. package/src/sync/sync-types.mts +46 -13
@@ -23,14 +23,18 @@ import { SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME, SYNC_SAGA_PAYLOAD_ADDRS_DOMA
23
23
  import { appendToTimeline, createTimeline, getHistory, Rel8nInfo, Rel8nRemovalInfo } from "../timeline/timeline-api.mjs";
24
24
  import {
25
25
  SyncData_V1, SyncIbGib_V1, SyncConflictStrategy, SyncMode, SyncOptions,
26
- SyncRel8ns_V1, DomainIbGibAnalysisInfo, HandleSagaFrameResult,
26
+ SyncRel8ns_V1, DomainIbGibAnalysisInfo, NextSagaFrameInfo,
27
+ SYNC_CONFLICT_STRATEGY_VALID_VALUES,
28
+ HandleSagaResponseContextResult,
27
29
  } from "./sync-types.mjs";
28
30
  import { getSyncIb, getTempSpaceName, isPastFrame } from "./sync-helpers.mjs";
29
- import { getDependencyGraph, toFlatGraph } from "../common/other/graph-helper.mjs";
31
+ import { getDeltaDependencyGraph, getDependencyGraph, toFlatGraph } from "../common/other/graph-helper.mjs";
30
32
  import {
31
33
  SyncSagaMessageData_V1, SyncSagaMessageInitData_V1,
32
34
  SyncSagaMessageAckData_V1, SyncSagaMessageDeltaData_V1,
33
35
  SyncSagaMessageCommitData_V1, SyncSagaConflictInfo,
36
+ SyncSagaPushOfferInfo,
37
+ SyncSagaRequestAddrInfo,
34
38
  } from "./sync-saga-message/sync-saga-message-types.mjs";
35
39
  import { getSyncSagaMessageIb } from "./sync-saga-message/sync-saga-message-helpers.mjs";
36
40
  import { SYNC_SAGA_MSG_ATOM } from "./sync-saga-message/sync-saga-message-constants.mjs";
@@ -38,7 +42,7 @@ import { SyncSagaInfo } from "./sync-types.mjs";
38
42
  import { splitPerTjpAndOrDna, getTimelinesGroupedByTjp, isIbGib } from "../common/other/ibgib-helper.mjs";
39
43
  import { SyncPeerWitness } from "./sync-peer/sync-peer-types.mjs";
40
44
  import { SyncSagaContextIbGib_V1, } from "./sync-saga-context/sync-saga-context-types.mjs";
41
- import { createSyncSagaContext } from "./sync-saga-context/sync-saga-context-helpers.mjs";
45
+ import { createSyncSagaContext, validateContextAndSagaFrame } from "./sync-saga-context/sync-saga-context-helpers.mjs";
42
46
  import { newupSubject, } from "../common/pubsub/subject/subject-helper.mjs";
43
47
  import { SubjectWitness } from "../common/pubsub/subject/subject-types.mjs";
44
48
  import { mergeDivergentTimelines } from "./strategies/conflict-optimistic.mjs";
@@ -58,14 +62,14 @@ const lcControlDomain = '[ControlDomain]';
58
62
 
59
63
  /**
60
64
  * Orchestrates the synchronization process between two spaces (Source and Destination).
61
- *
65
+ *
62
66
  * ## Execution Contexts
63
- *
67
+ *
64
68
  * The methods in this class are designed to be run on specific nodes in the sync topology:
65
- *
69
+ *
66
70
  * * **Sender/Local Node**: The node *initiating* the sync (calling `sync()`).
67
71
  * * **Receiver/Remote Node**: The node *accepting* the sync request (the Peer).
68
- *
72
+ *
69
73
  * Note that in a peer-to-peer architecture, "Sender" and "Receiver" are roles relative
70
74
  * to a specific Saga session, not fixed node identities.
71
75
  */
@@ -80,11 +84,11 @@ export class SyncSagaCoordinator {
80
84
 
81
85
  /**
82
86
  * Executes a synchronization saga using the Symmetric Sync Protocol.
83
- *
87
+ *
84
88
  * @remarks
85
89
  * **Execution Context**: **Sender (Local)**.
86
90
  * This method is the entry point for starting a sync session.
87
- *
91
+ *
88
92
  * @param opts.peer - The remote peer witness to communicate with.
89
93
  * @param opts.localSpace - The local space that will be read from and written to.
90
94
  * @param opts.metaspace - Service for creating temp spaces and managing ibgibs.
@@ -140,14 +144,14 @@ export class SyncSagaCoordinator {
140
144
  (async () => {
141
145
  try {
142
146
 
143
- // 2. BOOTSTRAP IDENTITY (Session Keystone)
147
+ // BOOTSTRAP IDENTITY (Session Keystone)
144
148
  const sessionIdentity = useSessionIdentity
145
149
  ? await this.getSessionIdentity({ sagaId, metaspace, tempSpace })
146
150
  : undefined;
147
151
  // if (logalot) { console.log(`${lc} sessionIdentity: ${sessionIdentity ? pretty(sessionIdentity) : 'undefined'} (I: abc01872800b3a66b819a05898bba826)`); }
148
152
 
149
- // 3. CREATE INITIAL FRAME (Stage.init)
150
- const { sagaFrame: initFrame, initialDomainGraph } = await this.createInitFrame({
153
+ // CREATE INITIAL FRAME (Stage.init)
154
+ const { initFrame, initDomainGraph } = await this.createInitFrame({
151
155
  sagaId,
152
156
  sessionIdentity,
153
157
  domainIbGibs,
@@ -155,17 +159,16 @@ export class SyncSagaCoordinator {
155
159
  metaspace, localSpace, tempSpace,
156
160
  });
157
161
 
158
- // 4. KICK OFF THE PING-PONG SAGA LOOP (FSM)
159
- // commented to compile
160
- // await this.executeSagaLoop({
161
- // initialFrame: initFrame,
162
- // peer,
163
- // sessionIdentity,
164
- // updates$,
165
- // localSpace,
166
- // tempSpace,
167
- // metaspace
168
- // });
162
+ // KICK OFF THE PING-PONG SAGA LOOP (FSM)
163
+ await this.executeSagaLoop({
164
+ initFrame, initDomainGraph,
165
+ peer,
166
+ sessionIdentity,
167
+ updates$,
168
+ localSpace,
169
+ tempSpace,
170
+ metaspace
171
+ });
169
172
 
170
173
  resolveDone();
171
174
  if (!updates$.complete) { throw new Error(`(UNEXPECTED) updates$.complete falsy? (E: d24cd82184aec130c89a320819b39126)`); }
@@ -226,13 +229,13 @@ export class SyncSagaCoordinator {
226
229
 
227
230
  /**
228
231
  * Drives the FSM loop of the Saga.
229
- *
232
+ *
230
233
  * @remarks
231
234
  * **Execution Context**: **Sender (Local)**.
232
- *
235
+ *
233
236
  * This method manages the "Ping Pong" request-response cycle on the Sender.
234
237
  * It sends frames via the Peer Witness and processes the responses using `handleSagaResponseContext`.
235
- *
238
+ *
236
239
  * **Data Transport Note**:
237
240
  * Actual ibGib data (payloads) are transported via `SyncSagaContext.rel8ns.payload`.
238
241
  * When `handleSagaResponseContext` returns a `nextPayloadIbGibs` (data to send), this loop injects it into
@@ -240,8 +243,8 @@ export class SyncSagaCoordinator {
240
243
  * When the Peer responds with data (in the response context), it is resolved and put into `tempSpace`.
241
244
  */
242
245
  protected async executeSagaLoop({
243
- initialFrame,
244
- initialDomainGraph,
246
+ initFrame,
247
+ initDomainGraph,
245
248
  peer,
246
249
  sessionIdentity,
247
250
  updates$,
@@ -249,8 +252,8 @@ export class SyncSagaCoordinator {
249
252
  tempSpace,
250
253
  metaspace
251
254
  }: {
252
- initialFrame: SyncIbGib_V1,
253
- initialDomainGraph: FlatIbGibGraph,
255
+ initFrame: SyncIbGib_V1,
256
+ initDomainGraph: FlatIbGibGraph,
254
257
  peer: SyncPeerWitness,
255
258
  sessionIdentity?: KeystoneIbGib_V1,
256
259
  updates$: SubjectWitness<SyncSagaContextIbGib_V1>,
@@ -260,44 +263,23 @@ export class SyncSagaCoordinator {
260
263
  }): Promise<void> {
261
264
  const lc = `${this.lc}[${this.executeSagaLoop.name}]`;
262
265
 
263
- // The current frame we just generated (e.g., Init or Delta Request)
264
- let currentFrame: SyncIbGib_V1 | null = initialFrame;
265
- // The payload we need to attach to the message (data we are sending)
266
+ /** The current frame we just generated (e.g., Init or Delta Request) */
267
+ let currentFrame: SyncIbGib_V1 | null = initFrame;
268
+ /** The **domain** payload we need to transmit (data we are sending) */
266
269
  let nextDomainIbGibs: IbGib_V1[] = [];
267
270
 
268
- // First, inject local/tempSpace into peer so it can pull as
269
- // needed...code smell?
270
- peer.senderSpace = localSpace;
271
- peer.senderTempSpace = tempSpace;
272
-
273
271
  while (currentFrame) {
274
- // A. Create Context (Request)
275
- // 1. Calculate Full Dependency Graph (including Ancestors/DNA)
276
- // We must do this BEFORE creating the Context so we can list them
277
- // all in payloadAddrsDomain and payloadAddrsControl.
278
- const nextDomainIbGibsAndDeps: IbGib_V1[] = [];
279
-
280
- // A. Payload (Standard Deep Deps)
281
- // TODO: THIS IS EXTREMELY INEFFICIENT. adjust this algorithm to
282
- // only do the dependencies that the other end doesn't need (diff
283
- // between tip and LCA). This can be done using the information in
284
- // the ack's conflicts array.
285
- for (const nextDomainIbGib of nextDomainIbGibs) {
286
- let nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: localSpace });
287
- if (!nextDomainIbGibGraph) {
288
- nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: tempSpace });
289
- }
290
- if (nextDomainIbGibGraph) {
291
- nextDomainIbGibsAndDeps.push(...Object.values(nextDomainIbGibGraph));
292
- } else {
293
- throw new Error(`(UNEXPECTED) we couldn't get the graph for a known domain ibgib? nextDomainIbGib addr: ${getIbGibAddr({ ibGib: nextDomainIbGib })} (E: 01b3e4db8768b5b77db72e486f4f7826)`);
294
- }
295
- }
296
- if (logalot) { console.log(`${lc} payloadIbGibsDomain count: ${nextDomainIbGibsAndDeps.length} (I: 2beda8ca7dc5ac0f48ed9e25e704b826)`); }
272
+ // first, prepare for context transmission...
297
273
 
298
- // 2. Create Context (Envelope)
299
- // set up subscription for response's payload ibgibs (if any)
274
+ /**
275
+ * this is used later in pollForDomainPayloads **iif** payloads are
276
+ * received **in the response**, i.e., on the return trip. So if we
277
+ * request addrs, the response should have these addrs and their
278
+ * dependencies. The ibgibs corresponding to these addrs will be
279
+ * streamed and placed into this map.
280
+ */
300
281
  const domainPayloadsMap = new Map<string, IbGib_V1>();
282
+ // #region set up peer observable for any domainPayloadsMap
301
283
  const sublc = `${lc}[peer.payloadIbGibsDomainReceived$]`;
302
284
  const subscription = await peer.payloadIbGibsDomainReceived$.subscribe(fnObs({
303
285
  next: async (ibgib: IbGib_V1) => {
@@ -319,43 +301,49 @@ export class SyncSagaCoordinator {
319
301
  await subscription.unsubscribe();
320
302
  },
321
303
  }));
304
+ // #endregion set up peer observable for any domainPayloadsMap
322
305
 
323
- // Create the Request Context...
306
+ // ...create/compose the Request Context itself...
324
307
  const requestCtx = await createSyncSagaContext({
325
308
  sagaFrame: currentFrame,
326
309
  sessionKeystones: sessionIdentity ? [sessionIdentity] : undefined,
327
-
328
310
  /**
329
311
  * init frame: empty
330
- *
312
+ * ack frame: possible push offers
313
+ * delta frame: requested ibgibs or commit offer
314
+ * commit frame: empty
331
315
  */
332
- payloadIbGibsDomain: nextDomainIbGibsAndDeps,
316
+ payloadIbGibsDomain: nextDomainIbGibs,
333
317
  localSpace,
334
318
  });
335
319
 
336
- // Log what we're sending
320
+ // #region Log what we're sending
337
321
  if (logalotControlDomain) {
338
- const domainAddrs = nextDomainIbGibsAndDeps.map(p => getIbGibAddr({ ibGib: p }));
339
- console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I: b3c4d5e6f7a8b9c0)`);
322
+ const domainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
323
+ console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I: 5b0081803698770f0bf64992220b312)`);
340
324
  console.log(`${lc}${lcControlDomain} Context: ${getIbGibAddr({ ibGib: requestCtx })}`);
341
325
  console.log(`${lc}${lcControlDomain} Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
342
326
  console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${domainAddrs.length}): ${domainAddrs.join(', ') || '(none)'}`);
343
327
  }
328
+ // #endregion Log what we're sending
344
329
 
345
330
  // update our saga listeners...
346
331
  // if (logalot) { console.log(`${lc} transmitting... requestCtx: ${pretty(requestCtx)} (I: 8cf20817c66899abdb1e76df50356826)`); }
347
- updates$.next(requestCtx); // spins off
332
+ updates$.next(requestCtx); // spins off for saga UI updates
348
333
 
349
- // ...And send the context
334
+ // ...And send the context.
335
+ peer.setOptionalOpts({ senderSpace: localSpace, senderTempSpace: tempSpace, });
350
336
  const responseCtx = await peer.witness(requestCtx);
351
337
 
352
- // C. Handle Response
338
+ // the send returned, but a peer can return a falsy responseCtx, if
339
+ // we just sent a commit to them and they're done. If so, there will
340
+ // necessarily be no payload to wait to receive
353
341
  if (!responseCtx) {
354
342
  if (currentFrame) {
355
343
  // Check for Commit (Peer silence expected)
356
344
  const msg = await getSyncSagaMessageFromFrame({ frameIbGib: currentFrame, space: localSpace });
357
345
  if (msg?.data?.stage === SyncStage.commit) {
358
- if (logalot) { console.log(`${lc} Sender sent Commit. Peer returned no response. Saga Complete.`); }
346
+ if (logalot) { console.log(`${lc} Sender sent Commit. Peer returned no response. Saga Complete. (I: 26f9ee073858ca78b8284753368b5226)`); }
359
347
  currentFrame = null;
360
348
  break;
361
349
  } else {
@@ -366,19 +354,21 @@ export class SyncSagaCoordinator {
366
354
  }
367
355
  }
368
356
 
369
- // ---------------------------------------------------------------------
370
- // 2d. HANDLE RESPONSE
371
- // ---------------------------------------------------------------------
372
- // at this point, we have received the response context ibgib, but
357
+ // at this point, we did indeed receive a response to analyze. BUT!
358
+ // we have only necessarily received the response context ibgib.
373
359
  // if there were payloads expected to be transferred, they may not
374
360
  // be done yet.
361
+
375
362
  if (!responseCtx.data) { throw new Error(`(UNEXPECTED) responseCtx.data falsy? (E: a969992bae53ab18a827ec58aec15826)`); }
376
- updates$.next(responseCtx); // spins off -- don't remove this comment!
363
+ updates$.next(responseCtx); // spins off for saga UI updating
364
+
365
+ // validate context
366
+ await validateContextAndSagaFrame
377
367
 
378
368
  // Extract expected domain addresses from response context
379
369
  const responsePayloadAddrsDomain = responseCtx.data[SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN] as string[] || [];
380
370
 
381
- // Poll for them if needed
371
+ // Poll for them if needed. see above jsdocs for domainPayloadsMap
382
372
  if (responsePayloadAddrsDomain.length > 0) {
383
373
  responseCtx.payloadIbGibsDomain = await this.pollForDomainPayloads({
384
374
  expectedAddrs: responsePayloadAddrsDomain,
@@ -388,51 +378,61 @@ export class SyncSagaCoordinator {
388
378
  });
389
379
  }
390
380
 
391
- // at this point, we have received the context AND **all** of the
392
- // domain payloads (if applicable), so we are ready to do the next
393
- // iteration in the saga loop
394
-
395
- // Log what we received back
381
+ // #region Log what we received back
396
382
  if (!responseCtx.sagaFrame) { throw new Error(`(UNEXPECTED) responseCtx.sagaFrame falsy? the Peer should have set this when it got the response back from the remote. (E: e650adadf9a2063ec6764a1e31d3d826)`); }
397
383
  if (logalotControlDomain) {
398
384
  const responseControlAddrs = responseCtx.data?.['@payloadAddrsControl'] as string[] || [];
399
- console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I: c4d5e6f7a8b9c0d1)`);
385
+ console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I: 3dc76a9744d89a4fc3e2f076c2be4826)`);
400
386
  console.log(`${lc}${lcControlDomain} Response Context: ${getIbGibAddr({ ibGib: responseCtx })}`);
401
387
  console.log(`${lc}${lcControlDomain} Response Saga Frame: ${getIbGibAddr({ ibGib: responseCtx.sagaFrame })}`);
402
388
  console.log(`${lc}${lcControlDomain} CONTROL Payloads (${responseControlAddrs.length}): ${responseControlAddrs.join(', ') || '(none)'}`);
403
389
  console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${responsePayloadAddrsDomain.length}): ${responsePayloadAddrsDomain.join(', ') || '(none)'}`);
404
390
  }
391
+ // #endregion Log what we received back
405
392
 
406
- // Handle Response Frame
407
- // interface HandleSagaFrameResult {
408
- // frame: SyncIbGib_V1;
409
- // payloadIbGibsDomain?: IbGib_V1[];
410
- // conflictInfos?: SyncSagaConflictInfo;
411
- // }
412
- const handleResult = await this.handleSagaContext({
393
+ // at this point, we have received the context AND **all** of the
394
+ // domain payloads (if applicable), so we are ready to do the next
395
+ // iteration in the saga loop
396
+
397
+ // this is the part that drives the FSM forward, i.e., when we
398
+ // evolve the sync saga ibgib itself to the next frame if we aren't
399
+ // finished/errored out.
400
+ const contextResult = await this.handleResponseSagaContext({
413
401
  sagaContext: responseCtx,
414
402
  mySpace: localSpace,
415
403
  myTempSpace: tempSpace,
416
404
  metaspace,
417
405
  });
418
406
 
419
- if (!handleResult) {
420
- if (logalot) { console.log(`${lc} Handler returned null (Saga End).`); }
407
+ if (!contextResult) {
408
+ if (logalot) { console.log(`${lc} Handler returned null (Saga End). (I: faae22abc818ba9b28ac6d2881cd7826)`); }
421
409
  break;
422
410
  }
423
411
 
424
- currentFrame = handleResult.frame;
412
+ // #region error conditions throw
413
+ if (contextResult.errorMsg) {
414
+ throw new Error(`Couldn't handle response saga context. errorMsg: ${contextResult.errorMsg} (E: c948e81d513b2a0eb8b8afa878edc626)`);
415
+ } else if (!contextResult.nextFrameInfo) {
416
+ throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo falsy? (E: c287a82e823e662a77923278e2418826)`);
417
+ } else if (contextResult.nextFrameInfo?.responseWasNull) {
418
+ throw new Error(`(UNEXPECTED) contextResult.nextFrameInfo.responseWasNull? logic flow should not have gotten here. (E: 104a32381db816b7183435e805b3d626)`);
419
+ }
420
+ // #endregion error conditions throw
425
421
 
426
- // Collect next DOMAIN payloads for the NEXT request
427
- nextDomainIbGibs = [...(handleResult.payloadIbGibsDomain || [])];
422
+ // we have another frame to process and send out, with possibly
423
+ // payload domain ibgibs as well
424
+ const { frame, payloadIbGibsDomain, } = contextResult.nextFrameInfo;
425
+ currentFrame = frame;
426
+ nextDomainIbGibs = [...(payloadIbGibsDomain || [])];
428
427
 
429
- // Log handler output for next iteration
428
+ // #region Log handler output for next iteration
430
429
  if (logalotControlDomain) {
431
430
  const handlerDomainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
432
- console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I: d5e6f7a8b9c0d1e2)`);
431
+ console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I: 6b0d88c4c28857ccd812381515bd7826)`);
433
432
  console.log(`${lc}${lcControlDomain} Next Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
434
433
  console.log(`${lc}${lcControlDomain} DOMAIN for next (${handlerDomainAddrs.length}): ${handlerDomainAddrs.join(', ') || '(none)'}`);
435
434
  }
435
+ // #endregion Log handler output for next iteration
436
436
  }
437
437
  }
438
438
 
@@ -499,7 +499,7 @@ export class SyncSagaCoordinator {
499
499
 
500
500
  const res = await getLatestAddrs({ space, tjpAddrs: tjps });
501
501
  if (!res.data || !res.data.latestAddrsMap) {
502
- throw new Error(`${lc} Failed to get latest addrs. (E: 7a8b9c0d)`);
502
+ throw new Error(`${lc} Failed to get latest addrs. (E: 7d395940c0e1419165c5196c39c6c826)`);
503
503
  }
504
504
 
505
505
  // if (false) { console.log(`${lc}[TEST DEBUG] res.data.latestAddrsMap: ${JSON.stringify(res.data.latestAddrsMap)} (I: a8e128bdf80898ac2e6d8021a5bff726)`); }
@@ -552,10 +552,10 @@ export class SyncSagaCoordinator {
552
552
 
553
553
  /**
554
554
  * Creates the Initial Saga Frame (Init Stage).
555
- *
555
+ *
556
556
  * @remarks
557
557
  * **Execution Context**: **Sender (Local)**.
558
- *
558
+ *
559
559
  * Generates the first frame containing the Knowledge Vector of the Local Space.
560
560
  * This is sent to the Receiver to begin Gap Analysis.
561
561
  */
@@ -575,14 +575,14 @@ export class SyncSagaCoordinator {
575
575
  metaspace: MetaspaceService,
576
576
  localSpace: IbGibSpaceAny,
577
577
  tempSpace: IbGibSpaceAny,
578
- }): Promise<{ sagaFrame: SyncIbGib_V1, initialDomainGraph: { [addr: string]: IbGib_V1 } }> {
578
+ }): Promise<{ initFrame: SyncIbGib_V1, initDomainGraph: { [addr: string]: IbGib_V1 } }> {
579
579
  const lc = `${this.lc}[${this.createInitFrame.name}]`;
580
580
  try {
581
581
  if (logalot) { console.log(`${lc} starting... (I: 551af8b411ae9be712ce3358d43ee726)`); }
582
582
 
583
583
  // Analyze Timelines & Stones
584
584
  const analysis = await this.analyzeDomainIbGibs({ domainIbGibs, space: localSpace });
585
- // if (logalot) { console.log(`${lc} analysis: ${pretty(analysis)}(I: cd00e2be5eccc8976879c888ff2dfb26)`); }
585
+ // if (logalot) { console.log(`${lc} analysis: ${pretty(analysis)}(I: cd00e2be5eccc8976879c888ff2dfb26)`); }
586
586
  const { timelinesMap: srcTimelinesMap, fullGraph, stones: srcStones, } = analysis;
587
587
 
588
588
  // we need to store the fullGraph in our tempSpace for later...
@@ -627,7 +627,7 @@ export class SyncSagaCoordinator {
627
627
 
628
628
  // if (logalot) { console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`); }
629
629
 
630
- return { sagaFrame, initialDomainGraph: fullGraph };
630
+ return { initFrame: sagaFrame, initDomainGraph: fullGraph };
631
631
  } catch (error) {
632
632
  console.error(`${lc} ${extractErrorMsg(error)}`);
633
633
  throw error;
@@ -639,7 +639,7 @@ export class SyncSagaCoordinator {
639
639
  /**
640
640
  * Helper to poll for streaming domain payloads and put them in the
641
641
  * local {@link tempSpace}.
642
- *
642
+ *
643
643
  * @returns when all {@link expectedAddrs} are done being transmitted.
644
644
  */
645
645
  protected async pollForDomainPayloads({
@@ -662,7 +662,7 @@ export class SyncSagaCoordinator {
662
662
  let pending = [...expectedAddrs];
663
663
  const start = Date.now();
664
664
  /**
665
- * This needs
665
+ * This needs
666
666
  */
667
667
  const timeoutMs = 5 * 60 * 1000; // 5 minutes...arbitrary at this point. This needs to be pulled out and improved eesh.
668
668
 
@@ -710,23 +710,23 @@ export class SyncSagaCoordinator {
710
710
  * This is the heart of the "ping pong" transaction, where we send a context
711
711
  * and receive a context. IOW, this drives the FSM of the sync saga ibgib as
712
712
  * a whole.
713
- *
713
+ *
714
714
  * This is called in two places:
715
- *
715
+ *
716
716
  * ## 1. Sender
717
- *
717
+ *
718
718
  * On the sender, this is called within the {@link executeSagaLoop} which
719
- * initiates and drives the sync.
720
- *
719
+ * initiates and drives the sync.
720
+ *
721
721
  * ## 2. Receiver
722
- *
722
+ *
723
723
  * On the receiver, this is called directly by the receiving endpoint. That
724
- * endpoint's job is basically to get these things collocated and prepared
724
+ * endpoint's job is basically to get these things collocated and prepared
725
725
  * to make this call.
726
- *
726
+ *
727
727
  * This is a one-off on the receiver.
728
728
  */
729
- public async handleSagaContext({
729
+ public async handleResponseSagaContext({
730
730
  sagaContext,
731
731
  mySpace,
732
732
  myTempSpace,
@@ -746,8 +746,8 @@ export class SyncSagaCoordinator {
746
746
  identity?: KeystoneIbGib_V1,
747
747
  identitySecret?: string,
748
748
  metaspace: MetaspaceService,
749
- }): Promise<HandleSagaFrameResult | null> {
750
- const lc = `${this.lc}[${this.handleSagaContext.name}]`;
749
+ }): Promise<HandleSagaResponseContextResult | null> {
750
+ const lc = `${this.lc}[${this.handleResponseSagaContext.name}]`;
751
751
  try {
752
752
  if (logalot) { console.log(`${lc} starting... (I: 5deec8a1f7a6d263c88cd458ad990826)`); }
753
753
 
@@ -765,9 +765,10 @@ export class SyncSagaCoordinator {
765
765
  */
766
766
  const srcGraph = toFlatGraph({ ibGibs: sagaContext.payloadIbGibsDomain }) ?? {};
767
767
 
768
+ let nextFrameInfo: NextSagaFrameInfo;
768
769
  switch (stage) {
769
770
  case SyncStage.init:
770
- return await this.handleInitFrame({
771
+ nextFrameInfo = await this.handleInitFrame({
771
772
  sagaIbGib,
772
773
  messageData: messageData as SyncSagaMessageInitData_V1,
773
774
  metaspace,
@@ -776,22 +777,30 @@ export class SyncSagaCoordinator {
776
777
  identity,
777
778
  identitySecret
778
779
  });
780
+ break;
779
781
 
780
782
  case SyncStage.ack:
781
- return await this.handleAckFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity });
783
+ nextFrameInfo = await this.handleAckFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity });
784
+ break;
782
785
 
783
786
  case SyncStage.delta:
784
- return await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
787
+ nextFrameInfo = await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
788
+ break;
785
789
 
786
790
  case SyncStage.commit:
787
- return await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
791
+ nextFrameInfo = await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
792
+ break;
788
793
 
789
794
  default:
790
795
  throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
791
796
  }
797
+
798
+ return { errorMsg: undefined, nextFrameInfo, }
799
+
792
800
  } catch (error) {
793
- console.error(`${lc} ${extractErrorMsg(error)}`);
794
- throw error;
801
+ const errorMsg = `${lc} ${extractErrorMsg(error)}`;
802
+ console.error(errorMsg);
803
+ return { errorMsg }
795
804
  } finally {
796
805
  if (logalot) { console.log(`${lc} complete.`); }
797
806
  }
@@ -801,10 +810,10 @@ export class SyncSagaCoordinator {
801
810
 
802
811
  /**
803
812
  * Handles the `Init` frame.
804
- *
813
+ *
805
814
  * @remarks
806
815
  * **Execution Context**: **Receiver (Remote)**.
807
- *
816
+ *
808
817
  * The Receiver performs Gap Analysis here:
809
818
  * 1. Compares Sender's Knowledge Vector (in `sagaIbGib`) vs Receiver's Local KV.
810
819
  * 2. Identifies what Sender needs (`pushOfferAddrs`).
@@ -828,7 +837,7 @@ export class SyncSagaCoordinator {
828
837
  mySpace: IbGibSpaceAny,
829
838
  /**
830
839
  * Local temp space relative to the execution context's POV.
831
- *
840
+ *
832
841
  * NOTE: Since this always executes on the receiver's end, this should
833
842
  * be the receiver's temp space.
834
843
  */
@@ -836,18 +845,17 @@ export class SyncSagaCoordinator {
836
845
  metaspace: MetaspaceService,
837
846
  identity?: KeystoneIbGib_V1,
838
847
  identitySecret?: string,
839
- }): Promise<HandleSagaFrameResult | null> {
848
+ }): Promise<NextSagaFrameInfo> {
840
849
  const lc = `${this.lc}[${this.handleInitFrame.name}]`;
841
850
  try {
842
851
  if (logalot) { console.log(`${lc} starting... (I: 9d88dcad0408c029e898a4bcf3b08426)`); }
843
852
 
844
- console.log(`${lc} [TEST DEBUG] Received destSpace: ${mySpace.data?.name || mySpace.ib} (uuid: ${mySpace.data?.uuid || '[no uuid]'})`);
845
- if (logalot) { console.log(`${lc} starting...`); }
853
+ console.log(`${lc} [TEST DEBUG] Receiver mySpace: ${mySpace.ib}`);
846
854
 
847
855
  // Extract Init Data
848
856
  const initData = messageData as SyncSagaMessageInitData_V1; // Using renamed variable for clarity
849
857
  if (initData.stage !== SyncStage.init) {
850
- throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E: 8a2b3c4d5e6f7g8h)`);
858
+ throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E: c91be82970e4decc58f56bf8fc1ffc26)`);
851
859
  }
852
860
  // if (logalot) { console.log(`${lc} initData: ${pretty(initData)} (I: 46b0f8441b96ad7a388f1ce3239dd826)`); }
853
861
  if (!initData || !initData.knowledgeVector) {
@@ -859,8 +867,8 @@ export class SyncSagaCoordinator {
859
867
 
860
868
  // 2. Gap Analysis
861
869
  const conflicts: SyncSagaConflictInfo[] = [];
862
- const deltaReqAddrs: string[] = [];
863
- const pushOfferAddrs: string[] = [];
870
+ const deltaRequestAddrInfos: SyncSagaRequestAddrInfo[] = [];
871
+ const pushOfferInfos: SyncSagaPushOfferInfo[] = [];
864
872
 
865
873
  // First do the consant stones (Non-TJPs)
866
874
  const stones = initData.stones || [];
@@ -874,13 +882,15 @@ export class SyncSagaCoordinator {
874
882
  if (addrsNotFound && addrsNotFound.length > 0) {
875
883
  if (logalot) { console.log(`${lc} stones missing (requesting): ${addrsNotFound.length}`); }
876
884
  addrsNotFound.forEach(addr => {
877
- if (!deltaReqAddrs.includes(addr)) { deltaReqAddrs.push(addr); }
885
+ if (!deltaRequestAddrInfos.some(x => x.addr === addr)) {
886
+ deltaRequestAddrInfos.push({ addr }); // no tjpAddr
887
+ }
878
888
  });
879
889
  }
880
890
  }
881
891
 
882
892
  /**
883
- * "remote" local to receiver's context is the sender
893
+ * "remote" is sender in this case
884
894
  */
885
895
  const remoteKV = initData.knowledgeVector;
886
896
  if (logalot) { console.log(`${lc} remoteKV: ${pretty(remoteKV)} (I: 9f957862356dfeae183c200854e86e26)`); }
@@ -889,7 +899,7 @@ export class SyncSagaCoordinator {
889
899
  if (logalot) { console.log(`${lc} remoteTjps: ${pretty(remoteTjps)} (I: 86ea4c53db0dc184c8b253386c402126)`); }
890
900
 
891
901
  // 1. Get Local Latest Addrs for all TJPs
892
- let localKV: { [tjp: string]: string | null } = {};
902
+ let localLatestAddrsMap: { [tjp: string]: string | null } = {};
893
903
  if (remoteTjps.length > 0) {
894
904
  // Batch get latest addrs for the TJPs
895
905
  const resGetLatestAddrs = await getLatestAddrs({
@@ -898,56 +908,91 @@ export class SyncSagaCoordinator {
898
908
  });
899
909
  if (!resGetLatestAddrs.data) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data falsy? (E: b180d813c088042b38e1e02e06a16926)`); }
900
910
  if (!resGetLatestAddrs.data.latestAddrsMap) { throw new Error(`(UNEXPECTED) resGetLatestAddrs.data.latestAddrsMap falsy? (E: 16bc386dd51d0ff53a49620b1e641826)`); }
901
- localKV = resGetLatestAddrs.data.latestAddrsMap;
902
- console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(localKV)}`);
903
- if (logalot) { console.log(`${lc} localKV: ${pretty(localKV)} (I: 980975642cbccd8018cf0cd808d30826)`); }
911
+ localLatestAddrsMap = resGetLatestAddrs.data.latestAddrsMap;
912
+ console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(localLatestAddrsMap)}`);
913
+ if (logalot) { console.log(`${lc} localKV: ${pretty(localLatestAddrsMap)} (I: 980975642cbccd8018cf0cd808d30826)`); }
904
914
  }
905
915
 
906
916
  // 2. Gap Analysis
907
917
  for (const tjp of remoteTjps) {
908
918
  const remoteAddr = remoteKV[tjp];
909
- const localAddr = localKV[tjp];
919
+ const localAddr = localLatestAddrsMap[tjp];
910
920
 
911
921
  if (!localAddr) {
912
922
  // We (Receiver) don't have this timeline at all. Request it.
913
923
  console.log(`${lc} [TEST DEBUG] Missing local timeline for TJP: ${tjp}. Requesting remoteAddr: ${remoteAddr}`);
914
- deltaReqAddrs.push(remoteAddr);
924
+ deltaRequestAddrInfos.push({
925
+ addr: remoteAddr,
926
+ tjpAddr: tjp,
927
+ // we don't have this timeline at all
928
+ // latestAddrAlreadyHave: undefined
929
+ });
915
930
  continue;
916
931
  }
917
932
 
933
+ // we do have this timeline...
934
+
918
935
  if (localAddr === remoteAddr) {
919
- // Synced
936
+ // ...already synced
920
937
  console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Synced (localAddr === remoteAddr)`);
921
938
  continue;
922
939
  }
923
940
  console.log(`${lc} [TEST DEBUG] TJP ${tjp}: localAddr=${localAddr}, remoteAddr=${remoteAddr} - checking for divergence...`);
924
941
 
925
- // Check if Remote is in Local's PAST (Local is Ahead -> Push Offer)
926
- // (Sender has older version, Receiver has newer) -> Receiver Offers Push
927
- const isRemoteInPast = await isPastFrame({
942
+ // we have this timeline but it's not synced...
943
+
944
+ // We're executing on receiver. Check if Remote (Sender) is in
945
+ // our past, and if so, we are ahead and need to push the delta
946
+ // to remote
947
+ const remoteIsInPast = await isPastFrame({
928
948
  olderAddr: remoteAddr,
929
949
  newerAddr: localAddr,
930
950
  space: mySpace,
931
951
  });
932
952
 
933
- if (isRemoteInPast) {
934
- console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote is in past - offering push`);
935
- pushOfferAddrs.push(localAddr);
953
+ if (remoteIsInPast) {
954
+ // we're ahead, so push the delta of what the sender doesn't
955
+ // have (we have full knowledge)
956
+ console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote (sender) is in past - offering push`);
957
+ const deltaGraph = await getDeltaDependencyGraph({
958
+ ibGibAddr: localAddr,
959
+ live: false, // always live: false right?
960
+ latestCommonFrameAddr: remoteAddr,
961
+ space: mySpace,
962
+ });
963
+ pushOfferInfos.push({ tjpAddr: tjp, addrs: Object.keys(deltaGraph), });
936
964
  } else {
937
- // Remote is not in our past.
938
- // Either Remote is ahead (Fast-Backward) OR Diverged.
965
+ // Remote tip is not in our past. So, either Remote is
966
+ // ahead (we're in THEIR past, i.e., Fast-Backward) OR
967
+ // Diverged/conflict (we've both made edits).
939
968
 
940
969
  // Check if Local is in Remote's PAST (Remote is Ahead -> Delta Request)
941
- const isLocalInPast = await isPastFrame({
942
- olderAddr: localAddr,
943
- newerAddr: remoteAddr,
944
- space: mySpace,
945
- });
970
+ /**
971
+ * we could first check for existence of remoteAddr in
972
+ * mySpace, but this is quick and easy. If it throws, we
973
+ * don't have it so it can't be in the past.
974
+ */
975
+ let localIsInPast = false;
976
+ try {
977
+ // we could
978
+ localIsInPast = await isPastFrame({
979
+ olderAddr: localAddr,
980
+ newerAddr: remoteAddr,
981
+ space: mySpace,
982
+ });
983
+ } catch (error) {
984
+ // couldn't get one of them, so localIsInPast remains false.
985
+ if (logalot) { console.log(`${lc} expected error if we don't have remote. verbose logging error though: ${extractErrorMsg(error)} (I: 47ea08d0b418cf4aa8a502a7bcb80826)`); }
986
+ }
946
987
 
947
- if (isLocalInPast) {
988
+ if (localIsInPast) {
948
989
  // Fast-Forward: We update to remote's tip.
949
990
  console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Local is in past - requesting delta`);
950
- deltaReqAddrs.push(remoteAddr);
991
+ deltaRequestAddrInfos.push({
992
+ addr: remoteAddr,
993
+ tjpAddr: tjp,
994
+ latestAddrAlreadyHave: localAddr,
995
+ });
951
996
  } else {
952
997
  // DIVERGENCE: Both have changes the other doesn't know about.
953
998
  console.log(`${lc} [TEST DEBUG] TJP ${tjp}: DIVERGENCE DETECTED! conflictStrategy=${conflictStrategy}`);
@@ -966,7 +1011,7 @@ export class SyncSagaCoordinator {
966
1011
  terminal: true
967
1012
  });
968
1013
  } else if (conflictStrategy === 'optimistic') {
969
- // Optimistic: We want to resolving this.
1014
+ // Optimistic: We want resolve this.
970
1015
  // We need to send our history to the Sender so they can Merge.
971
1016
 
972
1017
  // Fetch Full History for Local Timeline
@@ -979,10 +1024,10 @@ export class SyncSagaCoordinator {
979
1024
  // We have localAddr.
980
1025
  const resLocalTip = await getFromSpace({ space: mySpace, addr: localAddr });
981
1026
  if (!resLocalTip.success || resLocalTip.ibGibs?.length !== 1) {
982
- throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}) (E: ff06ff849fa8e8dba32ce09807411226)`);
1027
+ throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}) (E: 83cb88a767e22bbda99c6788bec50526)`);
983
1028
  }
984
1029
  const localTip = resLocalTip.ibGibs[0];
985
- if (!localTip) { throw new Error(`${lc} Failed to load local tip for conflict resolution. (E: 8f9b2c3d4e5f6g7h)`); }
1030
+ if (!localTip) { throw new Error(`${lc} Failed to load local tip for conflict resolution. (E: c39448ad6b3a72af78339ad877a56826)`); }
986
1031
 
987
1032
  const timelineAddrs = [...(localTip.rel8ns?.past ?? []), localAddr];
988
1033
 
@@ -994,9 +1039,8 @@ export class SyncSagaCoordinator {
994
1039
  reason: 'divergence',
995
1040
  terminal: false
996
1041
  });
997
-
998
1042
  } else {
999
- throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy} (E: 2a9b3c4d5e6f7g8h9i0j)`);
1043
+ throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy}. Only these currently implemented: ${SYNC_CONFLICT_STRATEGY_VALID_VALUES} (E: 8f12384180f8a718a983a749fe0adf26)`);
1000
1044
  }
1001
1045
  }
1002
1046
  }
@@ -1012,57 +1056,15 @@ export class SyncSagaCoordinator {
1012
1056
  throw new Error(`the saga has terminal conflicts. conflicts: ${JSON.stringify(conflicts)} (E: f2edbe93cc07a63b38bfc013d2213b26)`);
1013
1057
  }
1014
1058
 
1015
- // 3. Build Knowledge Vector (Full History for known timelines)
1016
- // [NEW] Smart Diff
1017
- // We iterate over all relevant addresses (deltas we are requesting OR push offers we might have newer versions of).
1018
- // Since we are "reacting" to Init, we primarily want to tell the Sender what we DO have for the things they talked about.
1019
-
1020
- /**
1021
- * we will put this in {@link SyncSagaMessageAckData_V1.knowledgeVector}
1022
- */
1023
- const knowledgeVector: { [groupKey: string]: string[] } = {};
1024
-
1025
- // [Smart Diff] Populate knowledge from timelines identified by Sender
1026
- for (const tjp of remoteTjps) {
1027
- const localAddr = localKV[tjp];
1028
- if (localAddr) {
1029
- const res = await getFromSpace({ addr: localAddr, space: mySpace });
1030
- if (res.success && res.ibGibs?.[0]) {
1031
- const ibGib = res.ibGibs[0];
1032
- const realTjp = ibGib.rel8ns?.tjp?.[0] || getIbGibAddr({ ibGib }); // Should match `tjp` if normalized
1033
- if (!knowledgeVector[realTjp]) {
1034
- const past = ibGib.rel8ns?.past || [];
1035
- knowledgeVector[realTjp] = [getIbGibAddr({ ibGib }), ...past];
1036
- }
1037
- }
1038
- }
1039
- }
1040
-
1041
- // Also populate from `knowledgeVector` in Init if we want bidirectional?
1042
- // No, `Init` doesn't have `knowledgeVector` in `SyncSagaMessageInitData` yet (it has `SyncInitData` generic props).
1043
- // Let's assume standard flow:
1044
- // 1. Sender says "I have X"
1045
- // 2. Receiver says "I don't have X. But if I did have Y (ancestor), I'd tell you."
1046
- // Problem: Receiver doesn't know X is related to Y without X.
1047
-
1048
- // SOLUTION:
1049
- // The *Sender* must include TJP mappings or we rely on `knowledgeVector` in `Init`?
1050
- // `SyncSagaMessageInitData_V1` extends `SyncInitData`.
1051
- // `SyncInitData` has `knowledgeVector`.
1052
- // If Sender populates `knowledgeVector` in `Init`, Receiver can use keys (TJPs) to look up its own state!
1053
-
1054
- // Let's implement Sender populating `Init.knowledgeVector`.
1055
- // But `SyncSagaCoordinator.startSaga` creates Init.
1056
-
1057
1059
  // 3. Create Ack Frame
1058
- const sagaId = sagaIbGib.data!.uuid;
1060
+ if (!sagaIbGib.data) { throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 24203af4600fb226ae6c1afbde442826)`); }
1061
+ const sagaId = sagaIbGib.data.uuid;
1059
1062
  // Create Payload Stone
1060
1063
  const ackData: SyncSagaMessageAckData_V1 = {
1061
1064
  sagaId,
1062
1065
  stage: SyncStage.ack,
1063
- deltaReqAddrs,
1064
- pushOfferAddrs,
1065
- knowledgeVector,
1066
+ deltaRequestAddrInfos,
1067
+ pushOfferInfos,
1066
1068
  };
1067
1069
  if (conflicts?.length > 0) { ackData.conflicts = conflicts; }
1068
1070
 
@@ -1090,14 +1092,63 @@ export class SyncSagaCoordinator {
1090
1092
  */
1091
1093
  // let payloadIbGibsDomain: IbGib_V1[] | undefined = await getPushOffers
1092
1094
 
1093
- if (pushOfferAddrs.length > 0) {
1095
+ let payloadIbGibsDomain: IbGib_V1[] | undefined;
1096
+ if (pushOfferInfos.length > 0) {
1097
+ const searchSecondSpaceAddrs: IbGibAddr[] = [];
1098
+ payloadIbGibsDomain = [];
1099
+ const domainAddrs: IbGibAddr[] = pushOfferInfos.flatMap(x => x.addrs);
1100
+ const resGet = await getFromSpace({ addrs: domainAddrs, space: mySpace, });
1101
+ const resGetRawData = resGet.rawResultIbGib?.data as IbGibSpaceResultData | undefined;
1102
+ if (!resGetRawData) { throw new Error(`(UNEXPECTED) resGet.rawResultIbGib.data falsy? (E: 1a2cc8cb99a1ffa60837e45a8229b826)`); }
1103
+ const addrsNotFound = resGetRawData.addrsNotFound ?? [];
1104
+ const addrsErrored = resGetRawData.addrsErrored ?? [];
1105
+ if (resGet.ibGibs && resGet.ibGibs.length === domainAddrs.length) {
1106
+ // found all of them
1107
+ resGet.ibGibs.forEach(x => { payloadIbGibsDomain!.push(x) })
1108
+ } else if (resGet.ibGibs && resGet.ibGibs.length > 0) {
1109
+ // found some of them
1110
+ resGet.ibGibs.forEach(x => { payloadIbGibsDomain!.push(x) })
1111
+ const foundPlusNotFoundCount =
1112
+ payloadIbGibsDomain.length +
1113
+ addrsNotFound.length +
1114
+ addrsErrored.length;
1115
+ if (foundPlusNotFoundCount === domainAddrs.length) {
1116
+ // we can still conceivably get the remaining addrs
1117
+ addrsNotFound.forEach(x => searchSecondSpaceAddrs.push(x));
1118
+ addrsErrored.forEach(x => searchSecondSpaceAddrs.push(x));
1119
+ } else if (addrsNotFound.length === 0 && addrsErrored.length === 0) {
1120
+ throw new Error(`(UNEXPECTED) found some but not all addrs but addrsNotFound and addrsErrored both empty? (E: ef1b2cf5bab8de2298ec507abe04e826)`);
1121
+ } else {
1122
+ throw new Error(`(UNEXPECTED) found some but not all addrs but addrsNotFound and addrsErrored don't contain the addrs not found? (E: ae9e015109f8c3c3a813da584cd98826)`);
1123
+ }
1124
+ } else {
1125
+ // found none of them(?)
1126
+ console.warn(`${lc} we were expecting all domainAddrs in mySpace (${mySpace.ib}) but we couldn't get all of them. addrsNotFound: ${addrsNotFound ?? []}. addrsErrored: ${addrsErrored ?? []}. Trying temp space (${myTempSpace.ib}). (W: f61908e264f8dddd2e3fb9084463d826)`);
1127
+ domainAddrs.forEach(x => searchSecondSpaceAddrs.push(x));
1128
+ }
1129
+ if (searchSecondSpaceAddrs.length > 0) {
1130
+ if (logalot) { console.log(`${lc} couldn't get all addrs in mySpace (${mySpace.ib}). searchSecondSpaceAddrs: ${searchSecondSpaceAddrs} (I: 233fd954dbd84e51bca02fa8eed5f826)`); }
1131
+ const resGet2 = await getFromSpace({ addrs: searchSecondSpaceAddrs, space: myTempSpace, });
1132
+ if (resGet2.success && resGet2.ibGibs && resGet2.ibGibs.length === searchSecondSpaceAddrs.length) {
1133
+ // got them all the second try
1134
+ resGet2.ibGibs.forEach(x => payloadIbGibsDomain!.push(x));
1135
+ } else {
1136
+ resGet2.ibGibs?.forEach(x => payloadIbGibsDomain!.push(x));
1137
+ const addrsFailed = domainAddrs.filter(x =>
1138
+ !payloadIbGibsDomain!.map(pid => getIbGibAddr({ ibGib: pid })).includes(x)
1139
+ );
1140
+ throw new Error(`couldn't get some or all of addrs from either mySpace (${mySpace.ib}) or myTempSpace (${myTempSpace.ib}). addrsFailed: ${addrsFailed} (E: 1394d412c4ffa4dd085f269b43338826)`);
1141
+ }
1142
+ }
1143
+ // we have now populated payloadIbGibsDomain
1094
1144
  }
1095
1145
 
1096
- return {
1097
- frame: ackFrame,
1098
- // conflictInfos,
1099
- // payloadIbGibsDomain,
1100
- };
1146
+ throw new Error(`not implemented (E: ed3f98abb0988c5ae8038bb8d741fb26)`);
1147
+ // return {
1148
+ // frame: ackFrame,
1149
+ // // conflictInfos,
1150
+ // payloadIbGibsDomain,
1151
+ // };
1101
1152
 
1102
1153
  } catch (error) {
1103
1154
  console.error(`${lc} ${extractErrorMsg(error)}`);
@@ -1109,14 +1160,14 @@ export class SyncSagaCoordinator {
1109
1160
 
1110
1161
  /**
1111
1162
  * Handles the `Ack` frame.
1112
- *
1163
+ *
1113
1164
  * @remarks
1114
1165
  * **Execution Context**: **Sender (Local)**.
1115
- *
1166
+ *
1116
1167
  * The Sender reacts to the Receiver's requirements:
1117
1168
  * 1. `deltaReqAddrs`: Receiver wants this data. Sender gathers it from `srcGraph` and puts it in `payloadIbGibs` (for next frame).
1118
1169
  * 2. `pushOfferAddrs`: Receiver has newer data. Sender acknowledges and adds them to `requests` (asking Receiver to send them).
1119
- *
1170
+ *
1120
1171
  * Returns a `Delta` frame.
1121
1172
  */
1122
1173
  protected async handleAckFrame({
@@ -1133,7 +1184,7 @@ export class SyncSagaCoordinator {
1133
1184
  tempSpace: IbGibSpaceAny,
1134
1185
  metaspace: MetaspaceService,
1135
1186
  identity?: KeystoneIbGib_V1,
1136
- }): Promise<HandleSagaFrameResult | null> {
1187
+ }): Promise<NextSagaFrameInfo> {
1137
1188
  const lc = `${this.lc}[${this.handleAckFrame.name}]`;
1138
1189
  try {
1139
1190
  if (logalot) { console.log(`${lc} starting... (I: 605b6860e898267a5b50c6d85704be26)`); }
@@ -1161,7 +1212,7 @@ export class SyncSagaCoordinator {
1161
1212
  console.warn(`${lc} Received terminal conflicts from Ack: ${JSON.stringify(terminalConflicts)}`);
1162
1213
  // Terminal failure. Sender should probably Commit(Fail) or just Abort.
1163
1214
  // For now, throw to trigger abort.
1164
- throw new Error(`${lc} Peer reported terminal conflicts. (E: a1b2c3d4e5f6g7h8i9j0k)`);
1215
+ throw new Error(`${lc} Peer reported terminal conflicts. (E: 23a0096ee05a2ccfa89334e8f156b426)`);
1165
1216
  }
1166
1217
 
1167
1218
  const optimisticConflicts = conflicts.filter(c => !c.terminal);
@@ -1189,7 +1240,7 @@ export class SyncSagaCoordinator {
1189
1240
  // Standard Saga: Init(Push) -> Ack(Pull Reqs) -> Delta(Push Data).
1190
1241
 
1191
1242
  // If Sender needs data, we might need a "Reverse Delta" or "Pull" phase?
1192
- // Or we just proceed to Delta (sending what Receiver wants),
1243
+ // Or we just proceed to Delta (sending what Receiver wants),
1193
1244
  // AND we piggyback our own requests?
1194
1245
  // OR: We treat the Conflict Resolution as a sub-saga or side-effect?
1195
1246
 
@@ -1218,7 +1269,7 @@ export class SyncSagaCoordinator {
1218
1269
  // We can fetch valid 'past' from space.
1219
1270
  const resSenderTip = await getFromSpace({ space: destSpace, addr: senderTip });
1220
1271
  const senderTipIbGib = resSenderTip.ibGibs?.[0];
1221
- if (!senderTipIbGib) { throw new Error(`${lc} Sender missing its own tip? ${senderTip} (E: 9c8d7e6f5g4h3i2j1k0l)`); }
1272
+ if (!senderTipIbGib) { throw new Error(`${lc} Sender missing its own tip? ${senderTip} (E: 832f3804645878869ee3c13714366726)`); }
1222
1273
 
1223
1274
  // Basic Diff: Find what Receiver has that we don't.
1224
1275
  // Actually, we need to traverse OUR past to find commonality.
@@ -1303,24 +1354,25 @@ export class SyncSagaCoordinator {
1303
1354
 
1304
1355
  // 2. Prepare Delta Payload (What Receiver Requesting + Our Conflict Logic)
1305
1356
 
1306
- const deltaReqAddrs = ackData.deltaReqAddrs || [];
1307
- const pushOfferAddrs = ackData.pushOfferAddrs || [];
1357
+ const deltaReqAddrs = ackData.deltaRequestAddrInfos || [];
1358
+ const pushOfferAddrs = ackData.pushOfferInfos || [];
1308
1359
 
1309
1360
  // 1. Process Push Offers (Pull Requests) (Naive: Accept all if missing)
1310
1361
  const pullReqAddrs: string[] = [];
1311
1362
  for (const addr of pushOfferAddrs) {
1312
- const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
1313
- if (!existing) {
1314
- pullReqAddrs.push(addr);
1315
- }
1363
+ // const existing = srcGraph[addr] || (await getFromSpace({ addr, space: destSpace })).ibGibs?.[0];
1364
+ // if (!existing) {
1365
+ // pullReqAddrs.push(addr);
1366
+ // }
1316
1367
  }
1317
1368
 
1318
1369
  // 2. Process Delta Requests (Push Payload)
1319
1370
  // [NEW] Smart Diff: Use knowledgeVector to skip dependencies
1371
+ // const useThisFunction = getDeltaDependencyGraph({ ibGibAddr: '', latestCommonFrameAddr: '', space: })
1320
1372
  const skipAddrs = new Set<string>();
1321
1373
  if (ackData.knowledgeVector) {
1322
1374
  Object.values(ackData.knowledgeVector).forEach(addrs => {
1323
- addrs.forEach(a => skipAddrs.add(a));
1375
+ // addrs.forEach(a => skipAddrs.add(a));
1324
1376
  });
1325
1377
  }
1326
1378
 
@@ -1328,24 +1380,24 @@ export class SyncSagaCoordinator {
1328
1380
  // Gather all tips to sync first
1329
1381
  const tipsToSync: IbGib_V1[] = [];
1330
1382
  for (const addr of deltaReqAddrs) {
1331
- let ibGib = srcGraph[addr];
1332
- if (!ibGib) {
1333
- const res = await getFromSpace({ addr, space: destSpace });
1334
- if (res.ibGibs && res.ibGibs.length > 0) {
1335
- ibGib = res.ibGibs[0];
1336
- }
1337
- }
1338
- if (ibGib) {
1339
- tipsToSync.push(ibGib);
1340
- } else {
1341
- throw new Error(`${lc} Requested addr not found: ${addr} (E: d41d59cff4a887f6414c3e92eabd8e26)`);
1342
- }
1383
+ // let ibGib = srcGraph[addr];
1384
+ // if (!ibGib) {
1385
+ // const res = await getFromSpace({ addr, space: destSpace });
1386
+ // if (res.ibGibs && res.ibGibs.length > 0) {
1387
+ // ibGib = res.ibGibs[0];
1388
+ // }
1389
+ // }
1390
+ // if (ibGib) {
1391
+ // tipsToSync.push(ibGib);
1392
+ // } else {
1393
+ // throw new Error(`${lc} Requested addr not found: ${addr} (E: d41d59cff4a887f6414c3e92eabd8e26)`);
1394
+ // }
1343
1395
  }
1344
1396
 
1345
1397
  // Calculate Dependency Graph for ALL tips, effectively utilizing common history
1346
1398
  // Pass skipAddrs to `getDependencyGraph` or gather manually.
1347
1399
  // `getDependencyGraph` takes a single ibGib.
1348
- // We can optimize by doing it for each tip and unioning the result?
1400
+ // We can optimize by doing it for each tip and unioning the result?
1349
1401
  // Or `graph-helper` could support `ibGibs: []`. It currently takes `ibGib`.
1350
1402
  // We will loop.
1351
1403
 
@@ -1420,7 +1472,8 @@ export class SyncSagaCoordinator {
1420
1472
  if (identity) { payloadIbGibsControl.push(identity); }
1421
1473
 
1422
1474
  // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
1423
- return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
1475
+ // return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
1476
+ throw new Error(`not implemented (E: 62e1e2a408e8bfa2982b2f87e8843826)`);
1424
1477
  } catch (error) {
1425
1478
  console.error(`${lc} ${extractErrorMsg(error)}`);
1426
1479
  throw error;
@@ -1431,10 +1484,10 @@ export class SyncSagaCoordinator {
1431
1484
 
1432
1485
  /**
1433
1486
  * Handles the `Delta` frame.
1434
- *
1487
+ *
1435
1488
  * @remarks
1436
1489
  * **Execution Context**: **Both**.
1437
- *
1490
+ *
1438
1491
  * 1. **Ingestion**: Receives data sent by Peer (`payloadAddrs`). Resolves them from `Rel8ns.payload` (implicitly via space/context) and returns them in `receivedPayload` to be saved.
1439
1492
  * 2. **Fulfillment**: Checks `requests`. If Peer requested data, gathers it and prepares `outgoingPayload`.
1440
1493
  * 3. **Completion**: If no more requests, transitions to `Commit`.
@@ -1453,7 +1506,7 @@ export class SyncSagaCoordinator {
1453
1506
  tempSpace: IbGibSpaceAny,
1454
1507
  metaspace: MetaspaceService,
1455
1508
  identity?: KeystoneIbGib_V1,
1456
- }): Promise<HandleSagaFrameResult | null> {
1509
+ }): Promise<NextSagaFrameInfo> {
1457
1510
  const lc = `${this.lc}[${this.handleDeltaFrame.name}]`;
1458
1511
  if (logalot) { console.log(`${lc} starting...`); }
1459
1512
 
@@ -1466,7 +1519,7 @@ export class SyncSagaCoordinator {
1466
1519
  if (deltaData.stage !== SyncStage.delta) {
1467
1520
  throw new Error(`${lc} Invalid delta frame: deltaData.stage !== SyncStage.delta (E: 0c28c8d8f08a4421b8344e6727271421)`);
1468
1521
  }
1469
- if (logalot) { console.log(`${lc} deltaData: ${pretty(deltaData)} (I: 8d7e6f5g4h3i2j1k0l9m)`); }
1522
+ if (logalot) { console.log(`${lc} deltaData: ${pretty(deltaData)} (I: a76008681df458cfbcdc4848f825a826)`); }
1470
1523
 
1471
1524
  console.log(`${lc} [CONFLICT DEBUG] deltaData.payloadAddrs count: ${deltaData.payloadAddrs?.length || 0}`);
1472
1525
 
@@ -1477,8 +1530,8 @@ export class SyncSagaCoordinator {
1477
1530
  // 1. Process Received Payload (Ingest)
1478
1531
  const receivedPayloadIbGibs: IbGib_V1[] = [];
1479
1532
  if (payloadAddrs.length > 0) {
1480
- // We use `payloadAddrs` as the manifest.
1481
- // The ACTUAL collection of ibGibs should be available via `getFromSpace`
1533
+ // We use `payloadAddrs` as the manifest.
1534
+ // The ACTUAL collection of ibGibs should be available via `getFromSpace`
1482
1535
  // assuming the "Transport" layer put them there implicitly?
1483
1536
  // OR, if we are local-only, we just get them.
1484
1537
  // The `handleDeltaFrame` contract assumes data is reachable in `space`.
@@ -1506,7 +1559,7 @@ export class SyncSagaCoordinator {
1506
1559
  // Get the requested ibGib
1507
1560
  let ibGib = srcGraph[addr];
1508
1561
  if (!ibGib) {
1509
- const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
1562
+ const res = await getFromSpace({ addr, space: destSpace }); // Query from destSpace
1510
1563
  if (res.ibGibs && res.ibGibs.length > 0) {
1511
1564
  ibGib = res.ibGibs[0];
1512
1565
  }
@@ -1602,7 +1655,7 @@ export class SyncSagaCoordinator {
1602
1655
  console.log(`${lc} [CONFLICT DEBUG] ReceiverTip found in tempSpace: ${!!resRecTip.ibGibs?.[0]}`);
1603
1656
  if (resRecTip.success && resRecTip.ibGibs?.[0]) {
1604
1657
  // We have the tip!
1605
- // Do we have the full history?
1658
+ // Do we have the full history?
1606
1659
  // `mergeDivergentTimelines` in `conflict-optimistic` will attempt to fetch history.
1607
1660
  // If we just ingested the missing pieces, `getFromSpace` inside `merge` should succeed.
1608
1661
 
@@ -1648,9 +1701,9 @@ export class SyncSagaCoordinator {
1648
1701
  payloadAddrs: outgoingPayload.map(p => getIbGibAddr({ ibGib: p })),
1649
1702
  requests: hasMyRequests ? myRequests : undefined,
1650
1703
  proposeCommit: !hasMyRequests // If we are sending data but have no requests, we VALIDATE PROPOSAL?
1651
- // Wait. If we send data, we are NOT committing yet.
1704
+ // Wait. If we send data, we are NOT committing yet.
1652
1705
  // We are sending data. The OTHER side must ingest it.
1653
- // So proposeCommit = true?
1706
+ // So proposeCommit = true?
1654
1707
  // "Here is the data. I'm done. If you are good, let's commit."
1655
1708
  // Yes.
1656
1709
  };
@@ -1680,7 +1733,8 @@ export class SyncSagaCoordinator {
1680
1733
  if (identity) { payloadIbGibsControl.push(identity); }
1681
1734
 
1682
1735
  // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
1683
- return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
1736
+ // return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
1737
+ throw new Error(`not implemented (E: 2b38a8afb6d84efcee5ab51673387826)`);
1684
1738
 
1685
1739
  } else {
1686
1740
  // We have nothing to send.
@@ -1712,7 +1766,8 @@ export class SyncSagaCoordinator {
1712
1766
  if (identity) { commitCtrlPayloads.push(identity); }
1713
1767
 
1714
1768
  // return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
1715
- return { frame: commitFrame, };
1769
+ // return { frame: commitFrame, };
1770
+ throw new Error(`not implemented (E: dda1ddc63fdcadff06653298e0d04826)`);
1716
1771
 
1717
1772
  } else {
1718
1773
  // peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
@@ -1770,7 +1825,8 @@ export class SyncSagaCoordinator {
1770
1825
  if (identity) { commitCtrlPayloads2.push(identity); }
1771
1826
 
1772
1827
  // return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
1773
- return { frame: commitFrame, };
1828
+ // return { frame: commitFrame, };
1829
+ throw new Error(`not implemented (E: 27514878585889e531ef21f1abbef826)`);
1774
1830
  }
1775
1831
 
1776
1832
  // Build control payloads for delta propose
@@ -1778,7 +1834,8 @@ export class SyncSagaCoordinator {
1778
1834
  if (identity) { deltaCtrlPayloads.push(identity); }
1779
1835
 
1780
1836
  // return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
1781
- return { frame: deltaFrame, };
1837
+ // return { frame: deltaFrame, };
1838
+ throw new Error(`not implemented (E: ff35584696b6fcb3ad6dd7c5cade2f26)`);
1782
1839
  }
1783
1840
  }
1784
1841
  }
@@ -1796,7 +1853,7 @@ export class SyncSagaCoordinator {
1796
1853
  tempSpace: IbGibSpaceAny,
1797
1854
  metaspace: MetaspaceService,
1798
1855
  identity?: KeystoneIbGib_V1,
1799
- }): Promise<HandleSagaFrameResult | null> {
1856
+ }): Promise<NextSagaFrameInfo> {
1800
1857
  const lc = `${this.lc}[${this.handleCommitFrame.name}]`;
1801
1858
  if (logalot) { console.log(`${lc} Commit received.`); }
1802
1859
 
@@ -1811,7 +1868,8 @@ export class SyncSagaCoordinator {
1811
1868
  // Note: Currently we don't have explicit cleanup logic implemented here yet (TODO).
1812
1869
 
1813
1870
  if (logalot) { console.log(`${lc} Peer committed. Finalizing saga locally. Saga Complete.`); }
1814
- return null;
1871
+ // return { responseWasNull: true };
1872
+ throw new Error(`not implemented (E: 4d7f878bcc45ad3dd9c4b8573f3aa826)`);
1815
1873
  }
1816
1874
 
1817
1875
  // #endregion Handlers
@@ -1909,7 +1967,7 @@ export class SyncSagaCoordinator {
1909
1967
  rel8nInfos.push({ rel8nName: 'identity', ibGibs: [sessionIdentity], });
1910
1968
  }
1911
1969
 
1912
- // remove the existing sync msg stones' addrs
1970
+ // remove the existing sync msg stones' addrs
1913
1971
  if (!prevSagaIbGib.rel8ns) { throw new Error(`(UNEXPECTED) prevSagaIbGib.rel8ns falsy? (E: 81375841aff85b1e48ea42ca218e6826)`); }
1914
1972
  if (!prevSagaIbGib.rel8ns[SYNC_MSG_REL8N_NAME] || prevSagaIbGib.rel8ns[SYNC_MSG_REL8N_NAME].length === 0) {
1915
1973
  throw new Error(`(UNEXPECTED) prevSagaIbGib.rel8ns[SYNC_MSG_REL8N_NAME] falsy/empty? (E: 15156baad26fcccda80aa3a31718c726)`);