@ibgib/core-gib 0.1.21 → 0.1.23

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 (127) hide show
  1. package/dist/common/other/graph-helper.d.mts +8 -0
  2. package/dist/common/other/graph-helper.d.mts.map +1 -1
  3. package/dist/common/other/graph-helper.mjs +31 -1
  4. package/dist/common/other/graph-helper.mjs.map +1 -1
  5. package/dist/sync/sync-conflict.respec.mjs +31 -28
  6. package/dist/sync/sync-conflict.respec.mjs.map +1 -1
  7. package/dist/sync/sync-constants.d.mts +21 -11
  8. package/dist/sync/sync-constants.d.mts.map +1 -1
  9. package/dist/sync/sync-constants.mjs +18 -6
  10. package/dist/sync/sync-constants.mjs.map +1 -1
  11. package/dist/sync/sync-helpers.d.mts +24 -15
  12. package/dist/sync/sync-helpers.d.mts.map +1 -1
  13. package/dist/sync/sync-helpers.mjs +163 -92
  14. package/dist/sync/sync-helpers.mjs.map +1 -1
  15. package/dist/sync/sync-innerspace-constants.respec.mjs +12 -7
  16. package/dist/sync/sync-innerspace-constants.respec.mjs.map +1 -1
  17. package/dist/sync/sync-innerspace-deep-updates.respec.mjs +12 -7
  18. package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
  19. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs +15 -10
  20. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs.map +1 -1
  21. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs +13 -7
  22. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
  23. package/dist/sync/sync-innerspace-partial-update.respec.mjs +15 -7
  24. package/dist/sync/sync-innerspace-partial-update.respec.mjs.map +1 -1
  25. package/dist/sync/sync-innerspace.respec.mjs +13 -7
  26. package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
  27. package/dist/sync/sync-peer/sync-peer-constants.d.mts +2 -0
  28. package/dist/sync/sync-peer/sync-peer-constants.d.mts.map +1 -0
  29. package/dist/sync/sync-peer/sync-peer-constants.mjs +2 -0
  30. package/dist/sync/sync-peer/sync-peer-constants.mjs.map +1 -0
  31. package/dist/sync/sync-peer/sync-peer-helpers.d.mts +2 -0
  32. package/dist/sync/sync-peer/sync-peer-helpers.d.mts.map +1 -0
  33. package/dist/sync/sync-peer/sync-peer-helpers.mjs +2 -0
  34. package/dist/sync/sync-peer/sync-peer-helpers.mjs.map +1 -0
  35. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.d.mts +8 -0
  36. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.d.mts.map +1 -0
  37. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs +8 -0
  38. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs.map +1 -0
  39. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-helpers.d.mts +18 -0
  40. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-helpers.d.mts.map +1 -0
  41. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-helpers.mjs +54 -0
  42. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-helpers.mjs.map +1 -0
  43. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.d.mts +65 -0
  44. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.d.mts.map +1 -0
  45. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.mjs +5 -0
  46. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.mjs.map +1 -0
  47. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts +44 -0
  48. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts.map +1 -0
  49. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +183 -0
  50. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -0
  51. package/dist/sync/sync-peer/sync-peer-types.d.mts +28 -0
  52. package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
  53. package/dist/sync/sync-peer/sync-peer-v1.d.mts +51 -9
  54. package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
  55. package/dist/sync/sync-peer/sync-peer-v1.mjs +244 -26
  56. package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
  57. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +26 -10
  58. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts.map +1 -1
  59. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs +119 -30
  60. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -1
  61. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts +31 -22
  62. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts.map +1 -1
  63. package/dist/sync/sync-saga-context/sync-saga-context-types.mjs +1 -9
  64. package/dist/sync/sync-saga-context/sync-saga-context-types.mjs.map +1 -1
  65. package/dist/sync/sync-saga-coordinator.d.mts +81 -87
  66. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  67. package/dist/sync/sync-saga-coordinator.mjs +627 -571
  68. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  69. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +105 -22
  70. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
  71. package/dist/sync/sync-types.d.mts +56 -76
  72. package/dist/sync/sync-types.d.mts.map +1 -1
  73. package/dist/sync/sync-types.mjs +5 -0
  74. package/dist/sync/sync-types.mjs.map +1 -1
  75. package/dist/timeline/timeline-api.d.mts.map +1 -1
  76. package/dist/timeline/timeline-api.mjs +15 -8
  77. package/dist/timeline/timeline-api.mjs.map +1 -1
  78. package/dist/witness/light-witness-base-v1.d.mts.map +1 -1
  79. package/dist/witness/light-witness-base-v1.mjs +2 -0
  80. package/dist/witness/light-witness-base-v1.mjs.map +1 -1
  81. package/dist/witness/space/inner-space/inner-space-v1.mjs +1 -1
  82. package/dist/witness/space/inner-space/inner-space-v1.mjs.map +1 -1
  83. package/package.json +1 -1
  84. package/src/common/other/graph-helper.mts +26 -1
  85. package/src/sync/README.md +31 -22
  86. package/src/sync/sync-conflict.respec.mts +31 -26
  87. package/src/sync/sync-constants.mts +19 -9
  88. package/src/sync/sync-helpers.mts +173 -97
  89. package/src/sync/sync-innerspace-constants.respec.mts +12 -7
  90. package/src/sync/sync-innerspace-deep-updates.respec.mts +12 -7
  91. package/src/sync/sync-innerspace-dest-ahead.respec.mts +14 -9
  92. package/src/sync/sync-innerspace-multiple-timelines.respec.mts +13 -7
  93. package/src/sync/sync-innerspace-partial-update.respec.mts +15 -7
  94. package/src/sync/sync-innerspace.respec.mts +13 -7
  95. package/src/sync/sync-peer/sync-peer-constants.mts +0 -0
  96. package/src/sync/sync-peer/sync-peer-helpers.mts +0 -0
  97. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mts +8 -0
  98. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-helpers.mts +72 -0
  99. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.mts +72 -0
  100. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +193 -0
  101. package/src/sync/sync-peer/sync-peer-types.mts +30 -1
  102. package/src/sync/sync-peer/sync-peer-v1.mts +229 -30
  103. package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +140 -43
  104. package/src/sync/sync-saga-context/sync-saga-context-types.mts +34 -30
  105. package/src/sync/sync-saga-coordinator.mts +678 -660
  106. package/src/sync/sync-saga-message/sync-saga-message-types.mts +106 -22
  107. package/src/sync/sync-types.mts +59 -87
  108. package/src/timeline/timeline-api.mts +17 -10
  109. package/src/witness/light-witness-base-v1.mts +2 -1
  110. package/src/witness/space/inner-space/inner-space-v1.mts +1 -1
  111. package/test_output.log +0 -0
  112. package/tmp.md +62 -44
  113. package/dist/sync/sync-local-spaces.respec.d.mts +0 -2
  114. package/dist/sync/sync-local-spaces.respec.d.mts.map +0 -1
  115. package/dist/sync/sync-local-spaces.respec.mjs +0 -159
  116. package/dist/sync/sync-local-spaces.respec.mjs.map +0 -1
  117. package/dist/sync/sync-peer/sync-peer-innerspace-v1.d.mts +0 -42
  118. package/dist/sync/sync-peer/sync-peer-innerspace-v1.d.mts.map +0 -1
  119. package/dist/sync/sync-peer/sync-peer-innerspace-v1.mjs +0 -194
  120. package/dist/sync/sync-peer/sync-peer-innerspace-v1.mjs.map +0 -1
  121. package/dist/sync/sync-saga-coordinator.respec.d.mts +0 -2
  122. package/dist/sync/sync-saga-coordinator.respec.d.mts.map +0 -1
  123. package/dist/sync/sync-saga-coordinator.respec.mjs +0 -40
  124. package/dist/sync/sync-saga-coordinator.respec.mjs.map +0 -1
  125. package/src/sync/sync-local-spaces.respec.mts +0 -200
  126. package/src/sync/sync-peer/sync-peer-innerspace-v1.mts +0 -240
  127. package/src/sync/sync-saga-coordinator.respec.mts +0 -52
