@githolon/runtime 0.110.0 → 0.111.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/runtime",
3
- "version": "0.110.0",
3
+ "version": "0.111.0",
4
4
  "type": "module",
5
5
  "description": "The portable Nomos application runtime, reception contracts, and Node/browser runners.",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -21835,6 +21835,15 @@ var ReplicaFollowController = class {
21835
21835
  }
21836
21836
  if (result.stillAhead || result.pushFailed || result.directOfferDeferred || result.reconnectRequired) {
21837
21837
  if (result.directOfferDeferred) {
21838
+ if (!result.reconnectRequired) {
21839
+ this.#desk.transition(result.workspace, {
21840
+ case: ReplicaFollowEventKind.Failed,
21841
+ nowUnixMillis,
21842
+ failure: "remote workspace opening; retrying",
21843
+ retryWithinMillis: 5e3
21844
+ });
21845
+ return delayUntilDue(this.#desk.get(result.workspace), nowUnixMillis);
21846
+ }
21838
21847
  this.#desk.transition(result.workspace, {
21839
21848
  case: ReplicaFollowEventKind.DependencyDeferred,
21840
21849
  nowUnixMillis,
@@ -23740,6 +23749,34 @@ function resolveCustodyDisagreement(kernel, mount, requestId, policy, verdict) {
23740
23749
  init_define_NOMOS_KERNEL_RELEASE();
23741
23750
  var CUSTODY_OFFER_MEDIA_TYPE = "application/vnd.nomos.custody-offer-batch+protobuf";
23742
23751
  var CUSTODY_OFFER_RESULT_MEDIA_TYPE = "application/vnd.nomos.custody-offer-result+protobuf";
23752
+ function settleCustodyOfferExchange(awaiting, response) {
23753
+ if (response.status === 202 && response.contentType?.includes("application/json")) {
23754
+ try {
23755
+ const pending = JSON.parse(new TextDecoder().decode(response.body));
23756
+ if (pending?.retryable === true && typeof pending.preparing === "string") {
23757
+ return { case: "maintenance", kind: pending.preparing };
23758
+ }
23759
+ } catch {
23760
+ }
23761
+ }
23762
+ if (!response.contentType?.includes(CUSTODY_OFFER_RESULT_MEDIA_TYPE)) {
23763
+ return {
23764
+ case: "unexpected",
23765
+ status: response.status,
23766
+ ...response.contentType === void 0 ? {} : { contentType: response.contentType }
23767
+ };
23768
+ }
23769
+ const verdict = decodeCustodyOffer(response.body, awaiting.requestId);
23770
+ const receipt = custodyOfferReceipt(awaiting.requestId, verdict);
23771
+ switch (verdict.case) {
23772
+ case "admission":
23773
+ return { case: "admission", value: verdict.value, receipt, status: response.status };
23774
+ case "failure":
23775
+ return { case: "failure", value: verdict.value, receipt, status: response.status };
23776
+ case "blocked":
23777
+ return { case: "blocked", value: verdict.value, receipt, status: response.status };
23778
+ }
23779
+ }
23743
23780
  function encodeInlineCustodyOffer(args) {
23744
23781
  return toBinary(CustodyOfferBatchRequestSchema, create(CustodyOfferBatchRequestSchema, {
23745
23782
  protocolVersion: 2,
@@ -23866,6 +23903,7 @@ function summarizeCustodyAdmission(admission) {
23866
23903
  var PLACEMENT_REF3 = "refs/nomos/placement";
23867
23904
  var LAW_PRODUCTS_MEDIA_TYPE = "application/vnd.nomos.export-law-products-response+protobuf";
23868
23905
  var WorkspaceSyncPhase = Object.freeze({
23906
+ Idle: "idle",
23869
23907
  Probe: "probe",
23870
23908
  Offer: "offer",
23871
23909
  Lease: "lease",
@@ -23875,7 +23913,6 @@ var WorkspaceSyncPhase = Object.freeze({
23875
23913
  Done: "done"
23876
23914
  });
23877
23915
  var trackedMain = "refs/remotes/nomos/main";
23878
- var NO_COMMON_BASE = "this replica holds local history with no frontier its upstream ever confirmed; convergence needs a common admitted base \u2014 drop the local copy to refetch it, or re-discover a provider holding this lineage";
23879
23916
  var deferredRemote = "refs/remotes/nomos/deferred";
23880
23917
  var deferredLocal = "refs/nomos/sync-deferred-local";
23881
23918
  var judgedThisLifetime = /* @__PURE__ */ new Map();
@@ -23899,7 +23936,6 @@ var WorkspaceSyncWork = class {
23899
23936
  #rediscover;
23900
23937
  /** The route the directory's answer yields, recomputed once that answer is observed. */
23901
23938
  #reroute;
23902
- #placementFetch;
23903
23939
  #discoveredFrontier;
23904
23940
  #observePlacement;
23905
23941
  /** Ask the directory for the provider's current placement frontier this round although a
@@ -23908,19 +23944,14 @@ var WorkspaceSyncWork = class {
23908
23944
  #directoryAnswered;
23909
23945
  #placementFetched;
23910
23946
  /** A round which converged without leasing, finishing once placement alone is fetched. */
23911
- #afterPlacement;
23912
23947
  #leased = false;
23913
- #lease;
23914
23948
  #command;
23915
23949
  #requestId;
23916
23950
  #started;
23917
- #phase = WorkspaceSyncPhase.Lease;
23951
+ #state = { phase: WorkspaceSyncPhase.Idle };
23918
23952
  #localHead;
23919
23953
  #offerHead;
23920
23954
  #previousHead;
23921
- #fetchStarted;
23922
- #offerStarted;
23923
- #offerRequestId;
23924
23955
  #pushMillis = 0;
23925
23956
  #pushBytes = 0n;
23926
23957
  #desiredHead;
@@ -24015,7 +24046,7 @@ var WorkspaceSyncWork = class {
24015
24046
  throw new Error(`replica '${this.#binding.replicaId}' has no custody remote`);
24016
24047
  }
24017
24048
  if (this.#rediscover || this.#recheckPlacement || !this.#route?.placementObserved && !this.#route?.placementFrontier && !this.#kernel.resolveRef(this.#mount, this.#requestId, PLACEMENT_REF3)) {
24018
- this.#phase = WorkspaceSyncPhase.Discover;
24049
+ this.#state = { phase: WorkspaceSyncPhase.Discover };
24019
24050
  return this.#remember({
24020
24051
  case: "pending",
24021
24052
  value: create(CorrespondencePendingSchema, {
@@ -24035,13 +24066,13 @@ var WorkspaceSyncWork = class {
24035
24066
  #openRound() {
24036
24067
  if (this.#receiveState || this.#replaceLocalHead !== void 0) return this.#leaseEffect();
24037
24068
  if (!this.#localHead) return this.#leaseEffect();
24038
- this.#offerRequestId = `${this.#binding.replicaId}:${this.#requestId}:${this.workId}:frontier`;
24069
+ const requestId = `${this.#binding.replicaId}:${this.#requestId}:${this.workId}:frontier`;
24039
24070
  return this.#beginOffer(encodeInlineCustodyOffer({
24040
24071
  session: this.#binding.replicaId,
24041
- requestId: this.#offerRequestId,
24072
+ requestId,
24042
24073
  observedHead: this.#localHead,
24043
24074
  intents: []
24044
- }), true);
24075
+ }), { case: "awaiting", requestId }, true);
24045
24076
  }
24046
24077
  #offerOrReconcile() {
24047
24078
  if (this.#command.admit && this.#localHead && this.#localHead !== this.#previousHead) {
@@ -24062,13 +24093,13 @@ var WorkspaceSyncWork = class {
24062
24093
  throw new Error(`kernel did not classify transport for intent '${unspecified.intentId}'`);
24063
24094
  }
24064
24095
  if (suffix.length > 0) {
24065
- this.#offerStarted = this.#clock.monotonicNanoseconds();
24066
- this.#offerRequestId = `${this.#binding.replicaId}:${this.#requestId}:${this.workId}`;
24096
+ const requestId2 = `${this.#binding.replicaId}:${this.#requestId}:${this.workId}`;
24097
+ const startedAt = this.#clock.monotonicNanoseconds();
24067
24098
  this.#offerAfterConverge = true;
24068
24099
  this.#rebaseFrom = this.#previousHead;
24069
24100
  return this.#beginOffer(encodeInlineCustodyOffer({
24070
24101
  session: this.#binding.replicaId,
24071
- requestId: this.#offerRequestId,
24102
+ requestId: requestId2,
24072
24103
  ...this.#previousHead === void 0 ? {} : { baseHead: this.#previousHead },
24073
24104
  observedHead: this.#localHead,
24074
24105
  intents: suffix,
@@ -24079,51 +24110,26 @@ var WorkspaceSyncWork = class {
24079
24110
  this.#previousHead
24080
24111
  ) : void 0,
24081
24112
  fastForwardOnly: true
24082
- }));
24113
+ }), { case: "awaiting", requestId: requestId2, startedAt });
24083
24114
  }
24084
24115
  }
24085
- this.#offerRequestId = `${this.#binding.replicaId}:${this.#requestId}:${this.workId}:reconcile`;
24116
+ const requestId = `${this.#binding.replicaId}:${this.#requestId}:${this.workId}:reconcile`;
24086
24117
  return this.#beginOffer(encodeInlineCustodyOffer({
24087
24118
  session: this.#binding.replicaId,
24088
- requestId: this.#offerRequestId,
24119
+ requestId,
24089
24120
  baseHead: this.#previousHead,
24090
24121
  observedHead: this.#localHead,
24091
24122
  intents: []
24092
- }));
24123
+ }), { case: "awaiting", requestId });
24093
24124
  }
24094
24125
  /**
24095
- * NO COMMON BASE — a round that cannot converge, and must not pay for history to find out.
24096
- *
24097
- * This replica holds a non-empty ledger but has never recorded a frontier its upstream
24098
- * confirmed (`refs/remotes/nomos/main` is absent). The kernel refuses convergence in that
24099
- * state ("a non-empty live ledger requires a common admitted base"), so every step after the
24100
- * O(1) frontier probe is wasted work — and it is not cheap waste: the fetch negotiates with
24101
- * no `have` (a full-history pack), and the dependency planner decodes EVERY intent from
24102
- * genesis and inspects every change, once per round. That is O(history) per follow round,
24103
- * repeated at every retry, for a round that can only end in a refusal.
24104
- *
24105
- * The one base this round can establish costs nothing and is established before this is
24106
- * asked: an upstream holding exactly this replica's head (the probe's answer) converges it
24107
- * and records the tracking frontier. Anything else needs the local copy to be dropped and
24108
- * refetched (an application decision, which preserves the copy), or a re-discovery that
24109
- * names a provider holding this lineage. Both are reported, at O(1), as this failure.
24110
- */
24111
- /**
24112
- * Establish this round's base when none is tracked, without walking any history it can avoid.
24113
- *
24114
- * The upstream just named its own frontier. If that commit is in THIS replica's lineage, it is
24115
- * a common admitted base by definition — both sides hold it — and adopting it turns the round
24116
- * into an ordinary fast-forward: the offer carries only the suffix above it, the fetch can name
24117
- * it as a truthful `have`, and convergence has the base the kernel requires. One commit walk
24118
- * (no change decoding, no pack, no network) decides it.
24119
- *
24120
- * If it is not in this replica's lineage, no base exists to establish and the round is refused
24121
- * (#noCommonBase) before it leases, fetches or plans anything.
24122
- *
24123
- * A kernel without the commit walk cannot be asked, so it keeps the previous behaviour.
24126
+ * Establish a cheap base when the provider's current tip is in our local chain.
24127
+ * A negative answer means the base is unknown: both sides may have advanced from
24128
+ * an earlier shared commit. Acquire the candidate and let the kernel discover
24129
+ * and judge that history. A tracking bookmark is not an admission prerequisite.
24124
24130
  */