@@ -1,24 +1,25 @@
1
1
  import { extractErrorMsg, getUUID, // so our timestamp in ticks as a string are uniform
2
- pretty } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
2
+ pretty, delay, } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
3
3
  import { getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
4
- import { splitPerTjpAndOrDna, getTimelinesGroupedByTjp } from "../common/other/ibgib-helper.mjs";
5
4
  import { Factory_V1 } from "@ibgib/ts-gib/dist/V1/factory.mjs";
6
- import { GLOBAL_LOG_A_LOT } from "../core-constants.mjs";
7
5
  import { putInSpace, getLatestAddrs, getFromSpace } from "../witness/space/space-helper.mjs";
8
- import { SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME } from "./sync-constants.mjs";
6
+ import { SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME, SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN } from "./sync-constants.mjs";
9
7
  import { appendToTimeline, createTimeline } from "../timeline/timeline-api.mjs";
10
- import { SyncMode, } from "./sync-types.mjs";
11
- import { getSyncIb, isPastFrame } from "./sync-helpers.mjs";
12
- import { getSyncSagaDependencyGraph } from "./sync-helpers.mjs";
13
- import { getDependencyGraph } from "../common/other/graph-helper.mjs";
8
+ import { SyncConflictStrategy, SyncMode, } from "./sync-types.mjs";
9
+ import { getSyncIb, getTempSpaceName, isPastFrame } from "./sync-helpers.mjs";
10
+ import { getDependencyGraph, toFlatGraph } from "../common/other/graph-helper.mjs";
14
11
  import { getSyncSagaMessageIb } from "./sync-saga-message/sync-saga-message-helpers.mjs";
15
12
  import { SYNC_SAGA_MSG_ATOM } from "./sync-saga-message/sync-saga-message-constants.mjs";
16
- import { SyncSagaContextCmd } from "./sync-saga-context/sync-saga-context-types.mjs";
13
+ import { splitPerTjpAndOrDna, getTimelinesGroupedByTjp, isIbGib } from "../common/other/ibgib-helper.mjs";
17
14
  import { createSyncSagaContext } from "./sync-saga-context/sync-saga-context-helpers.mjs";
18
- import { newupSubject } from "../common/pubsub/subject/subject-helper.mjs";
15
+ import { newupSubject, } from "../common/pubsub/subject/subject-helper.mjs";
19
16
  import { mergeDivergentTimelines } from "./strategies/conflict-optimistic.mjs";
20
17
  import { getSyncSagaMessageFromFrame } from "./sync-saga-message/sync-saga-message-helpers.mjs";
21
- const logalot = GLOBAL_LOG_A_LOT || true;
18
+ import { fnObs } from "../common/pubsub/observer/observer-helper.mjs";
19
+ // const logalot = GLOBAL_LOG_A_LOT || true;
20
+ const logalot = false;
21
+ const logalotControlDomain = true;
22
+ const lcControlDomain = '[ControlDomain]';
22
23
  /**
23
24
  * Orchestrates the synchronization process between two spaces (Source and Destination).
24
25
  *
@@ -51,14 +52,13 @@ export class SyncSagaCoordinator {
51
52
  * @param opts.domainIbGibs - The root ibgibs defining the scope of the sync.
52
53
  * @param opts.useSessionIdentity - (Optional) Whether to create an ephemeral session identity. Default: true.
53
54
  */
54
- async sync({ peer, localSpace: _localSpace, source: _source, metaspace, domainIbGibs, conflictStrategy = 'abort', useSessionIdentity = true, }) {
55
+ async sync({ peer, domainIbGibs, conflictStrategy = SyncConflictStrategy.abort, useSessionIdentity = true, metaspace, localSpace, }) {
55
56
  const lc = `${this.lc}[${this.sync.name}]`;
56
57
  if (logalot) {
57
58
  console.log(`${lc} starting...`);
58
59
  }
59
- const localSpace = (_source || _localSpace);
60
60
  if (!localSpace) {
61
- throw new Error(`${lc} source (or localSpace) required (E: 8a9b0c1d)`);
61
+ throw new Error(`${lc} source (or localSpace) required (E: 25df3761f7686a1099a552f83c95d326)`);
62
62
  }
63
63
  // 1. SETUP SAGA METADATA
64
64
  const sagaId = await getUUID();
@@ -74,10 +74,6 @@ export class SyncSagaCoordinator {
74
74
  resolveDone = resolve;
75
75
  rejectDone = reject;
76
76
  });
77
- async function getTempSpaceName() {
78
- const uuid = await getUUID();
79
- return `tmp_sync_space_${uuid.substring(0, 8)}`;
80
- }
81
77
  // WORKING CONTEXT (Transactional)
82
78
  const tempSpaceName = await getTempSpaceName();
83
79
  const tempSpace = await metaspace.createNewLocalSpace({
@@ -105,33 +101,24 @@ export class SyncSagaCoordinator {
105
101
  : undefined;
106
102
  // if (logalot) { console.log(`${lc} sessionIdentity: ${sessionIdentity ? pretty(sessionIdentity) : 'undefined'} (I: abc01872800b3a66b819a05898bba826)`); }
107
103
  // 3. CREATE INITIAL FRAME (Stage.init)
108
- const { sagaFrame: initFrame, srcGraph } = await this.createInitFrame({
104
+ const { sagaFrame: initFrame, initialDomainGraph } = await this.createInitFrame({
109
105
  sagaId,
110
106
  sessionIdentity,
111
- localSpace,
112
107
  domainIbGibs,
113
- tempSpace,
114
- metaspace,
115
- conflictStrategy
116
- });
117
- // 4. EXECUTE SAGA LOOP (FSM)
118
- const syncedIbGibs = await this.executeSagaLoop({
119
- initialFrame: initFrame,
120
- srcGraph,
121
- peer,
122
- sessionIdentity,
123
- updates$,
124
- localSpace,
125
- tempSpace,
126
- metaspace
108
+ conflictStrategy,
109
+ metaspace, localSpace, tempSpace,
127
110
  });
128
- // 5. MERGE RESULT (Optimistic Commit)
129
- if (syncedIbGibs && syncedIbGibs.length > 0) {
130
- if (logalot) {
131
- console.log(`${lc} Merging ${syncedIbGibs.length} synced ibGibs to localSpace...`);
132
- }
133
- await putInSpace({ space: localSpace, ibGibs: syncedIbGibs });
134
- }
111
+ // 4. KICK OFF THE PING-PONG SAGA LOOP (FSM)
112
+ // commented to compile
113
+ // await this.executeSagaLoop({
114
+ // initialFrame: initFrame,
115
+ // peer,
116
+ // sessionIdentity,
117
+ // updates$,
118
+ // localSpace,
119
+ // tempSpace,
120
+ // metaspace
121
+ // });
135
122
  resolveDone();
136
123
  if (!updates$.complete) {
137
124
  throw new Error(`(UNEXPECTED) updates$.complete falsy? (E: d24cd82184aec130c89a320819b39126)`);
@@ -194,87 +181,110 @@ export class SyncSagaCoordinator {
194
181
  * **Execution Context**: **Sender (Local)**.
195
182
  *
196
183
  * This method manages the "Ping Pong" request-response cycle on the Sender.
197
- * It sends frames via the Peer Witness and processes the responses using `handleSagaFrame`.
184
+ * It sends frames via the Peer Witness and processes the responses using `handleSagaResponseContext`.
198
185
  *
199
186
  * **Data Transport Note**:
200
187
  * Actual ibGib data (payloads) are transported via `SyncSagaContext.rel8ns.payload`.
201
- * When `handleSagaFrame` returns a `nextPayloadIbGibs` (data to send), this loop injects it into
188
+ * When `handleSagaResponseContext` returns a `nextPayloadIbGibs` (data to send), this loop injects it into
202
189
  * the NEXT request context.
203
190
  * When the Peer responds with data (in the response context), it is resolved and put into `tempSpace`.
204
191
  */
205
- async executeSagaLoop({ initialFrame, srcGraph, peer, sessionIdentity, updates$, localSpace, tempSpace, metaspace }) {
192
+ async executeSagaLoop({ initialFrame, initialDomainGraph, peer, sessionIdentity, updates$, localSpace, tempSpace, metaspace }) {
206
193
  const lc = `${this.lc}[${this.executeSagaLoop.name}]`;
207
194
  // The current frame we just generated (e.g., Init or Delta Request)
208
195
  let currentFrame = initialFrame;
209
196
  // The payload we need to attach to the message (data we are sending)
210
- let nextPayloadIbGibs = [];
211
- // Accumulator for all data we've successfully pulled from the remote
212
- const allReceivedIbGibs = [];
197
+ let nextDomainIbGibs = [];
198
+ // First, inject local/tempSpace into peer so it can pull as
199
+ // needed...code smell?
200
+ peer.senderSpace = localSpace;
201
+ peer.senderTempSpace = tempSpace;
213
202
  while (currentFrame) {
214
203
  // A. Create Context (Request)
215
204
  // 1. Calculate Full Dependency Graph (including Ancestors/DNA)
216
- // We must do this BEFORE creating the Context so we can list them all in payloadAddrs.
217
- const allDeps = [];
205
+ // We must do this BEFORE creating the Context so we can list them
206
+ // all in payloadAddrsDomain and payloadAddrsControl.
207
+ const nextDomainIbGibsAndDeps = [];
218
208
  // A. Payload (Standard Deep Deps)
219
- for (const item of nextPayloadIbGibs) {
220
- let graph = await getDependencyGraph({ ibGib: item, space: localSpace });
221
- if (!graph) {
222
- graph = await getDependencyGraph({ ibGib: item, space: tempSpace });
209
+ // TODO: THIS IS EXTREMELY INEFFICIENT. adjust this algorithm to
210
+ // only do the dependencies that the other end doesn't need (diff
211
+ // between tip and LCA). This can be done using the information in
212
+ // the ack's conflicts array.
213
+ for (const nextDomainIbGib of nextDomainIbGibs) {
214
+ let nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: localSpace });
215
+ if (!nextDomainIbGibGraph) {
216
+ nextDomainIbGibGraph = await getDependencyGraph({ ibGib: nextDomainIbGib, space: tempSpace });
223
217
  }
224
- if (graph) {
225
- allDeps.push(...Object.values(graph));
218
+ if (nextDomainIbGibGraph) {
219
+ nextDomainIbGibsAndDeps.push(...Object.values(nextDomainIbGibGraph));
226
220
  }
227
221
  else {
228
- allDeps.push(item);
222
+ throw new Error(`(UNEXPECTED) we couldn't get the graph for a known domain ibgib? nextDomainIbGib addr: ${getIbGibAddr({ ibGib: nextDomainIbGib })} (E: 01b3e4db8768b5b77db72e486f4f7826)`);
229
223
  }
230
224
  }
231
225
  if (logalot) {
232
- // console.log(`${lc} allDeps count: ${allDeps.length}`);
233
- }
234
- // B. Frames (Shallow Sync Deps)
235
- if (currentFrame) {
236
- const deps = await getSyncSagaDependencyGraph({ ibGib: currentFrame, space: tempSpace });
237
- if (deps)
238
- allDeps.push(...deps);
226
+ console.log(`${lc} payloadIbGibsDomain count: ${nextDomainIbGibsAndDeps.length} (I: 2beda8ca7dc5ac0f48ed9e25e704b826)`);
239
227
  }
240
228
  // 2. Create Context (Envelope)
241
- // Use the FULL dependency list as the payload manifest
242
- const payloadAddrs = allDeps.map(p => getIbGibAddr({ ibGib: p }));
229
+ // set up subscription for response's payload ibgibs (if any)
230
+ const domainPayloadsMap = new Map();
231
+ const sublc = `${lc}[peer.payloadIbGibsDomainReceived$]`;
232
+ const subscription = await peer.payloadIbGibsDomainReceived$.subscribe(fnObs({
233
+ next: async (ibgib) => {
234
+ if (logalot) {
235
+ console.log(`${sublc} next fired. (I: 2b4bdf502a38a90ba33d9711e7cb7826)`);
236
+ }
237
+ const addr = getIbGibAddr({ ibGib: ibgib });
238
+ if (logalotControlDomain) {
239
+ console.log(`${lc}${lcControlDomain} DOMAIN STREAM RECEIVED <- observable: ${addr} (I: d69ee80fcaece272483ec33b2d289826)`);
240
+ }
241
+ domainPayloadsMap.set(addr, ibgib);
242
+ },
243
+ error: async (e) => {
244
+ if (isIbGib(e)) {
245
+ console.error(`${sublc} error fired. error: ${JSON.stringify(e.data)} (E: 01cc08ba05ad99682831174fd7c31a26)`);
246
+ }
247
+ else {
248
+ console.dir(e);
249
+ console.error(`${sublc} error fired. error: ${extractErrorMsg(e)} (E: 73d3d61464e8e4ce4cd6efd8b9675826)`);
250
+ }
251
+ },
252
+ complete: async () => {
253
+ if (logalot) {
254
+ console.log(`${sublc} complete fired. (I: a47218aa9e4433fdb97c068880a45826)`);
255
+ }
256
+ await subscription.unsubscribe();
257
+ },
258
+ }));
259
+ // Create the Request Context...
243
260
  const requestCtx = await createSyncSagaContext({
244
- cmd: SyncSagaContextCmd.process,
245
261
  sagaFrame: currentFrame,
246
262
  sessionKeystones: sessionIdentity ? [sessionIdentity] : undefined,
247
- payloadAddrs: payloadAddrs.length > 0 ? payloadAddrs : undefined,
263
+ /**
264
+ * init frame: empty
265
+ *
266
+ */
267
+ payloadIbGibsDomain: nextDomainIbGibsAndDeps,
268
+ localSpace,
248
269
  });
249
- // Add Context Deps
250
- if (requestCtx) {
251
- const deps = await getSyncSagaDependencyGraph({ ibGib: requestCtx, space: tempSpace });
252
- if (deps)
253
- allDeps.push(...deps);
254
- }
255
- // 3. Identity (if exists)
256
- // Identity might be deep? Keystone? Usually self-contained or shallow references.
257
- if (sessionIdentity) {
258
- allDeps.push(sessionIdentity);
259
- }
260
- if (allDeps.length > 0) {
261
- await putInSpace({
262
- space: localSpace,
263
- ibGibs: allDeps
264
- });
265
- }
266
- // B. Transmit
270
+ // Log what we're sending
271
+ if (logalotControlDomain) {
272
+ const domainAddrs = nextDomainIbGibsAndDeps.map(p => getIbGibAddr({ ibGib: p }));
273
+ console.log(`${lc}${lcControlDomain} SENDER TRANSMIT -> peer.witness (I: b3c4d5e6f7a8b9c0)`);
274
+ console.log(`${lc}${lcControlDomain} Context: ${getIbGibAddr({ ibGib: requestCtx })}`);
275
+ console.log(`${lc}${lcControlDomain} Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
276
+ console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${domainAddrs.length}): ${domainAddrs.join(', ') || '(none)'}`);
277
+ }
278
+ // update our saga listeners...
267
279
  // if (logalot) { console.log(`${lc} transmitting... requestCtx: ${pretty(requestCtx)} (I: 8cf20817c66899abdb1e76df50356826)`); }
268
- updates$.next(requestCtx);
280
+ updates$.next(requestCtx); // spins off
281
+ // ...And send the context
269
282
  const responseCtx = await peer.witness(requestCtx);
270
283
  // C. Handle Response
271
284
  if (!responseCtx) {
272
- // Check if we just sent a Commit frame. If so, peer's silence is success/expected.
273
285
  if (currentFrame) {
286
+ // Check for Commit (Peer silence expected)
274
287
  const msg = await getSyncSagaMessageFromFrame({ frameIbGib: currentFrame, space: localSpace });
275
- if (logalot) {
276
- console.log(`${lc} Checking currentFrame stage: ${msg?.data?.stage} (Expected: ${SyncStage.commit})`);
277
- }
278
288
  if (msg?.data?.stage === SyncStage.commit) {
279
289
  if (logalot) {
280
290
  console.log(`${lc} Sender sent Commit. Peer returned no response. Saga Complete.`);
@@ -282,59 +292,79 @@ export class SyncSagaCoordinator {
282
292
  currentFrame = null;
283
293
  break;
284
294
  }
295
+ else {
296
+ throw new Error(`(UNEXPECTED) responseCtx falsy and currentFrame truthy, but we're not in the commit stage? This may be expected ultimately, but atow I am not seeing this as being expected. (E: cc34498962bd370deeff351fac939f26)`);
297
+ }
298
+ }
299
+ else {
300
+ throw new Error(`(UNEXPECTED) no response and currentFrame falsy? (E: 8d1085ea2f28cfc3f9c922649864a826)`);
285
301
  }
286
- throw new Error(`responseCtx falsy. Peer returned no response context (E: c099d8073b48d85e881f917835158f26)`);
287
- // console.warn(`${lc} Peer returned no response context. Ending loop.`);
288
- // currentFrame = null;
289
- // break;
290
302
  }
291
- if (logalot) {
292
- console.log(`${lc} received responseCtx: ${pretty(responseCtx)} (I: 8f35c8b7a9e886aa9743d38bee907826)`);
293
- }
294
- updates$.next(responseCtx);
295
- // D. Extract Remote Frame & React
296
- const remoteFrameAddr = responseCtx.rel8ns?.sagaFrame?.[0];
297
- if (!remoteFrameAddr) {
298
- // If the remote didn't send a saga frame, we can't continue the protocol.
299
- throw new Error(`${lc} Peer response has no sagaFrame. Ending loop. (E: 0224e84a83e1c7ee288aaed6bdc40826)`);
300
- }
301
- // We look in localSpace first (where Peer delivers), then tempSpace.
302
- let resRemoteFrame = await getFromSpace({ addr: remoteFrameAddr, space: localSpace });
303
- if (!resRemoteFrame.success || !resRemoteFrame.ibGibs?.length) {
304
- // Fallback to tempSpace just in case
305
- resRemoteFrame = await getFromSpace({ addr: remoteFrameAddr, space: tempSpace });
306
- }
307
- const remoteFrame = resRemoteFrame.ibGibs?.[0];
308
- if (!remoteFrame) {
309
- throw new Error(`Could not resolve remote frame: ${remoteFrameAddr}`);
310
- }
311
- // if (logalot) { console.log(`${lc} remoteFrame: ${pretty(remoteFrame)}`); } // leave this in for later use if needed
312
- // Ensure remote frame and its dependencies are in tempSpace
313
- // The Peer delivered them to localSpace, but handleSagaFrame works in tempSpace.
314
- const remoteDeps = await getSyncSagaDependencyGraph({ ibGib: remoteFrame, space: localSpace });
315
- if (remoteDeps && remoteDeps.length > 0) {
316
- await putInSpace({ space: tempSpace, ibGibs: remoteDeps });
317
- }
318
- // React (Reducer)
319
- // This processes the FRAME we just got from the peer.
320
- // i.e., We Sent Init -> Got Ack. This calls handleAckFrame.
321
- // i.e., We Sent Delta -> Got Delta. This calls handleDeltaFrame.
322
- const result = await this.handleSagaFrame({
323
- sagaIbGib: remoteFrame,
324
- srcGraph,
325
- destSpace: localSpace, // Query existing data from localSpace (Source)
326
- tempSpace: tempSpace, // Transaction space for saga frames
327
- identity: sessionIdentity,
328
- metaspace
303
+ // ---------------------------------------------------------------------
304
+ // 2d. HANDLE RESPONSE
305
+ // ---------------------------------------------------------------------
306
+ // at this point, we have received the response context ibgib, but
307
+ // if there were payloads expected to be transferred, they may not
308
+ // be done yet.
309
+ if (!responseCtx.data) {
310
+ throw new Error(`(UNEXPECTED) responseCtx.data falsy? (E: a969992bae53ab18a827ec58aec15826)`);
311
+ }
312
+ updates$.next(responseCtx); // spins off -- don't remove this comment!
313
+ // Extract expected domain addresses from response context
314
+ const responsePayloadAddrsDomain = responseCtx.data[SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN] || [];
315
+ // Poll for them if needed
316
+ if (responsePayloadAddrsDomain.length > 0) {
317
+ responseCtx.payloadIbGibsDomain = await this.pollForDomainPayloads({
318
+ expectedAddrs: responsePayloadAddrsDomain,
319
+ pollIntervalMs: 20, // relatively arbitrary right now
320
+ domainPayloadsMap,
321
+ tempSpace,
322
+ });
323
+ }
324
+ // at this point, we have received the context AND **all** of the
325
+ // domain payloads (if applicable), so we are ready to do the next
326
+ // iteration in the saga loop
327
+ // Log what we received back
328
+ if (!responseCtx.sagaFrame) {
329
+ throw new Error(`(UNEXPECTED) responseCtx.sagaFrame falsy? the Peer should have set this when it got the response back from the remote. (E: e650adadf9a2063ec6764a1e31d3d826)`);
330
+ }
331
+ if (logalotControlDomain) {
332
+ const responseControlAddrs = responseCtx.data?.['@payloadAddrsControl'] || [];
333
+ console.log(`${lc}${lcControlDomain} SENDER RECEIVED <- peer.witness (I: c4d5e6f7a8b9c0d1)`);
334
+ console.log(`${lc}${lcControlDomain} Response Context: ${getIbGibAddr({ ibGib: responseCtx })}`);
335
+ console.log(`${lc}${lcControlDomain} Response Saga Frame: ${getIbGibAddr({ ibGib: responseCtx.sagaFrame })}`);
336
+ console.log(`${lc}${lcControlDomain} CONTROL Payloads (${responseControlAddrs.length}): ${responseControlAddrs.join(', ') || '(none)'}`);
337
+ console.log(`${lc}${lcControlDomain} DOMAIN Payloads (${responsePayloadAddrsDomain.length}): ${responsePayloadAddrsDomain.join(', ') || '(none)'}`);
338
+ }
339
+ // Handle Response Frame
340
+ // interface HandleSagaFrameResult {
341
+ // frame: SyncIbGib_V1;
342
+ // payloadIbGibsDomain?: IbGib_V1[];
343
+ // conflictInfos?: SyncSagaConflictInfo;
344
+ // }
345
+ const handleResult = await this.handleSagaContext({
346
+ sagaContext: responseCtx,
347
+ mySpace: localSpace,
348
+ myTempSpace: tempSpace,
349
+ metaspace,
329
350
  });
330
- currentFrame = result?.frame || null;
331
- nextPayloadIbGibs = result?.payloadIbGibs || []; // Payload to send in NEXT Ping
332
- if (result?.receivedPayloadIbGibs) {
333
- // Keep track of what we received for final merge
334
- allReceivedIbGibs.push(...result.receivedPayloadIbGibs);
351
+ if (!handleResult) {
352
+ if (logalot) {
353
+ console.log(`${lc} Handler returned null (Saga End).`);
354
+ }
355
+ break;
356
+ }
357
+ currentFrame = handleResult.frame;
358
+ // Collect next DOMAIN payloads for the NEXT request
359
+ nextDomainIbGibs = [...(handleResult.payloadIbGibsDomain || [])];
360
+ // Log handler output for next iteration
361
+ if (logalotControlDomain) {
362
+ const handlerDomainAddrs = nextDomainIbGibs.map(p => getIbGibAddr({ ibGib: p }));
363
+ console.log(`${lc}${lcControlDomain} HANDLER RESULT -> next iteration (I: d5e6f7a8b9c0d1e2)`);
364
+ console.log(`${lc}${lcControlDomain} Next Frame: ${getIbGibAddr({ ibGib: currentFrame })}`);
365
+ console.log(`${lc}${lcControlDomain} DOMAIN for next (${handlerDomainAddrs.length}): ${handlerDomainAddrs.join(', ') || '(none)'}`);
335
366
  }
336
367
  }
337
- return allReceivedIbGibs;
338
368
  }
339
369
  /**
340
370
  * Helper to get Knowledge Vector for specific domain ibGibs or TJPs.
@@ -346,7 +376,7 @@ export class SyncSagaCoordinator {
346
376
  if (logalot) {
347
377
  console.log(`${lc} starting... (I: e184f8a7818666febfbbd2d841ed3826)`);
348
378
  }
349
- console.dir(space);
379
+ // console.dir(space);
350
380
  if (!(domainIbGibs && domainIbGibs.length > 0) &&
351
381
  !(tjpAddrs && tjpAddrs.length > 0)) {
352
382
  throw new Error(`(UNEXPECTED) domainIbGibs and tjpAddrs falsy/empty? we need one or the other (E: f674285111c8648398cd79d8c08ec826)`);
@@ -361,23 +391,21 @@ export class SyncSagaCoordinator {
361
391
  }
362
392
  else if (domainIbGibs && domainIbGibs.length > 0) {
363
393
  // Extract TJPs from domain Ibgibs
364
- if (logalot) {
365
- console.log(`${lc} domainIbGibs (${domainIbGibs.length}) provided. (I: a378995a0658af1f086ac1f297486c26)`);
366
- }
394
+ // if (false) { console.log(`${lc} domainIbGibs (${domainIbGibs.length}) provided. (I: a378995a0658af1f086ac1f297486c26)`); }
367
395
  const { mapWithTjp_YesDna, mapWithTjp_NoDna } = splitPerTjpAndOrDna({ ibGibs: domainIbGibs });
368
- if (logalot) {
396
+ if (false) {
369
397
  console.log(`${lc}[TEST DEBUG] mapWithTjp_YesDna: ${JSON.stringify(mapWithTjp_YesDna)} (I: 287e22897148298e185712c8d50cfb26)`);
370
398
  }
371
- if (logalot) {
399
+ if (false) {
372
400
  console.log(`${lc}[TEST DEBUG] mapWithTjp_NoDna: ${JSON.stringify(mapWithTjp_NoDna)} (I: 1bdc62656294aed0f9df334647dc7326)`);
373
401
  }
374
402
  const allWithTjp = [...Object.values(mapWithTjp_YesDna), ...Object.values(mapWithTjp_NoDna)];
375
403
  const timelineMap = getTimelinesGroupedByTjp({ ibGibs: allWithTjp });
376
- if (logalot) {
404
+ if (false) {
377
405
  console.log(`${lc}[TEST DEBUG] timelineMap: ${JSON.stringify(timelineMap)} (I: 2cc04898e5f85179fb1ac7f827abc426)`);
378
406
  }
379
407
  tjps = Object.keys(timelineMap);
380
- if (logalot) {
408
+ if (false) {
381
409
  console.log(`${lc}[TEST DEBUG] tjps: ${tjps} (I: 3dd548667cbd967c68e57c88dc570826)`);
382
410
  }
383
411
  }
@@ -390,16 +418,14 @@ export class SyncSagaCoordinator {
390
418
  if (tjps.length === 0) {
391
419
  return {};
392
420
  }
393
- if (logalot) {
421
+ if (false) {
394
422
  console.log(`${lc} getting latest addrs for tjps: ${tjps} (I: d4e7080b8ba8187c583b82fd91ac0626)`);
395
423
  }
396
424
  const res = await getLatestAddrs({ space, tjpAddrs: tjps });
397
425
  if (!res.data || !res.data.latestAddrsMap) {
398
426
  throw new Error(`${lc} Failed to get latest addrs. (E: 7a8b9c0d)`);
399
427
  }
400
- if (logalot) {
401
- console.log(`${lc}[TEST DEBUG] res.data.latestAddrsMap: ${JSON.stringify(res.data.latestAddrsMap)} (I: a8e128bdf80898ac2e6d8021a5bff726)`);
402
- }
428
+ // if (false) { console.log(`${lc}[TEST DEBUG] res.data.latestAddrsMap: ${JSON.stringify(res.data.latestAddrsMap)} (I: a8e128bdf80898ac2e6d8021a5bff726)`); }
403
429
  return res.data.latestAddrsMap;
404
430
  }
405
431
  catch (error) {
@@ -412,24 +438,27 @@ export class SyncSagaCoordinator {
412
438
  }
413
439
  }
414
440
  }
415
- async analyzeTimelines({ domainIbGibs, space, }) {
416
- const lc = `${this.lc}[${this.analyzeTimelines.name}]`;
417
- const srcGraph = await getDependencyGraph({
441
+ async analyzeDomainIbGibs({ domainIbGibs, space, }) {
442
+ const lc = `${this.lc}[${this.analyzeDomainIbGibs.name}]`;
443
+ if (domainIbGibs.length === 0) {
444
+ throw new Error(`invalid domainIbGibs. expected at least one, but array is empty. (E: 820a719bcac5a16878a2af697113b826)`);
445
+ }
446
+ const fullGraph = await getDependencyGraph({
418
447
  ibGibs: domainIbGibs,
419
448
  live: true,
420
449
  space,
421
450
  });
422
- const srcGraphIsValid = srcGraph && Object.keys(srcGraph).length > 0;
451
+ const graphIsValid = fullGraph && Object.keys(fullGraph).length > 0;
423
452
  if (logalot) {
424
- console.log(`${lc} graph generated. nodes: ${srcGraphIsValid ? Object.keys(srcGraph).length : 0}`);
453
+ console.log(`${lc} graph generated. nodes: ${graphIsValid ? Object.keys(fullGraph).length : 0}`);
425
454
  }
426
- const srcIbGibs = srcGraphIsValid ? Object.values(srcGraph) : [];
455
+ const srcIbGibs = graphIsValid ? Object.values(fullGraph) : [];
427
456
  const { mapWithTjp_YesDna: srcMapWithTjp_YesDna, mapWithTjp_NoDna: srcMapWithTjp_NoDna, mapWithoutTjps: src_MapWithoutTjps } = splitPerTjpAndOrDna({ ibGibs: srcIbGibs });
428
- const srcStones = Object.values(src_MapWithoutTjps);
429
- const srcLiving = [...Object.values(srcMapWithTjp_YesDna), ...Object.values(srcMapWithTjp_NoDna)];
430
- const srcTimelinesMap = getTimelinesGroupedByTjp({ ibGibs: srcLiving });
431
- const srcSortedTjps = this.sortTimelinesTopologically(srcTimelinesMap);
432
- return { srcStones, srcTimelinesMap, srcSortedTjps, srcGraph };
457
+ const stones = Object.values(src_MapWithoutTjps);
458
+ const withTimelines = [...Object.values(srcMapWithTjp_YesDna), ...Object.values(srcMapWithTjp_NoDna)];
459
+ const timelinesMap = getTimelinesGroupedByTjp({ ibGibs: withTimelines });
460
+ const topologicallySortedTjpAddrs = this.sortTimelinesTopologically(timelinesMap);
461
+ return { stones, timelinesMap, topologicallySortedTjpAddrs, fullGraph };
433
462
  }
434
463
  /**
435
464
  * Creates the Initial Saga Frame (Init Stage).
@@ -440,57 +469,54 @@ export class SyncSagaCoordinator {
440
469
  * Generates the first frame containing the Knowledge Vector of the Local Space.
441
470
  * This is sent to the Receiver to begin Gap Analysis.
442
471
  */
443
- async createInitFrame({ sagaId, sessionIdentity, localSpace, domainIbGibs, tempSpace, metaspace, conflictStrategy, }) {
472
+ async createInitFrame({ sagaId, sessionIdentity, domainIbGibs, conflictStrategy, metaspace, localSpace, tempSpace, }) {
444
473
  const lc = `${this.lc}[${this.createInitFrame.name}]`;
445
474
  try {
446
475
  if (logalot) {
447
476
  console.log(`${lc} starting... (I: 551af8b411ae9be712ce3358d43ee726)`);
448
477
  }
449
478
  // Analyze Timelines & Stones
450
- const analysis = await this.analyzeTimelines({ domainIbGibs, space: localSpace });
451
- // this is a lot, so uncomment this only if you want even more logging specific to this analysis
479
+ const analysis = await this.analyzeDomainIbGibs({ domainIbGibs, space: localSpace });
452
480
  // if (logalot) { console.log(`${lc} analysis: ${pretty(analysis)}(I: cd00e2be5eccc8976879c888ff2dfb26)`); }
453
- const { srcTimelinesMap, srcGraph, srcStones, } = analysis;
481
+ const { timelinesMap: srcTimelinesMap, fullGraph, stones: srcStones, } = analysis;
482
+ // we need to store the fullGraph in our tempSpace for later...
483
+ await putInSpace({ ibGibs: Object.values(fullGraph), space: tempSpace });
484
+ // populate our knowledge vector with tjp -> latest addr/tip in timeline
485
+ const knowledgeVector = {};
486
+ Object.keys(srcTimelinesMap).forEach(tjp => {
487
+ const timeline = srcTimelinesMap[tjp];
488
+ const tip = timeline.at(-1);
489
+ knowledgeVector[tjp] = getIbGibAddr({ ibGib: tip });
490
+ });
454
491
  const initData = {
455
492
  sagaId,
456
493
  stage: SyncStage.init,
457
- knowledgeVector: {},
494
+ knowledgeVector,
458
495
  identity: sessionIdentity, // KeystoneIbGib is already public data
459
496
  mode: SyncMode.sync,
460
497
  stones: srcStones.map(s => getIbGibAddr({ ibGib: s })),
461
498
  };
462
- // Populate Knowledge Vector
463
- Object.keys(srcTimelinesMap).forEach(tjp => {
464
- const timeline = srcTimelinesMap[tjp];
465
- const tip = timeline.at(-1);
466
- initData.knowledgeVector[tjp] = getIbGibAddr({ ibGib: tip });
467
- });
468
499
  if (logalot) {
469
500
  console.log(`${lc} SyncStage.init: ${SyncStage.init}, SyncStage.commit: ${SyncStage.commit}`);
470
501
  console.log(`${lc} initData.stage: ${initData.stage}`);
471
502
  }
503
+ // create the stone and save/register it
472
504
  const initStone = await this.createSyncMsgStone({
473
505
  data: initData,
474
- space: tempSpace,
475
- metaspace
506
+ localSpace,
507
+ metaspace,
476
508
  });
477
509
  // if (logalot) { console.log(`${lc} initStone: ${pretty(initStone)} (I: 06e532f8a408549069474e96bed44826)`); }
510
+ // create the initial sync ibgib frame, save/register it
478
511
  const sagaFrame = await this.evolveSyncSagaIbGib({
479
512
  msgStones: [initStone],
480
- identity: sessionIdentity,
481
- space: tempSpace,
482
- metaspace,
483
513
  conflictStrategy,
484
- });
485
- // IMMEDIATELY persist to both spaces for audit trail
486
- await this.ensureSagaFrameInBothSpaces({
487
- frame: sagaFrame,
488
- destSpace: localSpace, // localSpace is the Sender's destSpace
489
- tempSpace,
490
- metaspace
514
+ sessionIdentity: sessionIdentity,
515
+ metaspace,
516
+ localSpace,
491
517
  });
492
518
  // if (logalot) { console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`); }
493
- return { sagaFrame, srcGraph };
519
+ return { sagaFrame, initialDomainGraph: fullGraph };
494
520
  }
495
521
  catch (error) {
496
522
  console.error(`${lc} ${extractErrorMsg(error)}`);
@@ -503,24 +529,90 @@ export class SyncSagaCoordinator {
503
529
  }
504
530
  }
505
531
  /**
506
- * Reacts to an incoming saga frame and dispatches to appropriate handler.
532
+ * Helper to poll for streaming domain payloads and put them in the
533
+ * local {@link tempSpace}.
507
534
  *
508
- * @remarks
509
- * **Execution Context**: **Universal (Both Sender and Receiver)**.
535
+ * @returns when all {@link expectedAddrs} are done being transmitted.
536
+ */
537
+ async pollForDomainPayloads({ expectedAddrs, pollIntervalMs, domainPayloadsMap, tempSpace, }) {
538
+ const lc = `${this.lc}[${this.pollForDomainPayloads.name}]`;
539
+ try {
540
+ if (logalot) {
541
+ console.log(`${lc} starting... (I: 26dce86bfca572939885798802d6e926)`);
542
+ }
543
+ let resultDomainPayloads = [];
544
+ let pending = [...expectedAddrs];
545
+ const start = Date.now();
546
+ /**
547
+ * This needs
548
+ */
549
+ const timeoutMs = 5 * 60 * 1000; // 5 minutes...arbitrary at this point. This needs to be pulled out and improved eesh.
550
+ while (pending.length > 0) {
551
+ if (Date.now() - start > timeoutMs) {
552
+ throw new Error(`Timeout waiting for payloads: ${pending.join(', ')} (E: 46e1683c9578095261aaf798bd5e1826)`);
553
+ }
554
+ const stillPending = [];
555
+ const found = [];
556
+ for (const addr of pending) {
557
+ if (domainPayloadsMap.has(addr)) {
558
+ found.push(domainPayloadsMap.get(addr));
559
+ domainPayloadsMap.delete(addr);
560
+ }
561
+ else {
562
+ stillPending.push(addr);
563
+ }
564
+ }
565
+ if (found.length > 0) {
566
+ await putInSpace({ space: tempSpace, ibGibs: found });
567
+ found.forEach(x => resultDomainPayloads.push(x));
568
+ }
569
+ pending = stillPending;
570
+ if (pending.length > 0) {
571
+ await delay(pollIntervalMs);
572
+ }
573
+ }
574
+ if (expectedAddrs.length !== resultDomainPayloads.length) {
575
+ throw new Error(`(UNEXPECTED) expectedAddrs.length !== resultDomainPayloads.length? at this point, we expect all of the payload ibgibs to have been received. (E: 03749a7478c4b8b28bfc86951887a826)`);
576
+ }
577
+ return resultDomainPayloads;
578
+ }
579
+ catch (error) {
580
+ console.error(`${lc} ${extractErrorMsg(error)}`);
581
+ throw error;
582
+ }
583
+ finally {
584
+ if (logalot) {
585
+ console.log(`${lc} complete.`);
586
+ }
587
+ }
588
+ }
589
+ /**
590
+ * This is the heart of the "ping pong" transaction, where we send a context
591
+ * and receive a context. IOW, this drives the FSM of the sync saga ibgib as
592
+ * a whole.
593
+ *
594
+ * This is called in two places:
595
+ *
596
+ * ## 1. Sender
597
+ *
598
+ * On the sender, this is called within the {@link executeSagaLoop} which
599
+ * initiates and drives the sync.
600
+ *
601
+ * ## 2. Receiver
510
602
  *
511
- * This method acts as the "Reducer" for the Sync FSM. It determines the current stage
512
- * based on the incoming frame and delegates to the appropriate handler.
603
+ * On the receiver, this is called directly by the receiving endpoint. That
604
+ * endpoint's job is basically to get these things collocated and prepared
605
+ * to make this call.
513
606
  *
514
- * * If running on **Receiver**: Handles `Init` (via `handleInitFrame`).
515
- * * If running on **Sender**: Handles `Ack` (via `handleAckFrame`).
516
- * * If running on **Either**: Handles `Delta` (via `handleDeltaFrame`) or `Commit`.
607
+ * This is a one-off on the receiver.
517
608
  */
518
- async handleSagaFrame({ sagaIbGib, srcGraph, destSpace, tempSpace, identity, identitySecret, metaspace, }) {
519
- const lc = `${this.lc}[${this.handleSagaFrame.name}]`;
609
+ async handleSagaContext({ sagaContext, mySpace, myTempSpace, identity, identitySecret, metaspace, }) {
610
+ const lc = `${this.lc}[${this.handleSagaContext.name}]`;
520
611
  try {
521
612
  if (logalot) {
522
613
  console.log(`${lc} starting... (I: 5deec8a1f7a6d263c88cd458ad990826)`);
523
614
  }
615
+ const sagaIbGib = sagaContext.sagaFrame;
524
616
  if (!sagaIbGib.data) {
525
617
  throw new Error(`(UNEXPECTED) sagaIbGib.data falsy? (E: 71b938adf1d87c2527bfd4f86dfd0826)`);
526
618
  }
@@ -528,19 +620,31 @@ export class SyncSagaCoordinator {
528
620
  console.log(`${lc} sagaIbGib: ${pretty(sagaIbGib)} (I: 1b99d87d262e9d18d8a607a80b1a0126)`);
529
621
  }
530
622
  // Get Stage from Stone (or Frame for Init fallback)
531
- const { stage, messageData } = await this.getStageAndPayloadFromFrame({ ibGib: sagaIbGib, space: tempSpace });
623
+ const { stage, messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: myTempSpace });
532
624
  if (logalot) {
533
625
  console.log(`${lc} handling frame stage: ${stage}`);
534
626
  }
627
+ /**
628
+ * don't like this name, need to refactor
629
+ */
630
+ const srcGraph = toFlatGraph({ ibGibs: sagaContext.payloadIbGibsDomain }) ?? {};
535
631
  switch (stage) {
536
632
  case SyncStage.init:
537
- return await this.handleInitFrame({ sagaIbGib, messageData, metaspace, destSpace, tempSpace, identity, identitySecret });
633
+ return await this.handleInitFrame({
634
+ sagaIbGib,
635
+ messageData: messageData,
636
+ metaspace,
637
+ mySpace: mySpace,
638
+ myTempSpace: myTempSpace,
639
+ identity,
640
+ identitySecret
641
+ });
538
642
  case SyncStage.ack:
539
- return await this.handleAckFrame({ sagaIbGib, srcGraph, metaspace, destSpace, tempSpace, identity });
643
+ return await this.handleAckFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity });
540
644
  case SyncStage.delta:
541
- return await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace, tempSpace, identity, });
645
+ return await this.handleDeltaFrame({ sagaIbGib, srcGraph, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
542
646
  case SyncStage.commit:
543
- return await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace, tempSpace, identity, });
647
+ return await this.handleCommitFrame({ sagaIbGib, metaspace, destSpace: mySpace, tempSpace: myTempSpace, identity, });
544
648
  default:
545
649
  throw new Error(`${lc} (UNEXPECTED) Unknown sync stage: ${stage} (E: 9c2b4c8a6d34469f8263544710183355)`);
546
650
  }
@@ -568,277 +672,278 @@ export class SyncSagaCoordinator {
568
672
  * 3. Identifies what Receiver needs (`deltaReqAddrs`).
569
673
  * 4. Returns an `Ack` frame containing these lists.
570
674
  */
571
- async handleInitFrame({ sagaIbGib, messageData, destSpace, tempSpace, metaspace, identity, identitySecret, }) {
675
+ async handleInitFrame({ sagaIbGib, messageData, mySpace, myTempSpace, metaspace, identity,
676
+ // identitySecret,
677
+ }) {
572
678
  const lc = `${this.lc}[${this.handleInitFrame.name}]`;
573
- console.log(`${lc} [TEST DEBUG] Received destSpace: ${destSpace.data?.name || destSpace.ib} (uuid: ${destSpace.data?.uuid || '[no uuid]'})`);
574
- if (logalot) {
575
- console.log(`${lc} starting...`);
576
- }
577
- // Extract Init Data
578
- const initData = messageData; // Using renamed variable for clarity
579
- if (initData.stage !== SyncStage.init) {
580
- throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E: 8a2b3c4d5e6f7g8h)`);
581
- }
582
- // if (logalot) { console.log(`${lc} initData: ${pretty(initData)} (I: 46b0f8441b96ad7a388f1ce3239dd826)`); }
583
- if (!initData || !initData.knowledgeVector) {
584
- throw new Error(`${lc} Invalid init frame: missing knowledgeVector (E: ed02c869e028d2d06841b9c7f80f2826)`);
585
- }
586
- // Determine Strategy from Saga Data (since V1 stores it in root)
587
- const conflictStrategy = sagaIbGib.data.conflictStrategy || 'abort';
588
- // 2. Gap Analysis
589
- const conflicts = [];
590
- const deltaReqAddrs = [];
591
- const pushOfferAddrs = [];
592
- // Stones Analysis (Constants / Non-TJPs)
593
- const stones = initData.stones || [];
594
- if (stones.length > 0) {
679
+ try {
595
680
  if (logalot) {
596
- console.log(`${lc} processing stones: ${stones.length}`);
681
+ console.log(`${lc} starting... (I: 9d88dcad0408c029e898a4bcf3b08426)`);
597
682
  }
598
- // Check if we have these stones
599
- const resStones = await getFromSpace({ space: destSpace, addrs: stones });
600
- const addrsNotFound = resStones.rawResultIbGib?.data?.addrsNotFound;
601
- if (addrsNotFound && addrsNotFound.length > 0) {
683
+ console.log(`${lc} [TEST DEBUG] Received destSpace: ${mySpace.data?.name || mySpace.ib} (uuid: ${mySpace.data?.uuid || '[no uuid]'})`);
684
+ if (logalot) {
685
+ console.log(`${lc} starting...`);
686
+ }
687
+ // Extract Init Data
688
+ const initData = messageData; // Using renamed variable for clarity
689
+ if (initData.stage !== SyncStage.init) {
690
+ throw new Error(`${lc} Invalid init frame: initData.stage !== SyncStage.init (E: 8a2b3c4d5e6f7g8h)`);
691
+ }
692
+ // if (logalot) { console.log(`${lc} initData: ${pretty(initData)} (I: 46b0f8441b96ad7a388f1ce3239dd826)`); }
693
+ if (!initData || !initData.knowledgeVector) {
694
+ throw new Error(`${lc} Invalid init frame: missing knowledgeVector (E: ed02c869e028d2d06841b9c7f80f2826)`);
695
+ }
696
+ // Determine Strategy from Saga Data (since V1 stores it in root)
697
+ const conflictStrategy = sagaIbGib.data.conflictStrategy || SyncConflictStrategy.abort;
698
+ // 2. Gap Analysis
699
+ const conflicts = [];
700
+ const deltaReqAddrs = [];
701
+ const pushOfferAddrs = [];
702
+ // First do the consant stones (Non-TJPs)
703
+ const stones = initData.stones || [];
704
+ if (stones.length > 0) {
602
705
  if (logalot) {
603
- console.log(`${lc} stones missing (requesting): ${addrsNotFound.length}`);
706
+ console.log(`${lc} processing stones: ${stones.length}`);
707
+ }
708
+ // Check if we have these stones
709
+ const resStones = await getFromSpace({ space: mySpace, addrs: stones });
710
+ const rawResultIbGib = resStones.rawResultIbGib;
711
+ if (!rawResultIbGib.data) {
712
+ throw new Error(`(UNEXPECTED) rawResultIbGib.data falsy? (E: f3c40b492adc02c3f480b998fc7a5926)`);
604
713
  }
605
- addrsNotFound.forEach(addr => {
606
- if (!deltaReqAddrs.includes(addr)) {
607
- deltaReqAddrs.push(addr);
714
+ const addrsNotFound = rawResultIbGib.data.addrsNotFound;
715
+ if (addrsNotFound && addrsNotFound.length > 0) {
716
+ if (logalot) {
717
+ console.log(`${lc} stones missing (requesting): ${addrsNotFound.length}`);
608
718
  }
609
- });
610
- }
611
- }
612
- const remoteKV = initData.knowledgeVector;
613
- if (logalot) {
614
- console.log(`${lc} remoteKV: ${pretty(remoteKV)} (I: 9f957862356dfeae183c200854e86e26)`);
615
- }
616
- const remoteTjps = Object.keys(remoteKV);
617
- console.log(`${lc} [TEST DEBUG] remoteTjps: ${JSON.stringify(remoteTjps)}`);
618
- if (logalot) {
619
- console.log(`${lc} remoteTjps: ${pretty(remoteTjps)} (I: 86ea4c53db0dc184c8b253386c402126)`);
620
- }
621
- // 1. Get Local Latest Addrs for all TJPs
622
- let localKV = {};
623
- if (remoteTjps.length > 0) {
624
- // Batch get latest addrs for the TJPs
625
- const resGetLatestAddrs = await getLatestAddrs({
626
- space: destSpace,
627
- tjpAddrs: remoteTjps,
628
- });
629
- if (!resGetLatestAddrs.data) {
630
- throw new Error(`(UNEXPECTED) resGetLatestAddrs.data falsy? (E: b180d813c088042b38e1e02e06a16926)`);
631
- }
632
- if (!resGetLatestAddrs.data.latestAddrsMap) {
633
- throw new Error(`(UNEXPECTED) resGetLatestAddrs.data.latestAddrsMap falsy? (E: 16bc386dd51d0ff53a49620b1e641826)`);
719
+ addrsNotFound.forEach(addr => {
720
+ if (!deltaReqAddrs.includes(addr)) {
721
+ deltaReqAddrs.push(addr);
722
+ }
723
+ });
724
+ }
634
725
  }
635
- localKV = resGetLatestAddrs.data.latestAddrsMap;
636
- console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(localKV)}`);
726
+ /**
727
+ * "remote" local to receiver's context is the sender
728
+ */
729
+ const remoteKV = initData.knowledgeVector;
637
730
  if (logalot) {
638
- console.log(`${lc} localKV: ${pretty(localKV)} (I: 980975642cbccd8018cf0cd808d30826)`);
639
- }
640
- }
641
- // 2. Gap Analysis
642
- for (const tjp of remoteTjps) {
643
- const remoteAddr = remoteKV[tjp];
644
- const localAddr = localKV[tjp];
645
- if (!localAddr) {
646
- // We (Receiver) don't have this timeline. Request it.
647
- console.log(`${lc} [TEST DEBUG] Missing local timeline for TJP: ${tjp}. Requesting remoteAddr: ${remoteAddr}`);
648
- deltaReqAddrs.push(remoteAddr);
649
- continue;
650
- }
651
- if (localAddr === remoteAddr) {
652
- // Synced
653
- console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Synced (localAddr === remoteAddr)`);
654
- continue;
655
- }
656
- console.log(`${lc} [TEST DEBUG] TJP ${tjp}: localAddr=${localAddr}, remoteAddr=${remoteAddr} - checking for divergence...`);
657
- // Check if Remote is in Local's PAST (Local is Ahead -> Push Offer)
658
- // (Sender has older version, Receiver has newer) -> Receiver Offers Push
659
- const isRemoteInPast = await isPastFrame({
660
- olderAddr: remoteAddr,
661
- newerAddr: localAddr,
662
- space: destSpace,
663
- });
664
- if (isRemoteInPast) {
665
- console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote is in past - offering push`);
666
- pushOfferAddrs.push(localAddr);
731
+ console.log(`${lc} remoteKV: ${pretty(remoteKV)} (I: 9f957862356dfeae183c200854e86e26)`);
667
732
  }
668
- else {
669
- // Remote is not in our past.
670
- // Either Remote is ahead (Fast-Backward) OR Diverged.
671
- // Check if Local is in Remote's PAST (Remote is Ahead -> Delta Request)
672
- const isLocalInPast = await isPastFrame({
673
- olderAddr: localAddr,
674
- newerAddr: remoteAddr,
675
- space: destSpace,
733
+ const remoteTjps = Object.keys(remoteKV);
734
+ console.log(`${lc} [TEST DEBUG] remoteTjps: ${JSON.stringify(remoteTjps)}`);
735
+ if (logalot) {
736
+ console.log(`${lc} remoteTjps: ${pretty(remoteTjps)} (I: 86ea4c53db0dc184c8b253386c402126)`);
737
+ }
738
+ // 1. Get Local Latest Addrs for all TJPs
739
+ let localKV = {};
740
+ if (remoteTjps.length > 0) {
741
+ // Batch get latest addrs for the TJPs
742
+ const resGetLatestAddrs = await getLatestAddrs({
743
+ space: mySpace, // executing on receiver, so this is receiver's space
744
+ tjpAddrs: remoteTjps,
676
745
  });
677
- if (isLocalInPast) {
678
- // Fast-Forward: We update to remote's tip.
679
- console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Local is in past - requesting delta`);
746
+ if (!resGetLatestAddrs.data) {
747
+ throw new Error(`(UNEXPECTED) resGetLatestAddrs.data falsy? (E: b180d813c088042b38e1e02e06a16926)`);
748
+ }
749
+ if (!resGetLatestAddrs.data.latestAddrsMap) {
750
+ throw new Error(`(UNEXPECTED) resGetLatestAddrs.data.latestAddrsMap falsy? (E: 16bc386dd51d0ff53a49620b1e641826)`);
751
+ }
752
+ localKV = resGetLatestAddrs.data.latestAddrsMap;
753
+ console.log(`${lc} [TEST DEBUG] localKV: ${JSON.stringify(localKV)}`);
754
+ if (logalot) {
755
+ console.log(`${lc} localKV: ${pretty(localKV)} (I: 980975642cbccd8018cf0cd808d30826)`);
756
+ }
757
+ }
758
+ // 2. Gap Analysis
759
+ for (const tjp of remoteTjps) {
760
+ const remoteAddr = remoteKV[tjp];
761
+ const localAddr = localKV[tjp];
762
+ if (!localAddr) {
763
+ // We (Receiver) don't have this timeline at all. Request it.
764
+ console.log(`${lc} [TEST DEBUG] Missing local timeline for TJP: ${tjp}. Requesting remoteAddr: ${remoteAddr}`);
680
765
  deltaReqAddrs.push(remoteAddr);
766
+ continue;
767
+ }
768
+ if (localAddr === remoteAddr) {
769
+ // Synced
770
+ console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Synced (localAddr === remoteAddr)`);
771
+ continue;
772
+ }
773
+ console.log(`${lc} [TEST DEBUG] TJP ${tjp}: localAddr=${localAddr}, remoteAddr=${remoteAddr} - checking for divergence...`);
774
+ // Check if Remote is in Local's PAST (Local is Ahead -> Push Offer)
775
+ // (Sender has older version, Receiver has newer) -> Receiver Offers Push
776
+ const isRemoteInPast = await isPastFrame({
777
+ olderAddr: remoteAddr,
778
+ newerAddr: localAddr,
779
+ space: mySpace,
780
+ });
781
+ if (isRemoteInPast) {
782
+ console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Remote is in past - offering push`);
783
+ pushOfferAddrs.push(localAddr);
681
784
  }
682
785
  else {
683
- // DIVERGENCE: Both have changes the other doesn't know about.
684
- console.log(`${lc} [TEST DEBUG] TJP ${tjp}: DIVERGENCE DETECTED! conflictStrategy=${conflictStrategy}`);
685
- if (conflictStrategy === 'abort') {
686
- // Abort Strategy: We will treat this as terminal.
687
- // But for Unified Ack, we just mark it terminal in the list?
688
- // Or do we actually throw/abort the saga?
689
- // Current logic (below) aborts the saga if ANY conflict is terminal/abort.
690
- conflicts.push({
691
- tjpAddr: tjp,
692
- localAddr: localAddr,
693
- remoteAddr,
694
- timelineAddrs: [], // Not needed for abort
695
- reason: 'divergence',
696
- terminal: true
697
- });
698
- }
699
- else if (conflictStrategy === 'optimistic') {
700
- // Optimistic: We want to resolving this.
701
- // We need to send our history to the Sender so they can Merge.
702
- // Fetch Full History for Local Timeline
703
- // Note: We might optimize this to only send "recent" history if we had a KV?
704
- // But for now, get full past.
705
- // Optimization: localKV might not have full history.
706
- // We need to inspect the 'past' of the local tip.
707
- // We need the ACTUAL object to get the past.
708
- // We have localAddr.
709
- const resLocalTip = await getFromSpace({ space: destSpace, addr: localAddr });
710
- const localTip = resLocalTip.ibGibs?.[0];
711
- if (!localTip) {
712
- throw new Error(`${lc} Failed to load local tip for conflict resolution. (E: 8f9b2c3d4e5f6g7h)`);
713
- }
714
- const timelineAddrs = [localAddr, ...(localTip.rel8ns?.past || [])];
715
- conflicts.push({
716
- tjpAddr: tjp,
717
- localAddr: localAddr,
718
- remoteAddr,
719
- timelineAddrs,
720
- reason: 'divergence',
721
- terminal: false
722
- });
786
+ // Remote is not in our past.
787
+ // Either Remote is ahead (Fast-Backward) OR Diverged.
788
+ // Check if Local is in Remote's PAST (Remote is Ahead -> Delta Request)
789
+ const isLocalInPast = await isPastFrame({
790
+ olderAddr: localAddr,
791
+ newerAddr: remoteAddr,
792
+ space: mySpace,
793
+ });
794
+ if (isLocalInPast) {
795
+ // Fast-Forward: We update to remote's tip.
796
+ console.log(`${lc} [TEST DEBUG] TJP ${tjp}: Local is in past - requesting delta`);
797
+ deltaReqAddrs.push(remoteAddr);
723
798
  }
724
799
  else {
725
- throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy} (E: 2a9b3c4d5e6f7g8h9i0j)`);
800
+ // DIVERGENCE: Both have changes the other doesn't know about.
801
+ console.log(`${lc} [TEST DEBUG] TJP ${tjp}: DIVERGENCE DETECTED! conflictStrategy=${conflictStrategy}`);
802
+ if (conflictStrategy === 'abort') {
803
+ // Abort Strategy: We will treat this as terminal.
804
+ // But for Unified Ack, we just mark it terminal in the list?
805
+ // Or do we actually throw/abort the saga?
806
+ // Current logic (below) aborts the saga if ANY conflict is terminal/abort.
807
+ conflicts.push({
808
+ tjpAddr: tjp,
809
+ localAddr: localAddr,
810
+ remoteAddr,
811
+ timelineAddrs: [], // Not needed for abort
812
+ reason: 'divergence',
813
+ terminal: true
814
+ });
815
+ }
816
+ else if (conflictStrategy === 'optimistic') {
817
+ // Optimistic: We want to resolving this.
818
+ // We need to send our history to the Sender so they can Merge.
819
+ // Fetch Full History for Local Timeline
820
+ // Note: We might optimize this to only send "recent" history if we had a KV?
821
+ // But for now, get full past.
822
+ // Optimization: localKV might not have full history.
823
+ // We need to inspect the 'past' of the local tip.
824
+ // We need the ACTUAL object to get the past.
825
+ // We have localAddr.
826
+ const resLocalTip = await getFromSpace({ space: mySpace, addr: localAddr });
827
+ if (!resLocalTip.success || resLocalTip.ibGibs?.length !== 1) {
828
+ throw new Error(`couldn't get local tip (${localAddr}) from space (${mySpace.ib}) (E: ff06ff849fa8e8dba32ce09807411226)`);
829
+ }
830
+ const localTip = resLocalTip.ibGibs[0];
831
+ if (!localTip) {
832
+ throw new Error(`${lc} Failed to load local tip for conflict resolution. (E: 8f9b2c3d4e5f6g7h)`);
833
+ }
834
+ const timelineAddrs = [...(localTip.rel8ns?.past ?? []), localAddr];
835
+ conflicts.push({
836
+ tjpAddr: tjp,
837
+ localAddr,
838
+ remoteAddr,
839
+ timelineAddrs,
840
+ reason: 'divergence',
841
+ terminal: false
842
+ });
843
+ }
844
+ else {
845
+ throw new Error(`${lc} Unsupported conflict strategy: ${conflictStrategy} (E: 2a9b3c4d5e6f7g8h9i0j)`);
846
+ }
726
847
  }
727
848
  }
728
849
  }
729
- }
730
- // Check if we should ABORT (if any conflict is terminal)
731
- const hasTerminalConflicts = conflicts.some(c => c.terminal);
732
- if (hasTerminalConflicts) {
733
- // Abort Strategy: Kill the saga.
734
- if (logalot) {
735
- console.warn(`${lc} ABORTING Sync Saga due to terminal conflicts: ${JSON.stringify(conflicts)}`);
736
- }
737
- // We reuse the ConflictData structure for terminal aborts?
738
- // Or do we send an Ack with terminal conflicts?
739
- // Original design had explicit Conflict Frame for Abort.
740
- // Let's stick to that for purely terminal cases to be safe/explicit?
741
- // Or Unified: Just send Ack with terminal=true conflicts. Sender sees them and aborts.
742
- // Decision: Unified Ack for everything is cleaner protocol.
743
- // But wait, the original code below creates a Conflict Stone.
744
- // Let's preserve the explicit 'Conflict' frame for total aborts if that's easier,
745
- // OR fully switch to Ack.
746
- // Protocol states: Init -> Ack. If Ack contains terminal errors, Sender can Commit(Fail).
747
- // Let's use Ack with conflicts.
748
- }
749
- // 2. Add Push Offers (Missing in Local)
750
- // Check if we have them. If not, ask for them.
751
- for (const addr of pushOfferAddrs) {
752
- const hasIt = await getFromSpace({ addr, space: destSpace });
753
- if (!hasIt.success || !hasIt.ibGibs || hasIt.ibGibs.length === 0) {
754
- // If we don't have it, we put it in `deltaReqAddrs` of the Ack.
755
- deltaReqAddrs.push(addr);
756
- }
757
- }
758
- // 3. Build Knowledge Vector (Full History for known timelines)
759
- // [NEW] Smart Diff
760
- // We iterate over all relevant addresses (deltas we are requesting OR push offers we might have newer versions of).
761
- // Since we are "reacting" to Init, we primarily want to tell the Sender what we DO have for the things they talked about.
762
- const knowledgeVector = {};
763
- const relevantAddrs = new Set([...pushOfferAddrs, ...deltaReqAddrs]);
764
- // [Smart Diff] Populate knowledge from timelines identified by Sender
765
- for (const tjp of remoteTjps) {
766
- const localAddr = localKV[tjp];
767
- if (localAddr) {
768
- const res = await getFromSpace({ addr: localAddr, space: destSpace });
769
- if (res.success && res.ibGibs?.[0]) {
770
- const ibGib = res.ibGibs[0];
771
- const realTjp = ibGib.rel8ns?.tjp?.[0] || getIbGibAddr({ ibGib }); // Should match `tjp` if normalized
772
- if (!knowledgeVector[realTjp]) {
773
- const past = ibGib.rel8ns?.past || [];
774
- knowledgeVector[realTjp] = [getIbGibAddr({ ibGib }), ...past];
850
+ // Check if we should ABORT (if any conflict is terminal)
851
+ const hasTerminalConflicts = conflicts.some(c => c.terminal);
852
+ if (hasTerminalConflicts) {
853
+ // Abort Strategy: Kill the saga.
854
+ if (logalot) {
855
+ console.warn(`${lc} ABORTING Sync Saga due to terminal conflicts: ${JSON.stringify(conflicts)}`);
856
+ }
857
+ throw new Error(`the saga has terminal conflicts. conflicts: ${JSON.stringify(conflicts)} (E: f2edbe93cc07a63b38bfc013d2213b26)`);
858
+ }
859
+ // 3. Build Knowledge Vector (Full History for known timelines)
860
+ // [NEW] Smart Diff
861
+ // We iterate over all relevant addresses (deltas we are requesting OR push offers we might have newer versions of).
862
+ // Since we are "reacting" to Init, we primarily want to tell the Sender what we DO have for the things they talked about.
863
+ /**
864
+ * we will put this in {@link SyncSagaMessageAckData_V1.knowledgeVector}
865
+ */
866
+ const knowledgeVector = {};
867
+ // [Smart Diff] Populate knowledge from timelines identified by Sender
868
+ for (const tjp of remoteTjps) {
869
+ const localAddr = localKV[tjp];
870
+ if (localAddr) {
871
+ const res = await getFromSpace({ addr: localAddr, space: mySpace });
872
+ if (res.success && res.ibGibs?.[0]) {
873
+ const ibGib = res.ibGibs[0];
874
+ const realTjp = ibGib.rel8ns?.tjp?.[0] || getIbGibAddr({ ibGib }); // Should match `tjp` if normalized
875
+ if (!knowledgeVector[realTjp]) {
876
+ const past = ibGib.rel8ns?.past || [];
877
+ knowledgeVector[realTjp] = [getIbGibAddr({ ibGib }), ...past];
878
+ }
775
879
  }
776
880
  }
777
881
  }
778
- }
779
- // [Smart Diff] Also check individual requested addresses (Fall back for constants/unknown TJPs)
780
- for (const addr of relevantAddrs) {
781
- // Only process if not already covered by TJP logic above
782
- // We can't really know if it's covered easily without resolving.
783
- // But if we don't have it (requesting), we won't find it here anyway.
784
- // If we DO have it (push offer), we might find it.
785
- const res = await getFromSpace({ addr, space: destSpace });
786
- if (res.success && res.ibGibs?.[0]) {
787
- const ibGib = res.ibGibs[0];
788
- const tjpAddr = ibGib.rel8ns?.tjp?.[0] || getIbGibAddr({ ibGib });
789
- if (!knowledgeVector[tjpAddr]) {
790
- const past = ibGib.rel8ns?.past || [];
791
- knowledgeVector[tjpAddr] = [getIbGibAddr({ ibGib }), ...past];
792
- }
882
+ // Also populate from `knowledgeVector` in Init if we want bidirectional?
883
+ // No, `Init` doesn't have `knowledgeVector` in `SyncSagaMessageInitData` yet (it has `SyncInitData` generic props).
884
+ // Let's assume standard flow:
885
+ // 1. Sender says "I have X"
886
+ // 2. Receiver says "I don't have X. But if I did have Y (ancestor), I'd tell you."
887
+ // Problem: Receiver doesn't know X is related to Y without X.
888
+ // SOLUTION:
889
+ // The *Sender* must include TJP mappings or we rely on `knowledgeVector` in `Init`?
890
+ // `SyncSagaMessageInitData_V1` extends `SyncInitData`.
891
+ // `SyncInitData` has `knowledgeVector`.
892
+ // If Sender populates `knowledgeVector` in `Init`, Receiver can use keys (TJPs) to look up its own state!
893
+ // Let's implement Sender populating `Init.knowledgeVector`.
894
+ // But `SyncSagaCoordinator.startSaga` creates Init.
895
+ // 3. Create Ack Frame
896
+ const sagaId = sagaIbGib.data.uuid;
897
+ // Create Payload Stone
898
+ const ackData = {
899
+ sagaId,
900
+ stage: SyncStage.ack,
901
+ deltaReqAddrs,
902
+ pushOfferAddrs,
903
+ knowledgeVector,
904
+ };
905
+ if (conflicts?.length > 0) {
906
+ ackData.conflicts = conflicts;
793
907
  }
794
- else {
795
- // We don't have `addr`.
908
+ const ackStone = await this.createSyncMsgStone({
909
+ data: ackData,
910
+ localSpace: myTempSpace,
911
+ metaspace,
912
+ });
913
+ if (logalot) {
914
+ console.log(`${lc} ackStone created: ${pretty(ackStone)} (I: 313708132dd53ff946befb7833657826)`);
796
915
  }
916
+ // Evolve Saga
917
+ const ackFrame = await this.evolveSyncSagaIbGib({
918
+ prevSagaIbGib: sagaIbGib,
919
+ msgStones: [ackStone],
920
+ sessionIdentity: identity,
921
+ localSpace: mySpace,
922
+ metaspace,
923
+ });
924
+ // if (logalot) { console.log(`${lc} ackFrame created: ${pretty(ackFrame)} (I: be24480592eec478086bb3da49286826)`); }
925
+ /**
926
+ * we want to push ibgibs to the remote/sender if we have push
927
+ * offers. an ack frame's payloads, if any, are those push offers
928
+ */
929
+ // let payloadIbGibsDomain: IbGib_V1[] | undefined = await getPushOffers
930
+ if (pushOfferAddrs.length > 0) {
931
+ }
932
+ return {
933
+ frame: ackFrame,
934
+ // conflictInfos,
935
+ // payloadIbGibsDomain,
936
+ };
797
937
  }
798
- // Also populate from `knowledgeVector` in Init if we want bidirectional?
799
- // No, `Init` doesn't have `knowledgeVector` in `SyncSagaMessageInitData` yet (it has `SyncInitData` generic props).
800
- // Let's assume standard flow:
801
- // 1. Sender says "I have X"
802
- // 2. Receiver says "I don't have X. But if I did have Y (ancestor), I'd tell you."
803
- // Problem: Receiver doesn't know X is related to Y without X.
804
- // SOLUTION:
805
- // The *Sender* must include TJP mappings or we rely on `knowledgeVector` in `Init`?
806
- // `SyncSagaMessageInitData_V1` extends `SyncInitData`.
807
- // `SyncInitData` has `knowledgeVector`.
808
- // If Sender populates `knowledgeVector` in `Init`, Receiver can use keys (TJPs) to look up its own state!
809
- // Let's implement Sender populating `Init.knowledgeVector`.
810
- // But `SyncSagaCoordinator.startSaga` creates Init.
811
- // 3. Create Ack Frame
812
- const sagaId = sagaIbGib.data.uuid;
813
- // Create Payload Stone
814
- const ackData = {
815
- sagaId,
816
- stage: SyncStage.ack,
817
- deltaReqAddrs,
818
- pushOfferAddrs,
819
- knowledgeVector,
820
- conflicts: conflicts.length > 0 ? conflicts : undefined, // Include conflicts if any detected
821
- };
822
- const ackStone = await this.createSyncMsgStone({
823
- data: ackData,
824
- space: tempSpace,
825
- metaspace,
826
- });
827
- if (logalot) {
828
- console.log(`${lc} ackStone created: ${pretty(ackStone)} (I: 313708132dd53ff946befb7833657826)`);
938
+ catch (error) {
939
+ console.error(`${lc} ${extractErrorMsg(error)}`);
940
+ throw error;
941
+ }
942
+ finally {
943
+ if (logalot) {
944
+ console.log(`${lc} complete.`);
945
+ }
829
946
  }
830
- // Evolve Saga
831
- const ackFrame = await this.evolveSyncSagaIbGib({
832
- prevSagaIbGib: sagaIbGib,
833
- msgStones: [ackStone],
834
- identity,
835
- space: tempSpace,
836
- metaspace,
837
- });
838
- // IMMEDIATELY persist to both spaces for audit trail (before any errors can occur)
839
- await this.ensureSagaFrameInBothSpaces({ frame: ackFrame, destSpace, tempSpace, metaspace });
840
- // if (logalot) { console.log(`${lc} ackFrame created: ${pretty(ackFrame)} (I: be24480592eec478086bb3da49286826)`); }
841
- return { frame: ackFrame };
842
947
  }
843
948
  /**
844
949
  * Handles the `Ack` frame.
@@ -858,7 +963,7 @@ export class SyncSagaCoordinator {
858
963
  if (logalot) {
859
964
  console.log(`${lc} starting... (I: 605b6860e898267a5b50c6d85704be26)`);
860
965
  }
861
- const { messageData, } = await this.getStageAndPayloadFromFrame({ ibGib: sagaIbGib, space: tempSpace });
966
+ const { messageData, } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
862
967
  const ackData = messageData;
863
968
  if (!ackData) {
864
969
  throw new Error(`${lc} ackData falsy (E: 3b8415edc876084c88a25b98e2d55826)`);
@@ -938,50 +1043,7 @@ export class SyncSagaCoordinator {
938
1043
  // PULL these frames from Peer into Local Space
939
1044
  // (Validation: We trust peer for now / verification happens on put)
940
1045
  for (const addr of receiverOnlyAddrs) {
941
- // This 'pull' is a sync-peer method?
942
- // The Coordinator 'peer' passed in 'sync()' might be needed here?
943
- // Wait, `handleAckFrame` doesn't have reference to `peer`?
944
- // It only has `space`, `metaspace`.
945
- // The `peer` is held by the `executeSagaLoop`.
946
- // PROBLEM: `handleAckFrame` is pure logic on the Space/Data?
947
- // No, it's a method on Coordinator.
948
- // But `executeSagaLoop` calls it.
949
- // We might need to return "Requirements" to the loop?
950
- // Checking return type: `{ frame: SyncIbGib_V1, payloadIbGibs?: ... }`
951
- // It returns the NEXT frame (Delta).
952
- // If we need to fetch data, we are blocked.
953
- // We can't easily "Pull" here without the Peer reference.
954
- // OPTION A: Pass `peer` to `handleAckFrame`.
955
- // OPTION B: Return a strict list of "MissingDeps" and let Loop handle it.
956
- // Let's assume we can resolve this by adding `peer` to signature or using `metaspace` if it's a peer-witness?
957
- // No, Peer is ephemeral connection.
958
- // Let's add `peer` to `handleAckFrame` signature?
959
- // It breaks the pattern of just handling frame + space.
960
- // ALTERNATIVE: Use the `Delta` frame to request data?
961
- // `SyncSagaMessageDeltaData` has `requests?: string[]`.
962
- // Sender sends Delta Frame.
963
- // Does Receiver handle Delta Requests?
964
- // `handleDeltaFrame` (Receiver) -> checks `requests`.
965
- // YES.
966
- // So Sender puts `receiverOnlyAddrs` into `deltaFrame.requests`.
967
- // Receiver sees them, fetches them, and includes them in the Response (Commit?).
968
- // Wait, Init->Ack->Delta->Commit.
969
- // If Receiver sends data in Commit, that's "too late" for Sender to Merge in THIS saga round?
970
- // Unless Commit is not the end?
971
- // Or we do a "Delta 2" loop?
972
- // "Iterative Resolution Loop" from plan.
973
- // If we request data in Delta, Receiver sends it in Commit (or Delta-Response).
974
- // Sender gets Commit. Sees data. Merges.
975
- // Then Sender needs to Send the MERGE result.
976
- // Needs another Push/Delta.
977
- // REFINED FLOW:
978
- // 1. Sender sends Delta Frame with `requests: [receiverOnlyAddrs]`.
979
- // 2. Receiver responds (Commit? or Ack 2?) with Payload (Divergent Frames).
980
- // 3. Sender handles response -> Merges.
981
- // 4. Sender sends Commit (containing Merge Frame).
982
- // Issue: Current state machine is Init->Ack->Delta->Commit.
983
- // We need to keep Saga open.
984
- // If Sender sends Delta with requests, does it transition to Commit?
1046
+ console.error(`${lc} [CONFLICT DEBUG] NOT IMPLEMENTED (E: e6bf1a9d2758c469bb2f97514062d826)`);
985
1047
  }
986
1048
  // Compute DELTA dependencies for each receiver-only frame
987
1049
  // Find LCA to determine what dependencies we already have
@@ -1138,22 +1200,26 @@ export class SyncSagaCoordinator {
1138
1200
  }
1139
1201
  const deltaStone = await this.createSyncMsgStone({
1140
1202
  data: deltaData,
1141
- space: tempSpace,
1203
+ localSpace: tempSpace,
1142
1204
  metaspace,
1143
1205
  });
1144
1206
  const deltaFrame = await this.evolveSyncSagaIbGib({
1145
1207
  prevSagaIbGib: sagaIbGib,
1146
1208
  msgStones: [deltaStone],
1147
- identity,
1148
- space: tempSpace,
1209
+ sessionIdentity: identity,
1210
+ localSpace: tempSpace,
1149
1211
  metaspace,
1150
1212
  });
1151
- // IMMEDIATELY persist to both spaces for audit trail
1152
- await this.ensureSagaFrameInBothSpaces({ frame: deltaFrame, destSpace, tempSpace, metaspace });
1153
1213
  if (logalot) {
1154
1214
  console.log(`${lc} Delta Frame created. Rel8ns: ${JSON.stringify(deltaFrame.rel8ns)}`);
1155
1215
  }
1156
- return { frame: deltaFrame, payloadIbGibs };
1216
+ // Build control payloads: frame + its dependencies (msg stone, identity)
1217
+ const payloadIbGibsControl = [deltaFrame, deltaStone];
1218
+ if (identity) {
1219
+ payloadIbGibsControl.push(identity);
1220
+ }
1221
+ // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: payloadIbGibs };
1222
+ return { frame: deltaFrame, payloadIbGibsDomain: payloadIbGibs };
1157
1223
  }
1158
1224
  catch (error) {
1159
1225
  console.error(`${lc} ${extractErrorMsg(error)}`);
@@ -1180,7 +1246,7 @@ export class SyncSagaCoordinator {
1180
1246
  if (logalot) {
1181
1247
  console.log(`${lc} starting...`);
1182
1248
  }
1183
- const { messageData } = await this.getStageAndPayloadFromFrame({ ibGib: sagaIbGib, space: tempSpace });
1249
+ const { messageData } = await this.getStageAndPayloadFromFrame({ sagaFrame: sagaIbGib, space: tempSpace });
1184
1250
  const deltaData = messageData;
1185
1251
  if (!deltaData) {
1186
1252
  throw new Error(`${lc} deltaData falsy (E: 7c28c8d8f08a4421b8344e6727271421)`);
@@ -1366,19 +1432,23 @@ export class SyncSagaCoordinator {
1366
1432
  responseDeltaData.proposeCommit = true;
1367
1433
  const deltaStone = await this.createSyncMsgStone({
1368
1434
  data: responseDeltaData,
1369
- space: tempSpace,
1435
+ localSpace: tempSpace,
1370
1436
  metaspace
1371
1437
  });
1372
1438
  const deltaFrame = await this.evolveSyncSagaIbGib({
1373
1439
  prevSagaIbGib: sagaIbGib,
1374
1440
  msgStones: [deltaStone],
1375
- identity,
1376
- space: tempSpace,
1441
+ sessionIdentity: identity,
1442
+ localSpace: tempSpace,
1377
1443
  metaspace
1378
1444
  });
1379
- // IMMEDIATELY persist to both spaces for audit trail
1380
- await this.ensureSagaFrameInBothSpaces({ frame: deltaFrame, destSpace, tempSpace, metaspace });
1381
- return { frame: deltaFrame, payloadIbGibs: outgoingPayload, receivedPayloadIbGibs };
1445
+ // Build control payloads: frame + its dependencies (msg stone, identity)
1446
+ const payloadIbGibsControl = [deltaFrame, deltaStone];
1447
+ if (identity) {
1448
+ payloadIbGibsControl.push(identity);
1449
+ }
1450
+ // return { frame: deltaFrame, payloadIbGibsControl, payloadIbGibsDomain: outgoingPayload };
1451
+ return { frame: deltaFrame, payloadIbGibsDomain: outgoingPayload };
1382
1452
  }
1383
1453
  else {
1384
1454
  // We have nothing to send.
@@ -1391,19 +1461,23 @@ export class SyncSagaCoordinator {
1391
1461
  };
1392
1462
  const commitStone = await this.createSyncMsgStone({
1393
1463
  data: commitData,
1394
- space: tempSpace,
1464
+ localSpace: tempSpace,
1395
1465
  metaspace
1396
1466
  });
1397
1467
  const commitFrame = await this.evolveSyncSagaIbGib({
1398
1468
  prevSagaIbGib: sagaIbGib,
1399
1469
  msgStones: [commitStone],
1400
- identity,
1401
- space: tempSpace,
1470
+ sessionIdentity: identity,
1471
+ localSpace: tempSpace,
1402
1472
  metaspace
1403
1473
  });
1404
- // IMMEDIATELY persist to both spaces for audit trail
1405
- await this.ensureSagaFrameInBothSpaces({ frame: commitFrame, destSpace, tempSpace, metaspace });
1406
- return { frame: commitFrame, receivedPayloadIbGibs };
1474
+ // Build control payloads for commit
1475
+ const commitCtrlPayloads = [commitFrame, commitStone];
1476
+ if (identity) {
1477
+ commitCtrlPayloads.push(identity);
1478
+ }
1479
+ // return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads };
1480
+ return { frame: commitFrame, };
1407
1481
  }
1408
1482
  else {
1409
1483
  // peer did NOT propose commit (maybe they just sent data/requests and didn't ready flag).
@@ -1417,18 +1491,16 @@ export class SyncSagaCoordinator {
1417
1491
  };
1418
1492
  const deltaStone = await this.createSyncMsgStone({
1419
1493
  data: responseDeltaData,
1420
- space: tempSpace,
1494
+ localSpace: tempSpace,
1421
1495
  metaspace
1422
1496
  });
1423
1497
  const deltaFrame = await this.evolveSyncSagaIbGib({
1424
1498
  prevSagaIbGib: sagaIbGib,
1425
1499
  msgStones: [deltaStone],
1426
- identity,
1427
- space: tempSpace,
1500
+ sessionIdentity: identity,
1501
+ localSpace: tempSpace,
1428
1502
  metaspace
1429
1503
  });
1430
- // IMMEDIATELY persist to both spaces for audit trail
1431
- await this.ensureSagaFrameInBothSpaces({ frame: deltaFrame, destSpace, tempSpace, metaspace });
1432
1504
  // Check if PEER proposed commit
1433
1505
  if (deltaData.proposeCommit) {
1434
1506
  if (logalot) {
@@ -1443,21 +1515,31 @@ export class SyncSagaCoordinator {
1443
1515
  };
1444
1516
  const commitStone = await this.createSyncMsgStone({
1445
1517
  data: commitData,
1446
- space: tempSpace,
1518
+ localSpace: tempSpace,
1447
1519
  metaspace
1448
1520
  });
1449
1521
  const commitFrame = await this.evolveSyncSagaIbGib({
1450
1522
  prevSagaIbGib: deltaFrame, // Build on top of the Delta we just created/persisted
1451
1523
  msgStones: [commitStone],
1452
- identity,
1453
- space: tempSpace,
1524
+ sessionIdentity: identity,
1525
+ localSpace: tempSpace,
1454
1526
  metaspace
1455
1527
  });
1456
- // IMMEDIATELY persist to both spaces for audit trail
1457
- await this.ensureSagaFrameInBothSpaces({ frame: commitFrame, destSpace, tempSpace, metaspace });
1458
- return { frame: commitFrame, receivedPayloadIbGibs };
1528
+ // Build control payloads for commit
1529
+ const commitCtrlPayloads2 = [commitFrame, commitStone];
1530
+ if (identity) {
1531
+ commitCtrlPayloads2.push(identity);
1532
+ }
1533
+ // return { frame: commitFrame, payloadIbGibsControl: commitCtrlPayloads2 };
1534
+ return { frame: commitFrame, };
1535
+ }
1536
+ // Build control payloads for delta propose
1537
+ const deltaCtrlPayloads = [deltaFrame, deltaStone];
1538
+ if (identity) {
1539
+ deltaCtrlPayloads.push(identity);
1459
1540
  }
1460
- return { frame: deltaFrame, receivedPayloadIbGibs };
1541
+ // return { frame: deltaFrame, payloadIbGibsControl: deltaCtrlPayloads };
1542
+ return { frame: deltaFrame, };
1461
1543
  }
1462
1544
  }
1463
1545
  }
@@ -1480,7 +1562,7 @@ export class SyncSagaCoordinator {
1480
1562
  return null;
1481
1563
  }
1482
1564
  // #endregion Handlers
1483
- async createSyncMsgStone({ data, space, metaspace, }) {
1565
+ async createSyncMsgStone({ data, localSpace, metaspace, }) {
1484
1566
  const lc = `${this.lc}[${this.createSyncMsgStone.name}]`;
1485
1567
  try {
1486
1568
  if (logalot) {
@@ -1496,7 +1578,7 @@ export class SyncSagaCoordinator {
1496
1578
  if (logalot) {
1497
1579
  console.log(`${lc} Created stone: ${getIbGibAddr({ ibGib: stone })}`);
1498
1580
  }
1499
- await putInSpace({ space, ibGib: stone });
1581
+ await metaspace.put({ ibGib: stone, space: localSpace });
1500
1582
  await metaspace.registerNewIbGib({ ibGib: stone });
1501
1583
  return stone;
1502
1584
  }
@@ -1510,41 +1592,10 @@ export class SyncSagaCoordinator {
1510
1592
  }
1511
1593
  }
1512
1594
  }
1513
- /**
1514
- * Ensures saga frame and its msg stone(s) are in BOTH spaces for audit trail.
1515
- * Control ibgibs (saga frames, msg stones, identity) must be in both destSpace and tempSpace.
1516
- */
1517
- async ensureSagaFrameInBothSpaces({ frame, destSpace, tempSpace, metaspace, }) {
1518
- // Frame itself (already in tempSpace from creation, need in destSpace for audit)
1519
- await putInSpace({ space: destSpace, ibGib: frame });
1520
- await metaspace.registerNewIbGib({ ibGib: frame });
1521
- // Msg stone(s) (already in tempSpace, need in destSpace for audit)
1522
- const msgStoneAddrs = frame.rel8ns?.[SYNC_MSG_REL8N_NAME];
1523
- if (msgStoneAddrs && msgStoneAddrs.length > 0) {
1524
- const resMsgStones = await getFromSpace({ space: tempSpace, addrs: msgStoneAddrs });
1525
- if (resMsgStones.ibGibs) {
1526
- for (const msgStone of resMsgStones.ibGibs) {
1527
- await putInSpace({ space: destSpace, ibGib: msgStone });
1528
- await metaspace.registerNewIbGib({ ibGib: msgStone });
1529
- }
1530
- }
1531
- }
1532
- // Identity (if present, already in tempSpace, need in destSpace)
1533
- const identityAddrs = frame.rel8ns?.identity;
1534
- if (identityAddrs && identityAddrs.length > 0) {
1535
- const resIdentity = await getFromSpace({ space: tempSpace, addrs: identityAddrs });
1536
- if (resIdentity.ibGibs) {
1537
- for (const identity of resIdentity.ibGibs) {
1538
- await putInSpace({ space: destSpace, ibGib: identity });
1539
- await metaspace.registerNewIbGib({ ibGib: identity });
1540
- }
1541
- }
1542
- }
1543
- }
1544
1595
  /**
1545
1596
  * Evolves the saga timeline with a new frame.
1546
1597
  */
1547
- async evolveSyncSagaIbGib({ prevSagaIbGib, msgStones, identity, space, metaspace, conflictStrategy, }) {
1598
+ async evolveSyncSagaIbGib({ prevSagaIbGib, conflictStrategy, msgStones, sessionIdentity, localSpace, metaspace, }) {
1548
1599
  const lc = `${this.lc}[${this.evolveSyncSagaIbGib.name}]`;
1549
1600
  try {
1550
1601
  // Validation
@@ -1567,7 +1618,7 @@ export class SyncSagaCoordinator {
1567
1618
  throw new Error(`${lc} Mismatched stage in stones. Expected ${stage}, got ${d.stage} (E: d12c6571b0882f762921b60880c3f826)`);
1568
1619
  }
1569
1620
  }
1570
- const identityAddr = identity ? getIbGibAddr({ ibGib: identity }) : undefined;
1621
+ const identityAddr = sessionIdentity ? getIbGibAddr({ ibGib: sessionIdentity }) : undefined;
1571
1622
  if (prevSagaIbGib) {
1572
1623
  /**
1573
1624
  * rel8ns always include the new msg stone(s)
@@ -1579,8 +1630,8 @@ export class SyncSagaCoordinator {
1579
1630
  }
1580
1631
  ];
1581
1632
  // if we're authenticating/signing, we'll have identity
1582
- if (identity) {
1583
- rel8nInfos.push({ rel8nName: 'identity', ibGibs: [identity], });
1633
+ if (sessionIdentity) {
1634
+ rel8nInfos.push({ rel8nName: 'identity', ibGibs: [sessionIdentity], });
1584
1635
  }
1585
1636
  // remove the existing sync msg stones' addrs
1586
1637
  if (!prevSagaIbGib.rel8ns) {
@@ -1601,7 +1652,7 @@ export class SyncSagaCoordinator {
1601
1652
  rel8nInfos,
1602
1653
  rel8nRemovalInfos,
1603
1654
  metaspace,
1604
- space,
1655
+ space: localSpace,
1605
1656
  noDna: true, // Explicitly no DNA for sync frames
1606
1657
  });
1607
1658
  const newFrame = resAppend;
@@ -1609,31 +1660,36 @@ export class SyncSagaCoordinator {
1609
1660
  }
1610
1661
  else {
1611
1662
  // Create New Timeline (Root Frame)
1663
+ // data
1612
1664
  const data = {
1613
1665
  uuid: sagaId,
1614
- // stage, // Removed from V1
1615
- payload: undefined, // Data in stone
1616
1666
  n: 0,
1617
1667
  isTjp: true,
1618
1668
  conflictStrategy,
1619
1669
  };
1670
+ // ib
1620
1671
  const ib = await getSyncIb({ data });
1672
+ // rel8ns
1621
1673
  const stoneAddrs = msgStones.map(s => getIbGibAddr({ ibGib: s }));
1622
- const rel8ns = {
1623
- [SYNC_MSG_REL8N_NAME]: stoneAddrs,
1624
- };
1674
+ const rel8ns = { [SYNC_MSG_REL8N_NAME]: stoneAddrs, };
1625
1675
  if (identityAddr) {
1626
1676
  rel8ns.identity = [identityAddr];
1627
1677
  }
1628
1678
  const resNew = await createTimeline({
1629
- space,
1679
+ space: localSpace,
1630
1680
  metaspace,
1631
1681
  ib,
1632
1682
  data,
1633
1683
  rel8ns,
1634
1684
  parentIb: SYNC_ATOM, // "sync"
1685
+ /**
1686
+ * this will squash the result into a single frame
1687
+ */
1635
1688
  noDna: true,
1636
1689
  });
1690
+ if (!!resNew.intermediateIbGibs) {
1691
+ throw new Error(`(UNEXPECTED) resNew.intermediateIbGibs? we were assuming we were creating a single frame. (E: 456818147235d991ccb4d10d0bbe4826)`);
1692
+ }
1637
1693
  return resNew.newIbGib;
1638
1694
  }
1639
1695
  }
@@ -1642,16 +1698,16 @@ export class SyncSagaCoordinator {
1642
1698
  throw error;
1643
1699
  }
1644
1700
  }
1645
- async getStageAndPayloadFromFrame({ ibGib, space }) {
1701
+ async getStageAndPayloadFromFrame({ sagaFrame, space }) {
1646
1702
  const lc = `${this.lc}[${this.getStageAndPayloadFromFrame.name}]`;
1647
1703
  try {
1648
1704
  if (logalot) {
1649
1705
  console.log(`${lc} starting... (I: fddc287bd4d55253dc50e519fd352226)`);
1650
1706
  }
1651
- if (!ibGib.rel8ns) {
1652
- throw new Error(`(UNEXPECTED) ibGib.rel8ns falsy? (E: 725bc8eb5dfe5c7058907ad8e3063826)`);
1707
+ if (!sagaFrame.rel8ns) {
1708
+ throw new Error(`(UNEXPECTED) sagaFrame.rel8ns falsy? (E: 725bc8eb5dfe5c7058907ad8e3063826)`);
1653
1709
  }
1654
- const stoneAddrs = ibGib.rel8ns?.[SYNC_MSG_REL8N_NAME];
1710
+ const stoneAddrs = sagaFrame.rel8ns?.[SYNC_MSG_REL8N_NAME];
1655
1711
  if (stoneAddrs && stoneAddrs.length > 0) {
1656
1712
  const stoneAddr = stoneAddrs[0];
1657
1713
  const res = await getFromSpace({ addr: stoneAddr, space });
@@ -1670,7 +1726,7 @@ export class SyncSagaCoordinator {
1670
1726
  }
1671
1727
  }
1672
1728
  else {
1673
- throw new Error(`(UNEXPECTED) no stone addrs rel8d to sync saga ibgib frame? ibGib addr: ${getIbGibAddr(ibGib)} (E: 43eae8579e289d016741b5526052f226)`);
1729
+ throw new Error(`(UNEXPECTED) no stone addrs rel8d to sync saga ibgib frame? sagaFrame addr: ${getIbGibAddr(sagaFrame)} (E: 43eae8579e289d016741b5526052f226)`);
1674
1730
  }
1675
1731
  }
1676
1732
  catch (error) {