24125
24131
  #establishBase(upstreamHead) {
24126
- if (!this.#noCommonBase() || !this.#kernel.historySummary) return true;
24132
+ if (!this.#missingTrackedBase() || !this.#kernel.historySummary) return true;
24127
24133
  const summary = this.#kernel.historySummary(this.#mount, this.#requestId, upstreamHead, this.#localHead);
24128
24134
  if (!summary.reachedBase) return false;
24129
24135
  this.#previousHead = upstreamHead;
@@ -24138,26 +24144,9 @@ var WorkspaceSyncWork = class {
24138
24144
  get establishedBase() {
24139
24145
  return this.#establishedBase;
24140
24146
  }
24141
- #noCommonBase() {
24147
+ #missingTrackedBase() {
24142
24148
  return this.#previousHead === void 0 && this.#localHead !== void 0 && this.#replaceLocalHead === void 0;
24143
24149
  }
24144
- #noCommonBaseResult() {
24145
- return this.#finish(create(SyncWorkspaceResponseSchema, {
24146
- workspace: this.#mount.workspace,
24147
- ...this.#localHead === void 0 ? {} : { currentHead: this.#localHead },
24148
- stillAhead: true,
24149
- ...this.#offerReceipt === void 0 ? {} : { offerReceipt: this.#offerReceipt },
24150
- pushError: create(RuntimeTransportFailureSchema, {
24151
- endpoint: this.#offerEndpoint(),
24152
- status: 409,
24153
- refused: true,
24154
- message: NO_COMMON_BASE
24155
- }),
24156
- timings: create(SyncPhaseTimingsSchema, {
24157
- convergeMillis: elapsedMillis(this.#clock, this.#started)
24158
- })
24159
- }));
24160
- }
24161
24150
  #holdsLocalWork() {
24162
24151
  return this.#command.admit && !!this.#localHead && this.#localHead !== this.#previousHead;
24163
24152
  }
@@ -24165,18 +24154,18 @@ var WorkspaceSyncWork = class {
24165
24154
  #pullBeforeOffer() {
24166
24155
  this.#offerAfterConverge = true;
24167
24156
  this.#rebaseFrom = this.#previousHead;
24168
- this.#offerRequestId = `${this.#binding.replicaId}:${this.#requestId}:${this.workId}:pull-${this.#rebases}`;
24157
+ const requestId = `${this.#binding.replicaId}:${this.#requestId}:${this.workId}:pull-${this.#rebases}`;
24169
24158
  return this.#beginOffer(encodeInlineCustodyOffer({
24170
24159
  session: this.#binding.replicaId,
24171
- requestId: this.#offerRequestId,
24160
+ requestId,
24172
24161
  ...this.#previousHead === void 0 ? {} : { baseHead: this.#previousHead },
24173
24162
  observedHead: this.#localHead,
24174
24163
  intents: []
24175
- }));
24164
+ }), { case: "awaiting", requestId });
24176
24165
  }
24177
- #leaseEffect() {
24166
+ #leaseEffect(afterPlacement) {
24178
24167
  const token = this.#authToken();
24179
- this.#phase = WorkspaceSyncPhase.Lease;
24168
+ this.#state = { phase: WorkspaceSyncPhase.Lease, ...afterPlacement ? { afterPlacement } : {} };
24180
24169
  return this.#remember({
24181
24170
  case: "pending",
24182
24171
  value: create(CorrespondencePendingSchema, {
@@ -24205,35 +24194,38 @@ var WorkspaceSyncWork = class {
24205
24194
  `workspace sync '${this.workId}' expected effect '${expected}', received '${receipt.effectId}'`
24206
24195
  );
24207
24196
  }
24197
+ const state = this.#state;
24208
24198
  const previousReceipt = this.#lastReceiptEffectId;
24209
24199
  this.#lastReceiptEffectId = receipt.effectId;
24210
24200
  try {
24211
24201
  if (receipt.outcome.case === "failure") {
24212
- if (this.#phase === WorkspaceSyncPhase.Discover) return this.#openRound();
24202
+ if (state.phase === WorkspaceSyncPhase.Discover) return this.#openRound();
24213
24203
  return this.#transportFailure(
24214
24204
  receipt.outcome.value.message,
24215
24205
  receipt.outcome.value.reason === PeerEffectFailureReason.OFFLINE
24216
24206
  );
24217
24207
  }
24218
- if (this.#phase === WorkspaceSyncPhase.Offer || this.#phase === WorkspaceSyncPhase.Probe) return this.#acceptOffer(receipt);
24219
- if (this.#phase === WorkspaceSyncPhase.Lease) return this.#acceptLease(receipt);
24220
- if (this.#phase === WorkspaceSyncPhase.Discover) return this.#acceptDiscovery(receipt);
24221
- if (this.#phase === WorkspaceSyncPhase.Placement) {
24222
- if (!this.#placementFetch || !this.#lease) throw new Error("placement transfer lost its lease");
24223
- this.#placementFetch.accept(receipt);
24208
+ if (state.phase === WorkspaceSyncPhase.Offer || state.phase === WorkspaceSyncPhase.Probe) {
24209
+ return this.#acceptOffer(receipt, state);
24210
+ }
24211
+ if (state.phase === WorkspaceSyncPhase.Lease) return this.#acceptLease(receipt, state);
24212
+ if (state.phase === WorkspaceSyncPhase.Discover) return this.#acceptDiscovery(receipt);
24213
+ if (state.phase === WorkspaceSyncPhase.Placement) {
24214
+ state.transfer.accept(receipt);
24224
24215
  this.#placementFetched?.();
24225
- if (this.#afterPlacement) return this.#finish(this.#afterPlacement);
24226
- return this.#beginMainFetch(this.#lease);
24216
+ if (state.afterPlacement) return this.#finish(state.afterPlacement);
24217
+ return this.#beginMainFetch(state.lease);
24227
24218
  }
24228
- if (this.#phase === WorkspaceSyncPhase.Fetch) return this.#acceptFetch(receipt);
24219
+ if (state.phase === WorkspaceSyncPhase.Fetch) return this.#acceptFetch(receipt, state);
24229
24220
  throw new Error(`workspace sync '${this.workId}' is already complete`);
24230
24221
  } catch (error) {
24231
24222
  this.#lastReceiptEffectId = previousReceipt;
24232
24223
  throw error;
24233
24224
  }
24234
24225
  }
24235
- #beginOffer(body, probe = false) {
24236
- this.#phase = probe ? WorkspaceSyncPhase.Probe : WorkspaceSyncPhase.Offer;
24226
+ #beginOffer(body, exchange, probe = false) {
24227
+ const phase = probe ? WorkspaceSyncPhase.Probe : WorkspaceSyncPhase.Offer;
24228
+ this.#state = { phase, exchange };
24237
24229
  const token = this.#authToken();
24238
24230
  this.#pushBytes += BigInt(body.byteLength);
24239
24231
  return this.#remember({
@@ -24243,7 +24235,7 @@ var WorkspaceSyncWork = class {
24243
24235
  effect: {
24244
24236
  case: "http",
24245
24237
  value: create(PeerHttpEffectSchema, {
24246
- effectId: `${this.workId}:${this.#phase}`,
24238
+ effectId: `${this.workId}:${phase}`,
24247
24239
  method: PeerHttpMethod.POST,
24248
24240
  url: this.#offerEndpoint(),
24249
24241
  contentType: CUSTODY_OFFER_MEDIA_TYPE,
@@ -24260,28 +24252,30 @@ var WorkspaceSyncWork = class {
24260
24252
  })
24261
24253
  });
24262
24254
  }
24263
- #acceptOffer(receipt) {
24264
- this.#pushMillis = this.#offerStarted === void 0 ? 0 : elapsedMillis(this.#clock, this.#offerStarted);
24255
+ #acceptOffer(receipt, state) {
24256
+ const exchange = state.exchange;
24257
+ this.#pushMillis = exchange.startedAt === void 0 ? 0 : elapsedMillis(this.#clock, exchange.startedAt);
24265
24258
  if (receipt.outcome.case !== "response") {
24266
24259
  throw new Error("platform returned the wrong receipt for a custody offer");
24267
24260
  }
24268
- const response = receipt.outcome.value;
24269
- if (!response.contentType?.includes(CUSTODY_OFFER_RESULT_MEDIA_TYPE)) {
24261
+ const outcome = settleCustodyOfferExchange(exchange, receipt.outcome.value);
24262
+ if (outcome.case === "maintenance") {
24263
+ return this.#finishOfferOutcome(true, false);
24264
+ }
24265
+ if (outcome.case === "unexpected") {
24270
24266
  this.#pushError = create(RuntimeTransportFailureSchema, {
24271
24267
  endpoint: this.#offerEndpoint(),
24272
- status: response.status,
24268
+ status: outcome.status,
24273
24269
  refused: false,
24274
- message: `Sync received an unexpected server response (HTTP ${response.status}; ${response.contentType || "no content type"}). No custody receipt was returned.`
24270
+ message: `Sync received an unexpected server response (HTTP ${outcome.status}; ${outcome.contentType || "no content type"}). No custody receipt was returned.`
24275
24271
  });
24276
24272
  return this.#finishOfferOutcome(false);
24277
24273
  }
24278
- if (!this.#offerRequestId) throw new Error("custody offer has no request identity");
24279
- const outcome = decodeCustodyOffer(response.body, this.#offerRequestId);
24280
- this.#offerReceipt = custodyOfferReceipt(this.#offerRequestId, outcome);
24274
+ this.#offerReceipt = outcome.receipt;
24281
24275
  if (outcome.case === "failure") {
24282
24276
  this.#pushError = create(RuntimeTransportFailureSchema, {
24283
24277
  endpoint: this.#offerEndpoint(),
24284
- status: response.status,
24278
+ status: outcome.status,
24285
24279
  refused: false,
24286
24280
  message: `${outcome.value.code}: ${outcome.value.message}`
24287
24281
  });
@@ -24290,12 +24284,12 @@ var WorkspaceSyncWork = class {
24290
24284
  if (outcome.case === "blocked") {
24291
24285
  return this.#finishOfferOutcome(true);
24292
24286
  }
24293
- if (response.status < 200 || response.status >= 300) {
24287
+ if (outcome.status < 200 || outcome.status >= 300) {
24294
24288
  this.#pushError = create(RuntimeTransportFailureSchema, {
24295
24289
  endpoint: this.#offerEndpoint(),
24296
- status: response.status,
24297
- refused: response.status === 422,
24298
- message: `custody offer returned an admission verdict with HTTP ${response.status}`
24290
+ status: outcome.status,
24291
+ refused: outcome.status === 422,
24292
+ message: `custody offer returned an admission verdict with HTTP ${outcome.status}`
24299
24293
  });
24300
24294
  return this.#finishOfferOutcome(false);
24301
24295
  }
@@ -24303,8 +24297,7 @@ var WorkspaceSyncWork = class {
24303
24297
  this.#desiredHead = admission.main || void 0;
24304
24298
  if (!this.#desiredHead) return this.#transportFailure("resident peer returned no admitted frontier", false);
24305
24299
  this.#candidateDurable = admission.sealedMain === this.#desiredHead;
24306
- if (this.#phase === WorkspaceSyncPhase.Probe && this.#desiredHead !== this.#localHead) {
24307
- if (!this.#establishBase(this.#desiredHead)) return this.#noCommonBaseResult();
24300
+ if (state.phase === WorkspaceSyncPhase.Probe && this.#desiredHead !== this.#localHead) {
24308
24301
  if (this.#alreadyJudged(this.#desiredHead)) {
24309
24302
  return this.#finish(create(SyncWorkspaceResponseSchema, {
24310
24303
  workspace: this.#mount.workspace,
@@ -24316,6 +24309,22 @@ var WorkspaceSyncWork = class {
24316
24309
  offerReceipt: this.#offerReceipt
24317
24310
  }));
24318
24311
  }
24312
+ if (!this.#establishBase(this.#desiredHead)) {
24313
+ this.#offerAfterConverge = this.#command.admit;
24314
+ this.#rebaseFrom = void 0;
24315
+ return this.#leaseEffect();
24316
+ }
24317
+ if (this.#establishedBase && !this.#candidateDurable && this.#kernel.resolveRef(this.#mount, this.#requestId, trackedMain) === void 0) {
24318
+ return this.#finish(create(SyncWorkspaceResponseSchema, {
24319
+ workspace: this.#mount.workspace,
24320
+ currentHead: this.#localHead,
24321
+ previousHead: this.#previousHead,
24322
+ stillAhead: true,
24323
+ directOfferDeferred: true,
24324
+ reconnectRequired: false,
24325
+ offerReceipt: this.#offerReceipt
24326
+ }));
24327
+ }
24319
24328
  if (this.#probeOnly) {
24320
24329
  this.#needsReconciliation = true;
24321
24330
  return this.#finish(create(SyncWorkspaceResponseSchema, {
@@ -24361,11 +24370,11 @@ var WorkspaceSyncWork = class {
24361
24370
  return this.#converge(this.#desiredHead, admission.resultPack.byteLength, 0);
24362
24371
  }
24363
24372
  if (this.#desiredHead === this.#previousHead) return this.#converge(this.#desiredHead, 0, 0);
24364
- if (!this.#establishBase(this.#desiredHead)) return this.#noCommonBaseResult();
24373
+ this.#establishBase(this.#desiredHead);
24365
24374
  if (admission.recoveryRequired !== false) return this.#leaseEffect();
24366
24375
  return this.#transportFailure("resident peer supplied neither the missing delta nor a history gap", false);
24367
24376
  }
24368
- #acceptLease(receipt) {
24377
+ #acceptLease(receipt, state) {
24369
24378
  if (receipt.outcome.case !== "response") {
24370
24379
  throw new Error("platform returned the wrong receipt for a Git read lease");
24371
24380
  }
@@ -24381,21 +24390,25 @@ var WorkspaceSyncWork = class {
24381
24390
  this.#leased = true;
24382
24391
  const frontier = this.#placementFrontier();
24383
24392
  if (frontier) {
24384
- this.#placementFetch = new PlacementFetch(
24393
+ const transfer = new PlacementFetch(
24385
24394
  this.#kernel,
24386
24395
  this.#mount,
24387
24396
  this.#requestId,
24388
24397
  this.workId,
24389
24398
  frontier
24390
24399
  );
24391
- const pending = this.#placementFetch.start(lease);
24400
+ const pending = transfer.start(lease);
24392
24401
  if (pending) {
24393
- this.#phase = WorkspaceSyncPhase.Placement;
24394
- this.#lease = lease;
24402
+ this.#state = {
24403
+ phase: WorkspaceSyncPhase.Placement,
24404
+ transfer,
24405
+ lease,
24406
+ ...state.afterPlacement ? { afterPlacement: state.afterPlacement } : {}
24407
+ };
24395
24408
  return this.#remember({ case: "pending", value: pending });
24396
24409
  }
24397
24410
  }
24398
- if (this.#afterPlacement) return this.#finish(this.#afterPlacement);
24411
+ if (state.afterPlacement) return this.#finish(state.afterPlacement);
24399
24412
  return this.#beginMainFetch(lease);
24400
24413
  }
24401
24414
  /** The provider's placement frontier as this round knows it: what the directory named in this
@@ -24415,8 +24428,7 @@ var WorkspaceSyncWork = class {
24415
24428
  if (!new PlacementFetch(this.#kernel, this.#mount, this.#requestId, this.workId, frontier).wanted()) {
24416
24429
  return void 0;
24417
24430
  }
24418
- this.#afterPlacement = presented;
24419
- return this.#leaseEffect();
24431
+ return this.#leaseEffect(presented);
24420
24432
  }
24421
24433
  #acceptDiscovery(receipt) {
24422
24434
  this.#directoryAnswered?.();
@@ -24440,9 +24452,11 @@ var WorkspaceSyncWork = class {
24440
24452
  return this.#replaceLocalHead !== void 0 || this.#localHead === void 0 && this.#previousHead === void 0;
24441
24453
  }
24442
24454
  #beginMainFetch(lease) {
24443
- this.#phase = WorkspaceSyncPhase.Fetch;
24444
24455
  const lawProducts = this.#lawProductsAlongside();
24445
- this.#fetchStarted = this.#clock.monotonicNanoseconds();
24456
+ this.#state = {
24457
+ phase: WorkspaceSyncPhase.Fetch,
24458
+ startedAt: this.#clock.monotonicNanoseconds()
24459
+ };
24446
24460
  const haves = this.#replaceLocalHead !== void 0 ? this.#repairMissingCommit ? [] : [this.#kernel.resolveRef(this.#mount, this.#requestId, deferredRemote)].filter(
24447
24461
  (head) => !!head
24448
24462
  ) : [...new Set([
@@ -24473,7 +24487,7 @@ var WorkspaceSyncWork = class {
24473
24487
  })
24474
24488
  });
24475
24489
  }
24476
- #acceptFetch(receipt) {
24490
+ #acceptFetch(receipt, state) {
24477
24491
  if (receipt.outcome.case !== "gitFetch") {
24478
24492
  throw new Error("platform returned the wrong receipt for a Git object fetch");
24479
24493
  }
@@ -24483,7 +24497,7 @@ var WorkspaceSyncWork = class {
24483
24497
  if (fetched.pack.byteLength > 0) {
24484
24498
  this.#kernel.applyPack(this.#mount, this.#requestId, fetched.pack, fetched.head, fetched.shallow);
24485
24499
  }
24486
- const prefetchMillis = this.#fetchStarted === void 0 ? 0 : elapsedMillis(this.#clock, this.#fetchStarted);
24500
+ const prefetchMillis = elapsedMillis(this.#clock, state.startedAt);
24487
24501
  const candidateHead = this.#receiveState ? this.#kernel.resolveStatePosition?.(this.#mount, this.#requestId, fetched.head, this.#receiveState) : fetched.head;
24488
24502
  if (!candidateHead) {
24489
24503
  return this.#finish(create(SyncWorkspaceResponseSchema, {
@@ -24579,7 +24593,7 @@ var WorkspaceSyncWork = class {
24579
24593
  }
24580
24594
  }
24581
24595
  #converge(candidateHead, receivedBytes, prefetchMillis) {
24582
- if (!this.#establishBase(candidateHead)) return this.#noCommonBaseResult();
24596
+ this.#establishBase(candidateHead);
24583
24597
  this.#beforeConverge?.(candidateHead, this.#previousHead);
24584
24598
  const convergeStarted = this.#clock.monotonicNanoseconds();
24585
24599
  let judged;
@@ -24834,11 +24848,11 @@ var WorkspaceSyncWork = class {
24834
24848
  pushBytes: this.#pushBytes,
24835
24849
  pullBytes: BigInt(receivedBytes),
24836
24850
  serverMillis: this.#admissionSummary?.serverMillis ?? 0,
24837
- offerInPush: this.#offerRequestId !== void 0
24851
+ offerInPush: this.#pushBytes > 0n
24838
24852
  })
24839
24853
  });
24840
24854
  }
24841
- #finishOfferOutcome(blocked) {
24855
+ #finishOfferOutcome(blocked, reconnectRequired = true) {
24842
24856
  return this.#finish(create(SyncWorkspaceResponseSchema, {
24843
24857
  workspace: this.#mount.workspace,
24844
24858
  previousHead: this.#previousHead,
@@ -24846,7 +24860,7 @@ var WorkspaceSyncWork = class {
24846
24860
  admitted: BigInt(this.#admissionSummary?.admittedIntentIds.length ?? 0),
24847
24861
  refused: BigInt((this.#admissionSummary?.refusedMessages.length ?? 0) + this.#parkedLocal),
24848
24862
  deferred: BigInt(this.#admissionSummary ? this.#admissionSummary.blockedMessages.length + this.#admissionSummary.deadLetteredMessages.length + this.#admissionSummary.quarantinedMessages.length + this.#admissionSummary.deferredMessages.length : blocked ? 1 : 0),
24849
- reconnectRequired: true,
24863
+ reconnectRequired,
24850
24864
  stillAhead: this.#localHead !== this.#previousHead,
24851
24865
  directOfferDeferred: blocked,
24852
24866
  offerReceipt: this.#offerReceipt,
@@ -24860,15 +24874,16 @@ var WorkspaceSyncWork = class {
24860
24874
  }));
24861
24875
  }
24862
24876
  #transportFailure(message, offline, status = -1) {
24863
- const pushFailed = this.#phase === WorkspaceSyncPhase.Offer;
24877
+ const state = this.#state;
24878
+ const pushFailed = state.phase === WorkspaceSyncPhase.Offer;
24864
24879
  const failedEndpoint = this.#offerEndpoint();
24865
24880
  const failure = create(RuntimeTransportFailureSchema, {
24866
24881
  endpoint: pushFailed ? failedEndpoint : this.#binding.cloud ?? "",
24867
24882
  status,
24868
24883
  message
24869
24884
  });
24870
- if (pushFailed) {
24871
- this.#pushMillis = this.#offerStarted === void 0 ? 0 : elapsedMillis(this.#clock, this.#offerStarted);
24885
+ if (state.phase === WorkspaceSyncPhase.Offer) {
24886
+ this.#pushMillis = state.exchange.startedAt === void 0 ? 0 : elapsedMillis(this.#clock, state.exchange.startedAt);
24872
24887
  this.#pushError = failure;
24873
24888
  }
24874
24889
  return this.#finish(create(SyncWorkspaceResponseSchema, {
@@ -24885,7 +24900,7 @@ var WorkspaceSyncWork = class {
24885
24900
  pushMillis: this.#pushMillis,
24886
24901
  pushBytes: this.#pushBytes,
24887
24902
  prefetchMillis: elapsedMillis(this.#clock, this.#started),
24888
- offerInPush: this.#offerRequestId !== void 0
24903
+ offerInPush: this.#pushBytes > 0n
24889
24904
  })
24890
24905
  }));
24891
24906
  }
@@ -24895,7 +24910,7 @@ var WorkspaceSyncWork = class {
24895
24910
  return step;
24896
24911
  }
24897
24912
  #finish(value) {
24898
- this.#phase = WorkspaceSyncPhase.Done;
24913
+ this.#state = { phase: WorkspaceSyncPhase.Done };
24899
24914
  this.#current = void 0;
24900
24915
  return this.#remember({ case: "synced", value });
24901
24916
  }
@@ -27033,13 +27048,21 @@ var CorrespondenceDesk = class {
27033
27048
  }
27034
27049
  #observeSyncResult(requestId, workId2, result) {
27035
27050
  const timings = result.timings;
27051
+ const receipt = result.offerReceipt?.outcome;
27052
+ const admission = receipt?.case === "admission" ? receipt.value : void 0;
27036
27053
  this.#events.instant("peer.follow.sync-result", {
27037
27054
  request: create(RuntimeRequestSchema, { protocolVersion: 2, requestId }),
27038
27055
  workspace: result.workspace
27039
27056
  }, {
27040
27057
  "sync.workId": workId2,
27041
27058
  ...result.currentHead ? { "sync.head": result.currentHead } : {},
27059
+ ...result.previousHead ? { "sync.previousHead": result.previousHead } : {},
27060
+ ...admission?.main ? { "sync.providerHead": admission.main } : {},
27061
+ ...admission?.sealedMain ? { "sync.sealedHead": admission.sealedMain } : {},
27042
27062
  "sync.stillAhead": result.stillAhead,
27063
+ "sync.reconnectRequired": result.reconnectRequired,
27064
+ "sync.refused": result.refused,
27065
+ "sync.deferred": result.deferred,
27043
27066
  "sync.directOfferDeferred": result.directOfferDeferred,
27044
27067
  "sync.offline": result.offline,
27045
27068
  ...timings ? {