@zlink-systems/framework 0.25.0 → 0.26.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.
Files changed (215) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Channels/IZLinkFanoutClient.d.ts +0 -6
  3. package/dist/contracts/RouteMesh/RuntimeTopology.d.ts +8 -0
  4. package/dist/internal.d.ts +1 -0
  5. package/dist/internal.js +1 -0
  6. package/dist/runtime/activation-admission.d.ts +29 -0
  7. package/dist/runtime/activation-admission.js +61 -0
  8. package/dist/runtime/actors/actor-context.js +6 -6
  9. package/dist/runtime/actors/actor-handoff.d.ts +9 -3
  10. package/dist/runtime/actors/actor-handoff.js +28 -17
  11. package/dist/runtime/actors/actor-local-native-join.js +2 -18
  12. package/dist/runtime/actors/actor-remote-joiner.d.ts +2 -2
  13. package/dist/runtime/actors/actor-remote-joiner.js +6 -8
  14. package/dist/runtime/actors/actor-runtime-contracts.d.ts +4 -2
  15. package/dist/runtime/actors/actor-source-transfer.d.ts +5 -5
  16. package/dist/runtime/actors/deferred-join-completion.d.ts +14 -0
  17. package/dist/runtime/actors/deferred-join-completion.js +20 -0
  18. package/dist/runtime/actors/index.d.ts +1 -1
  19. package/dist/runtime/actors/index.js +15 -5
  20. package/dist/runtime/admission.d.ts +5 -1
  21. package/dist/runtime/admission.js +4 -0
  22. package/dist/runtime/backend/contracts/index.d.ts +2 -10
  23. package/dist/runtime/backend/mesh-actor-session-node-adapter.js +2 -9
  24. package/dist/runtime/backend/node/node-backend-adapter-factory.d.ts +1 -2
  25. package/dist/runtime/backend/node/node-backend-adapter-factory.js +4 -18
  26. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +6 -3
  27. package/dist/runtime/backend/node/node-backend-adapter-support.js +12 -28
  28. package/dist/runtime/backend/node/node-event-loop-poller.d.ts +1 -1
  29. package/dist/runtime/backend/node/node-event-loop-poller.js +8 -2
  30. package/dist/runtime/backend/node/node-raw-binding-port.d.ts +1 -1
  31. package/dist/runtime/backend/node/node-raw-binding-port.js +11 -3
  32. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +12 -9
  33. package/dist/runtime/backend/node/node-raw-mesh-backend.js +61 -29
  34. package/dist/runtime/backend/node/node-socket-backend-adapter.js +1 -1
  35. package/dist/runtime/backend/raw-binding-port.d.ts +12 -0
  36. package/dist/runtime/channels/channel-clients.d.ts +0 -1
  37. package/dist/runtime/channels/channel-clients.js +0 -8
  38. package/dist/runtime/channels/channel-outbound-operations.js +2 -10
  39. package/dist/runtime/channels/channel-runtime-manager.d.ts +2 -5
  40. package/dist/runtime/channels/channel-runtime-manager.js +1 -12
  41. package/dist/runtime/channels/channel-socket-registry.d.ts +17 -4
  42. package/dist/runtime/channels/channel-socket-registry.js +49 -62
  43. package/dist/runtime/channels/channel-transports.d.ts +0 -3
  44. package/dist/runtime/channels/fanout-location-runtime.js +3 -9
  45. package/dist/runtime/execution/index.d.ts +18 -8
  46. package/dist/runtime/execution/index.js +55 -29
  47. package/dist/runtime/execution/serial-execution-queue.d.ts +5 -6
  48. package/dist/runtime/execution/serial-execution-queue.js +49 -15
  49. package/dist/runtime/foundation/listener-records.d.ts +11 -0
  50. package/dist/runtime/foundation/listener-records.js +20 -0
  51. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +10 -21
  52. package/dist/runtime/foundation/raw-service-mesh-runtime.js +66 -392
  53. package/dist/runtime/foundation/service-authority-payload-codec.d.ts +1 -0
  54. package/dist/runtime/foundation/service-authority-payload-codec.js +8 -0
  55. package/dist/runtime/foundation/service-maintenance-runtime.js +23 -2
  56. package/dist/runtime/foundation/service-relocation-coordinator.d.ts +9 -0
  57. package/dist/runtime/foundation/service-relocation-coordinator.js +14 -1
  58. package/dist/runtime/foundation/service-relocation-object-owner.d.ts +8 -0
  59. package/dist/runtime/foundation/service-relocation-object-owner.js +16 -4
  60. package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +6 -13
  61. package/dist/runtime/foundation/service-stateful-runtime.d.ts +10 -7
  62. package/dist/runtime/foundation/service-stateful-runtime.js +100 -60
  63. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +9 -4
  64. package/dist/runtime/foundation/service-stateful-wire-codec.js +153 -90
  65. package/dist/runtime/foundation/service-topology-registry.d.ts +1 -2
  66. package/dist/runtime/foundation/service-topology-registry.js +6 -30
  67. package/dist/runtime/host/actor-transfer-runtime.d.ts +17 -15
  68. package/dist/runtime/host/actor-transfer-runtime.js +73 -86
  69. package/dist/runtime/host/index.d.ts +6 -2
  70. package/dist/runtime/host/index.js +63 -37
  71. package/dist/runtime/host/remote-bound-session-relay.js +4 -3
  72. package/dist/runtime/host/route-mesh-runtime.d.ts +2 -0
  73. package/dist/runtime/host/route-mesh-runtime.js +7 -5
  74. package/dist/runtime/host/service-relocation-host-runtime.d.ts +50 -8
  75. package/dist/runtime/host/service-relocation-host-runtime.js +343 -270
  76. package/dist/runtime/host/spot-runtime-options-factory.d.ts +7 -4
  77. package/dist/runtime/host/spot-runtime-options-factory.js +4 -8
  78. package/dist/runtime/host/stateful-authority-route-runtime.js +6 -2
  79. package/dist/runtime/host/user-spot-creation-coordinator.d.ts +11 -3
  80. package/dist/runtime/host/user-spot-creation-coordinator.js +85 -87
  81. package/dist/runtime/locations/in-memory-provider-location-store.js +8 -6
  82. package/dist/runtime/locations/lifecycle.d.ts +1 -1
  83. package/dist/runtime/locations/lifecycle.js +2 -2
  84. package/dist/runtime/locations/resolvers.js +7 -2
  85. package/dist/runtime/locations/spot-location-claims.d.ts +2 -2
  86. package/dist/runtime/locations/spot-location-claims.js +3 -27
  87. package/dist/runtime/protocol/service_wire_codec.generated.d.ts +1 -0
  88. package/dist/runtime/protocol/service_wire_codec.generated.js +5 -1
  89. package/dist/runtime/protocol/service_wire_pilot_codec.generated.d.ts +1 -0
  90. package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +3 -2
  91. package/dist/runtime/spots/formal-remote-actor-admission-registry.d.ts +3 -3
  92. package/dist/runtime/spots/formal-remote-actor-admission-registry.js +4 -4
  93. package/dist/runtime/spots/formal-remote-actor-transfer-registry.d.ts +3 -3
  94. package/dist/runtime/spots/index.d.ts +25 -19
  95. package/dist/runtime/spots/index.js +207 -205
  96. package/dist/runtime/spots/spot-activation-registry.d.ts +4 -2
  97. package/dist/runtime/spots/spot-activation-registry.js +34 -31
  98. package/dist/runtime/spots/spot-activation-state.d.ts +2 -11
  99. package/dist/runtime/spots/spot-activation-state.js +4 -17
  100. package/dist/runtime/spots/spot-activation.d.ts +8 -6
  101. package/dist/runtime/spots/spot-activation.js +54 -49
  102. package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +1 -1
  103. package/dist/runtime/spots/spot-actor-admission-coordinator.js +1 -1
  104. package/dist/runtime/spots/spot-actor-join-dispatch.d.ts +2 -2
  105. package/dist/runtime/spots/spot-actor-join-dispatch.js +3 -5
  106. package/dist/runtime/spots/spot-actor-lifecycle-drain.js +2 -2
  107. package/dist/runtime/spots/spot-actor-membership.d.ts +4 -0
  108. package/dist/runtime/spots/spot-actor-membership.js +26 -12
  109. package/dist/runtime/spots/spot-entry-activation.d.ts +1 -2
  110. package/dist/runtime/spots/spot-entry-activation.js +0 -1
  111. package/dist/runtime/spots/spot-manager-public.js +1 -1
  112. package/dist/runtime/spots/spot-native-actor-join-admission.d.ts +1 -1
  113. package/dist/runtime/spots/spot-native-actor-join-admission.js +2 -2
  114. package/dist/runtime/spots/spot-node-runtime-manager.d.ts +5 -5
  115. package/dist/runtime/spots/spot-node-runtime-manager.js +7 -11
  116. package/dist/runtime/spots/spot-outbound.js +1 -29
  117. package/dist/runtime/spots/spot-routed-actor-admission.d.ts +1 -0
  118. package/dist/runtime/spots/spot-routed-actor-admission.js +10 -0
  119. package/dist/runtime/spots/spot-routed-frame-dispatch.d.ts +2 -6
  120. package/dist/runtime/spots/spot-routed-frame-dispatch.js +2 -70
  121. package/dist/runtime/spots/spot-runtime-ports.d.ts +3 -5
  122. package/dist/runtime/spots/spot-serial-executor.d.ts +5 -0
  123. package/dist/runtime/spots/spot-serial-executor.js +8 -1
  124. package/dist/runtime/spots/spot-serial-turn-executor.d.ts +3 -0
  125. package/dist/runtime/spots/spot-serial-turn-executor.js +8 -4
  126. package/dist/runtime/streams/actor-session-binding-registry.d.ts +13 -15
  127. package/dist/runtime/streams/actor-session-binding-registry.js +19 -171
  128. package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +3 -4
  129. package/dist/runtime/streams/bound-session-service.js +1 -18
  130. package/dist/runtime/streams/index.d.ts +2 -0
  131. package/dist/runtime/streams/index.js +19 -19
  132. package/dist/runtime/streams/session-serial-executor.js +2 -2
  133. package/package.json +3 -3
  134. package/src/contracts/Channels/IZLinkFanoutClient.ts +0 -7
  135. package/src/contracts/RouteMesh/RuntimeTopology.ts +10 -0
  136. package/src/internal.ts +1 -0
  137. package/src/runtime/activation-admission.ts +78 -0
  138. package/src/runtime/actors/actor-context.ts +6 -6
  139. package/src/runtime/actors/actor-handoff.ts +31 -23
  140. package/src/runtime/actors/actor-local-native-join.ts +3 -42
  141. package/src/runtime/actors/actor-remote-joiner.ts +6 -25
  142. package/src/runtime/actors/actor-runtime-contracts.ts +4 -10
  143. package/src/runtime/actors/actor-source-transfer.ts +5 -18
  144. package/src/runtime/actors/deferred-join-completion.ts +40 -0
  145. package/src/runtime/actors/index.ts +22 -9
  146. package/src/runtime/admission.ts +5 -1
  147. package/src/runtime/backend/contracts/index.ts +2 -13
  148. package/src/runtime/backend/mesh-actor-session-node-adapter.ts +2 -10
  149. package/src/runtime/backend/node/node-backend-adapter-factory.ts +5 -25
  150. package/src/runtime/backend/node/node-backend-adapter-support.ts +9 -29
  151. package/src/runtime/backend/node/node-event-loop-poller.ts +14 -2
  152. package/src/runtime/backend/node/node-raw-binding-port.ts +17 -3
  153. package/src/runtime/backend/node/node-raw-mesh-backend.ts +65 -32
  154. package/src/runtime/backend/node/node-socket-backend-adapter.ts +2 -2
  155. package/src/runtime/backend/raw-binding-port.ts +13 -0
  156. package/src/runtime/channels/channel-clients.ts +0 -11
  157. package/src/runtime/channels/channel-outbound-operations.ts +3 -11
  158. package/src/runtime/channels/channel-runtime-manager.ts +4 -15
  159. package/src/runtime/channels/channel-socket-registry.ts +69 -71
  160. package/src/runtime/channels/channel-transports.ts +0 -3
  161. package/src/runtime/channels/fanout-location-runtime.ts +4 -16
  162. package/src/runtime/execution/index.ts +68 -30
  163. package/src/runtime/execution/serial-execution-queue.ts +52 -20
  164. package/src/runtime/foundation/listener-records.ts +21 -0
  165. package/src/runtime/foundation/raw-service-mesh-runtime.ts +66 -466
  166. package/src/runtime/foundation/service-authority-payload-codec.ts +10 -0
  167. package/src/runtime/foundation/service-maintenance-runtime.ts +30 -9
  168. package/src/runtime/foundation/service-relocation-coordinator.ts +13 -0
  169. package/src/runtime/foundation/service-relocation-object-owner.ts +18 -5
  170. package/src/runtime/foundation/service-session-binding-ingress-port.ts +6 -14
  171. package/src/runtime/foundation/service-stateful-runtime.ts +151 -89
  172. package/src/runtime/foundation/service-stateful-wire-codec.ts +203 -121
  173. package/src/runtime/foundation/service-topology-registry.ts +8 -48
  174. package/src/runtime/host/actor-transfer-runtime.ts +94 -166
  175. package/src/runtime/host/index.ts +119 -54
  176. package/src/runtime/host/remote-bound-session-relay.ts +4 -3
  177. package/src/runtime/host/route-mesh-runtime.ts +9 -5
  178. package/src/runtime/host/service-relocation-host-runtime.ts +495 -376
  179. package/src/runtime/host/spot-runtime-options-factory.ts +24 -13
  180. package/src/runtime/host/stateful-authority-route-runtime.ts +8 -4
  181. package/src/runtime/host/user-spot-creation-coordinator.ts +147 -142
  182. package/src/runtime/locations/in-memory-provider-location-store.ts +11 -7
  183. package/src/runtime/locations/lifecycle.ts +3 -2
  184. package/src/runtime/locations/resolvers.ts +19 -13
  185. package/src/runtime/locations/spot-location-claims.ts +5 -34
  186. package/src/runtime/protocol/service_wire_codec.generated.ts +9 -2
  187. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +4 -4
  188. package/src/runtime/spots/formal-remote-actor-admission-registry.ts +6 -6
  189. package/src/runtime/spots/formal-remote-actor-transfer-registry.ts +3 -3
  190. package/src/runtime/spots/index.ts +341 -295
  191. package/src/runtime/spots/spot-activation-registry.ts +41 -31
  192. package/src/runtime/spots/spot-activation-state.ts +4 -16
  193. package/src/runtime/spots/spot-activation.ts +102 -80
  194. package/src/runtime/spots/spot-actor-admission-coordinator.ts +2 -2
  195. package/src/runtime/spots/spot-actor-join-dispatch.ts +6 -7
  196. package/src/runtime/spots/spot-actor-lifecycle-drain.ts +20 -18
  197. package/src/runtime/spots/spot-actor-membership.ts +81 -17
  198. package/src/runtime/spots/spot-entry-activation.ts +0 -3
  199. package/src/runtime/spots/spot-manager-public.ts +8 -4
  200. package/src/runtime/spots/spot-native-actor-join-admission.ts +2 -2
  201. package/src/runtime/spots/spot-node-runtime-manager.ts +12 -18
  202. package/src/runtime/spots/spot-outbound.ts +4 -47
  203. package/src/runtime/spots/spot-routed-actor-admission.ts +20 -0
  204. package/src/runtime/spots/spot-routed-frame-dispatch.ts +6 -76
  205. package/src/runtime/spots/spot-runtime-ports.ts +6 -17
  206. package/src/runtime/spots/spot-serial-executor.ts +9 -1
  207. package/src/runtime/spots/spot-serial-turn-executor.ts +10 -4
  208. package/src/runtime/streams/actor-session-binding-registry.ts +20 -308
  209. package/src/runtime/streams/actor-session-binding-runtime-owner.ts +2 -12
  210. package/src/runtime/streams/bound-session-service.ts +2 -36
  211. package/src/runtime/streams/index.ts +29 -27
  212. package/src/runtime/streams/session-serial-executor.ts +2 -2
  213. package/dist/runtime/actors/deferred-join-accepted-journal.d.ts +0 -47
  214. package/dist/runtime/actors/deferred-join-accepted-journal.js +0 -992
  215. package/src/runtime/actors/deferred-join-accepted-journal.ts +0 -1346
@@ -44,7 +44,6 @@ Object.defineProperty(exports, "DefaultZLinkSpotHandlerRegistry", { enumerable:
44
44
  var spot_publisher_transport_1 = require("./spot-publisher-transport");
45
45
  Object.defineProperty(exports, "ZLinkRuntimeSpotPublisherTransport", { enumerable: true, get: function () { return spot_publisher_transport_1.ZLinkRuntimeSpotPublisherTransport; } });
46
46
  const spot_activation_registry_1 = require("./spot-activation-registry");
47
- const spot_activation_state_1 = require("./spot-activation-state");
48
47
  const spot_activation_1 = require("./spot-activation");
49
48
  const spot_actor_membership_1 = require("./spot-actor-membership");
50
49
  const formal_remote_actor_transfer_registry_1 = require("./formal-remote-actor-transfer-registry");
@@ -74,8 +73,7 @@ class DefaultZLinkSpotManager {
74
73
  // callbacks (isInstanceMaterializing, sibling-generation lookups) resolve
75
74
  // in O(1) instead of scanning every pending key.
76
75
  pendingInstanceMaterializationsByPrefix = new Map();
77
- instanceActivationGates = new Map();
78
- pendingInstanceCloses = new Map();
76
+ closeOperations = new Map();
79
77
  pendingInstanceTerminals = new Map();
80
78
  pendingInstanceTerminalGenerations = new Map();
81
79
  deferredInstanceAuthorityReleases = new Map();
@@ -83,7 +81,7 @@ class DefaultZLinkSpotManager {
83
81
  idleSweepRunning = false;
84
82
  constructor(options, timerClock) {
85
83
  this.options = options;
86
- this.activations = new spot_activation_registry_1.ZLinkSpotActivationRegistry(options.metrics);
84
+ this.activations = new spot_activation_registry_1.ZLinkSpotActivationRegistry(options.metrics, (meshName, spotId) => this.isSpotClosing(meshName, spotId));
87
85
  this.factories = new Set(options.spotFactories);
88
86
  this.workerRuntime = options.workerRuntime ?? new workers_1.ZLinkWorkerRuntime();
89
87
  this.locationClaim = new spot_location_claim_1.ZLinkSpotLocationClaim({
@@ -101,6 +99,8 @@ class DefaultZLinkSpotManager {
101
99
  dispatchErrors: options.dispatchErrors
102
100
  });
103
101
  this.actorMembership = new spot_actor_membership_1.ZLinkSpotActorMembership({
102
+ isSpotClosing: (activation) => this.isSpotClosing(activation.meshName, activation.spotId) ||
103
+ this.activations.resolve(activation.meshName, activation.spotId) !== activation,
104
104
  resolveActivation: (spotId, meshName) => meshName === undefined
105
105
  ? this.activations.resolveUnique(spotId)
106
106
  : this.activations.resolve(meshName, spotId),
@@ -136,12 +136,12 @@ class DefaultZLinkSpotManager {
136
136
  spotRouterChannelIdForMesh: options.spotRouterChannelIdForMesh,
137
137
  providerResolver: options.providerResolver,
138
138
  dispatchErrors: options.dispatchErrors,
139
+ closeErrorSink: options.closeErrorSink,
139
140
  runtimeEventPublisher: options.runtimeEventPublisher,
140
141
  workerRuntime: this.workerRuntime,
141
142
  messageSerializers: options.messageSerializers,
142
143
  locationClaim: this.locationClaim,
143
144
  createNativeSpot: options.createNativeSpot,
144
- createReceived: options.createReceived,
145
145
  createTopicMessage: options.createTopicMessage,
146
146
  nativeSpotNodeProvider: options.nativeSpotNodeProvider,
147
147
  actorResolver: options.actorResolver,
@@ -152,7 +152,9 @@ class DefaultZLinkSpotManager {
152
152
  admission: options.admission,
153
153
  statefulExecutionAllowed: options.statefulExecutionAllowed,
154
154
  leaveActor: (spotId, actor, signal, meshName) => this.actorMembership.leaveActor(spotId, actor, signal, meshName),
155
- closeSpot: (meshName, spotId, signal, reason) => this.closeWithReason(meshName, spotId, signal, reason),
155
+ requestContextClose: (activation, objectGeneration, signal) => this.closeFromContext(activation, objectGeneration, signal),
156
+ isSpotClosing: (activation) => this.isSpotClosing(activation.meshName, activation.spotId) ||
157
+ this.activations.resolve(activation.meshName, activation.spotId) !== activation,
156
158
  registerActivation: (activation) => {
157
159
  this.activations.register(activation);
158
160
  this.scheduleIdleSweep();
@@ -199,10 +201,6 @@ class DefaultZLinkSpotManager {
199
201
  activeSpotCount(meshName) {
200
202
  return this.activations.list(meshName).length;
201
203
  }
202
- instanceActivationConcurrency(meshName) {
203
- const limit = this.options.activationConcurrencyLimitProvider?.(meshName) ?? 128;
204
- return { active: this.instanceActivationGates.get(meshName)?.active ?? 0, limit };
205
- }
206
204
  resolveRelocationActivation(meshName, spotId) {
207
205
  return this.activations.resolve(meshName, spotId);
208
206
  }
@@ -237,12 +235,12 @@ class DefaultZLinkSpotManager {
237
235
  await (0, abort_1.awaitWithAbort)(existing, signal);
238
236
  return;
239
237
  }
240
- const release = await this.acquireInstanceActivation(meshName, signal);
238
+ const release = await this.options.activationAdmission?.acquire(meshName, signal);
241
239
  try {
242
240
  await this.materializeInstanceCore(meshName, instanceType, spotId, objectGeneration, readyTarget?.objectGeneration === objectGeneration, signal);
243
241
  }
244
242
  finally {
245
- release();
243
+ release?.();
246
244
  }
247
245
  }
248
246
  async materializeInstanceCore(meshName, instanceType, spotId, objectGeneration, requiresReadyTarget, signal) {
@@ -260,11 +258,16 @@ class DefaultZLinkSpotManager {
260
258
  // explicit Instance intent arriving at that boundary must wait for the
261
259
  // old resources to be released before materializing the replacement.
262
260
  // This is lifecycle convergence; it does not resubmit application work.
263
- const pendingClose = this.pendingInstanceCloses.get(key);
261
+ const closeOperation = this.closeOperations.get(key);
262
+ const pendingClose = closeOperation?.ready;
264
263
  if (pendingClose !== undefined) {
265
264
  await (0, abort_1.awaitWithAbort)(pendingClose, signal);
266
265
  continue;
267
266
  }
267
+ if (closeOperation?.committed === true) {
268
+ await (0, abort_1.awaitWithAbort)(this.closeWithReason(meshName, spotId), signal);
269
+ continue;
270
+ }
268
271
  const closing = this.activations.closingOperation(meshName, spotId);
269
272
  if (closing !== undefined) {
270
273
  await (0, abort_1.awaitWithAbort)(closing.ready, signal);
@@ -340,58 +343,6 @@ class DefaultZLinkSpotManager {
340
343
  return;
341
344
  }
342
345
  }
343
- async acquireInstanceActivation(meshName, signal) {
344
- let gate = this.instanceActivationGates.get(meshName);
345
- if (gate === undefined) {
346
- gate = {
347
- meshName,
348
- limit: this.options.activationConcurrencyLimitProvider?.(meshName) ?? 128,
349
- active: 0,
350
- waiters: [],
351
- waiterHead: 0
352
- };
353
- this.instanceActivationGates.set(meshName, gate);
354
- }
355
- if (signal?.aborted === true)
356
- throw signal.reason;
357
- if (gate.active < gate.limit) {
358
- gate.active += 1;
359
- this.options.onInstanceActivationConcurrencyChanged?.(meshName);
360
- return () => this.releaseInstanceActivation(gate);
361
- }
362
- return new Promise((resolve, reject) => {
363
- const waiter = { resolve, reject, signal, abort: undefined };
364
- const slot = gate.waiters.length;
365
- waiter.abort = () => {
366
- if (gate.waiters[slot] === waiter)
367
- gate.waiters[slot] = undefined;
368
- reject(signal?.reason);
369
- };
370
- signal?.addEventListener('abort', waiter.abort, { once: true });
371
- gate.waiters.push(waiter);
372
- });
373
- }
374
- releaseInstanceActivation(gate) {
375
- while (gate.waiterHead < gate.waiters.length) {
376
- const waiter = gate.waiters[gate.waiterHead];
377
- gate.waiters[gate.waiterHead] = undefined;
378
- gate.waiterHead += 1;
379
- if (gate.waiterHead >= gate.waiters.length) {
380
- gate.waiters.length = 0;
381
- gate.waiterHead = 0;
382
- }
383
- if (waiter === undefined)
384
- continue;
385
- if (waiter.abort !== undefined)
386
- waiter.signal?.removeEventListener('abort', waiter.abort);
387
- waiter.resolve(() => this.releaseInstanceActivation(gate));
388
- return;
389
- }
390
- gate.waiters.length = 0;
391
- gate.waiterHead = 0;
392
- gate.active -= 1;
393
- this.options.onInstanceActivationConcurrencyChanged?.(gate.meshName);
394
- }
395
346
  isInstanceMaterialized(meshName, instanceType, spotId) {
396
347
  const activation = this.activations.resolve(meshName, spotId);
397
348
  return (activation !== undefined &&
@@ -401,10 +352,15 @@ class DefaultZLinkSpotManager {
401
352
  const prefix = instanceMaterializationPrefix(meshName, spotId);
402
353
  return (this.pendingInstanceMaterializationsByPrefix.get(prefix)?.size ?? 0) > 0;
403
354
  }
404
- isInstanceClosing(meshName, spotId) {
355
+ isSpotClosing(meshName, spotId) {
405
356
  const key = `${meshName}\0${String(spotId)}`;
406
- return (this.pendingInstanceCloses.has(key) ||
407
- this.activations.closingOperation(meshName, spotId) !== undefined);
357
+ return this.closeOperations.get(key)?.committed === true;
358
+ }
359
+ pendingSpotCloseDecision(meshName, spotId) {
360
+ return this.closeOperations.get(`${meshName}\0${String(spotId)}`)?.authorityDecision;
361
+ }
362
+ isInstanceClosing(meshName, spotId) {
363
+ return this.closeOperations.has(`${meshName}\0${String(spotId)}`);
408
364
  }
409
365
  async discardInstance(meshName, spotId) {
410
366
  const activation = this.activations.resolve(meshName, spotId);
@@ -443,7 +399,7 @@ class DefaultZLinkSpotManager {
443
399
  }
444
400
  this.pendingInstanceTerminals.delete(key);
445
401
  this.pendingInstanceTerminalGenerations.delete(key);
446
- const pendingClose = this.pendingInstanceCloses.get(key);
402
+ const pendingClose = this.closeOperations.get(key)?.ready;
447
403
  if (pendingClose !== undefined) {
448
404
  this.deferInstanceAuthorityRelease(key, objectGeneration);
449
405
  await pendingClose;
@@ -539,32 +495,16 @@ class DefaultZLinkSpotManager {
539
495
  !activation.beginIdleEviction()) {
540
496
  continue;
541
497
  }
542
- let durableClosing;
543
- try {
544
- durableClosing = await (this.options.beginInstanceIdleClosingAuthority?.(activation.meshName, activation.spotId) ?? Promise.resolve({ restoreReady: async () => undefined }));
545
- }
546
- catch (error) {
547
- activation.abortIdleEviction();
548
- throw error;
549
- }
550
- if (durableClosing === undefined) {
551
- activation.abortIdleEviction();
552
- continue;
553
- }
554
498
  const close = this.closeWithReason(activation.meshName, activation.spotId, undefined, contracts_1.ZLinkSpotCloseReason.IdleEvicted);
555
499
  const run = async () => {
556
500
  try {
557
501
  const closed = await close;
558
- if (!closed) {
559
- await durableClosing.restoreReady();
502
+ if (!closed)
560
503
  activation.abortIdleEviction();
561
- }
562
504
  }
563
505
  catch (error) {
564
- if (error instanceof spot_activation_state_1.ZLinkSpotCloseOccupiedError) {
565
- await durableClosing.restoreReady();
566
- }
567
- activation.abortIdleEviction();
506
+ if (!this.isSpotClosing(activation.meshName, activation.spotId))
507
+ activation.abortIdleEviction();
568
508
  throw error;
569
509
  }
570
510
  };
@@ -603,6 +543,7 @@ class DefaultZLinkSpotManager {
603
543
  (0, abort_1.throwIfAborted)(args.signal);
604
544
  const operation = this.activations.getOrBegin(meshName, spotType, spotId, async () => {
605
545
  this.options.admission?.requireRequest('SPOT create', meshName);
546
+ const release = await this.options.activationAdmission?.acquire(meshName, args.signal);
606
547
  const ownedRequest = args.request === undefined
607
548
  ? runtime_message_1.ZLinkBufferMessage.from(Buffer.alloc(0))
608
549
  : (0, payload_codec_1.encodeFrameworkPayloadMessage)(args.request, this.options.messageSerializers);
@@ -611,6 +552,7 @@ class DefaultZLinkSpotManager {
611
552
  }
612
553
  finally {
613
554
  ownedRequest.close();
555
+ release?.();
614
556
  }
615
557
  });
616
558
  return await (0, abort_1.awaitWithAbort)(operation.ready, args.signal);
@@ -639,99 +581,109 @@ class DefaultZLinkSpotManager {
639
581
  async close(meshName, spotId, signal) {
640
582
  return await this.closeWithReason(meshName, spotId, signal, contracts_1.ZLinkSpotCloseReason.ExplicitClose);
641
583
  }
642
- async closeWithReason(meshName, spotId, signal, reason = contracts_1.ZLinkSpotCloseReason.ExplicitClose, deadline) {
643
- requireMeshName(meshName);
644
- const key = `${meshName}\0${String(spotId)}`;
645
- const closing = this.activations.closingOperation(meshName, spotId);
646
- if (closing !== undefined) {
647
- return await closing.ready;
648
- }
649
- const pendingClose = this.pendingInstanceCloses.get(key);
650
- if (pendingClose !== undefined) {
651
- const pendingActivation = this.activations.resolve(meshName, spotId);
652
- if (pendingActivation?.serial.isCurrentTurn === true)
653
- return true;
654
- return await pendingClose;
584
+ async closeUserWithAuthority(meshName, spotId, beginAuthority, signal) {
585
+ return await this.closeWithReason(meshName, spotId, signal, contracts_1.ZLinkSpotCloseReason.ExplicitClose, undefined, beginAuthority);
586
+ }
587
+ closeFromContext(activation, objectGeneration, signal) {
588
+ const { meshName, spotId } = activation;
589
+ const operation = this.closeOperations.get(`${meshName}\0${String(spotId)}`);
590
+ const current = operation?.activation ?? this.activations.resolve(meshName, spotId);
591
+ if (current === undefined) {
592
+ return Promise.resolve(false);
655
593
  }
656
- const activation = this.activations.resolve(meshName, spotId);
657
- if (activation === undefined) {
658
- return false;
594
+ if (current !== activation || current.objectGeneration !== objectGeneration) {
595
+ return Promise.reject((0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.InvalidOperation, `Spot '${String(spotId)}' generation is stale.`));
659
596
  }
660
- const currentTurn = activation.serial.isCurrentTurn;
661
- const isInstance = activation.domain.kind === 'instance';
662
- const beginClose = () => {
663
- const seal = activation.sealExecution();
664
- const operation = this.activations.startClose(meshName, spotId, (target) => this.activationLifecycle.closeAfterSeal(target, seal, signal, reason, deadline), (target) => this.activationLifecycle.resourcesReleased(target), reason);
665
- if (operation === undefined) {
666
- activation.abortExecutionSeal(seal);
667
- }
668
- return operation;
669
- };
670
- if (isInstance) {
671
- const beginAuthorityClose = reason === contracts_1.ZLinkSpotCloseReason.ExplicitClose
672
- ? this.options.beginInstanceClosingAuthority
673
- : undefined;
674
- // Register the close gate before waiting for the current application
675
- // and activation-authority terminal completions. A close requested from
676
- // a handler cannot publish Closing until that handler's durable inbox
677
- // completion has removed the recovery fence.
678
- const trackedClosePromise = (async () => {
679
- const waitForApplication = this.options.instanceSpotApplicationQuiescenceProvider?.(meshName, spotId, signal);
680
- if (waitForApplication !== undefined) {
681
- await waitForApplication;
682
- }
683
- const closingAuthority = beginAuthorityClose === undefined
684
- ? undefined
685
- : await beginAuthorityClose(meshName, spotId);
686
- if (beginAuthorityClose !== undefined && closingAuthority === undefined) {
687
- return false;
688
- }
689
- const operation = beginClose();
690
- if (operation === undefined) {
691
- await closingAuthority?.restoreReady();
692
- return false;
693
- }
694
- const closed = await operation.ready;
695
- if (!closed)
696
- await closingAuthority?.restoreReady();
697
- return closed;
698
- })().finally(() => {
699
- if (this.pendingInstanceCloses.get(key) === trackedClosePromise) {
700
- this.pendingInstanceCloses.delete(key);
597
+ const beginUserAuthority = activation.domain.kind === 'user' && this.options.beginUserClosingAuthority !== undefined
598
+ ? (onCommitted) => this.options.beginUserClosingAuthority(meshName, spotId, objectGeneration, onCommitted)
599
+ : undefined;
600
+ return this.closeWithReason(meshName, spotId, signal, contracts_1.ZLinkSpotCloseReason.ExplicitClose, undefined, beginUserAuthority);
601
+ }
602
+ async closeWithReason(meshName, spotId, signal, reason = contracts_1.ZLinkSpotCloseReason.ExplicitClose, deadline, beginUserAuthority) {
603
+ requireMeshName(meshName);
604
+ const key = `${meshName}\0${String(spotId)}`;
605
+ let operation = this.closeOperations.get(key);
606
+ if (operation === undefined) {
607
+ const activation = this.activations.resolve(meshName, spotId);
608
+ if (activation === undefined)
609
+ return false;
610
+ if (activation.domain.kind === 'user' &&
611
+ reason === contracts_1.ZLinkSpotCloseReason.ExplicitClose &&
612
+ beginUserAuthority === undefined) {
613
+ throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.InvalidOperation, `User Spot '${String(spotId)}' Close requires its authority owner.`);
614
+ }
615
+ const beginAuthority = beginUserAuthority ??
616
+ (activation.domain.kind === 'instance' &&
617
+ reason === contracts_1.ZLinkSpotCloseReason.ExplicitClose &&
618
+ this.options.beginInstanceClosingAuthority !== undefined
619
+ ? (onCommitted) => this.options.beginInstanceClosingAuthority(meshName, spotId, onCommitted)
620
+ : activation.domain.kind === 'instance' &&
621
+ reason === contracts_1.ZLinkSpotCloseReason.IdleEvicted &&
622
+ this.options.beginInstanceIdleClosingAuthority !== undefined
623
+ ? (onCommitted) => this.options.beginInstanceIdleClosingAuthority(meshName, spotId, onCommitted)
624
+ : async (onCommitted) => {
625
+ onCommitted();
626
+ return { release: async () => undefined };
627
+ });
628
+ operation = {
629
+ activation,
630
+ reason,
631
+ beginAuthority,
632
+ committed: false
633
+ };
634
+ this.closeOperations.set(key, operation);
635
+ }
636
+ if (operation.ready === undefined) {
637
+ const active = operation;
638
+ const run = active.activation.serial.executeLifecycleOperation(() => this.runCloseOperation(meshName, spotId, active, signal, deadline));
639
+ active.ready = run.finally(() => {
640
+ active.ready = undefined;
641
+ if (this.closeOperations.get(key) === active) {
642
+ this.closeOperations.delete(key);
701
643
  }
702
644
  });
703
- this.pendingInstanceCloses.set(key, trackedClosePromise);
704
- if (currentTurn) {
705
- // The current turn must return before its terminal completion can
706
- // release the activation recovery fence and allow the durable close
707
- // CAS. The registered close gate prevents a second admission while
708
- // that detached close finishes.
709
- this.options.detachedTaskRunner?.runDetached(`instance spot close ${String(spotId)}`, async () => {
710
- await trackedClosePromise;
711
- });
712
- if (this.options.detachedTaskRunner === undefined) {
713
- void trackedClosePromise.catch(() => undefined);
714
- }
715
- return true;
716
- }
717
- return await trackedClosePromise;
718
645
  }
719
- const operation = beginClose();
720
- if (operation === undefined) {
646
+ return await operation.ready;
647
+ }
648
+ async runCloseOperation(meshName, spotId, operation, signal, deadline) {
649
+ const began = await this.beginCloseAuthority(meshName, spotId, operation, signal);
650
+ if (!began)
721
651
  return false;
652
+ const seal = operation.activation.sealExecution(true);
653
+ await this.activationLifecycle.sealForClose(operation.activation, seal);
654
+ const closingFailure = await this.activationLifecycle.cleanupClosedActivation(operation.activation, operation.reason, deadline);
655
+ await operation.authority?.release();
656
+ // The incarnation has no further lifecycle item once its authority is
657
+ // released, so its lifecycle lane admission closes with this Close.
658
+ operation.activation.serial.closeAdmission();
659
+ this.activations.finishClose(meshName, spotId);
660
+ this.closeOperations.delete(`${meshName}\0${String(spotId)}`);
661
+ // An OnClosing failure never changes a Close result; host shutdown alone
662
+ // reports it as its teardown outcome after cleanup has finished.
663
+ if (closingFailure !== undefined && operation.reason === contracts_1.ZLinkSpotCloseReason.HostShutdown) {
664
+ throw closingFailure;
722
665
  }
723
- if (currentTurn) {
724
- if (operation.started) {
725
- this.options.detachedTaskRunner?.runDetached(`spot close ${String(spotId)}`, async () => {
726
- await operation.ready;
727
- });
728
- if (this.options.detachedTaskRunner === undefined) {
729
- void operation.ready.catch(() => undefined);
730
- }
731
- }
732
- return true;
666
+ return true;
667
+ }
668
+ async beginCloseAuthority(meshName, spotId, operation, signal) {
669
+ await this.options.instanceSpotApplicationQuiescenceProvider?.(meshName, spotId, signal);
670
+ if (!operation.activation.canClose(operation.reason))
671
+ return false;
672
+ if (operation.activation.executionBarrier.isSealed) {
673
+ throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.SpotMoving, `Spot '${String(spotId)}' is sealed for relocation.`, true);
674
+ }
675
+ const authorityPromise = Promise.resolve().then(() => operation.beginAuthority(() => {
676
+ operation.committed = true;
677
+ }));
678
+ operation.authorityDecision = authorityPromise.then(() => undefined, () => undefined);
679
+ const authority = await authorityPromise;
680
+ if (authority === undefined)
681
+ return false;
682
+ operation.authority = authority;
683
+ if (!this.isSpotClosing(meshName, spotId)) {
684
+ throw new Error(`Spot '${String(spotId)}' Closing authority did not confirm its commit.`);
733
685
  }
734
- return await operation.ready;
686
+ return true;
735
687
  }
736
688
  async executeOnSpot(spotType, spotId, operation, signal) {
737
689
  (0, abort_1.throwIfAborted)(signal);
@@ -750,9 +702,6 @@ class DefaultZLinkSpotManager {
750
702
  hasActiveSpot(spotId) {
751
703
  return this.activations.resolveUnique(spotId) !== undefined;
752
704
  }
753
- canCloseUserSpot(meshName, spotId) {
754
- return this.activations.canClose(meshName, spotId);
755
- }
756
705
  beginUserSpotPublication(meshName, spotId) {
757
706
  this.activations.stage(meshName, spotId);
758
707
  }
@@ -884,7 +833,7 @@ class DefaultZLinkSpotManager {
884
833
  requireMeshSpotReply(record.reply((0, channel_envelope_1.encodeChannelReplyParts)(envelope.header, response, codecs)));
885
834
  }
886
835
  catch (error) {
887
- requireMeshSpotReply(record.reply((0, channel_envelope_1.encodeChannelErrorReplyParts)(envelope.header, error instanceof Error ? error.message : String(error))));
836
+ requireMeshSpotReply(record.reply((0, channel_envelope_1.encodeChannelErrorReplyParts)(envelope.header, error)));
888
837
  }
889
838
  }
890
839
  async ensureInstanceApplicationActivation(meshName, spotId) {
@@ -975,7 +924,7 @@ class DefaultZLinkSpotManager {
975
924
  action: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorAction.ReplyError,
976
925
  error
977
926
  });
978
- requireMeshSpotReply(record.reply((0, channel_envelope_1.encodeChannelErrorReplyParts)(envelope.header, error instanceof Error ? error.message : String(error))));
927
+ requireMeshSpotReply(record.reply((0, channel_envelope_1.encodeChannelErrorReplyParts)(envelope.header, error)));
979
928
  }
980
929
  });
981
930
  }
@@ -1074,6 +1023,29 @@ class DefaultZLinkSpotManager {
1074
1023
  }
1075
1024
  }
1076
1025
  async dispatchMeshActorJoin(meshName, owner, record) {
1026
+ const spotId = owner.spotId;
1027
+ const activation = spotId === null ? undefined : this.activations.resolve(meshName, spotId);
1028
+ if (activation?.domain.kind === 'user') {
1029
+ await activation.serial.executeLifecycleOperation(async () => {
1030
+ if (this.isSpotClosing(meshName, activation.spotId) ||
1031
+ this.activations.resolve(meshName, activation.spotId) !== activation) {
1032
+ const rejection = (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.RequestRejected, `User Spot '${String(spotId)}' is closing.`);
1033
+ if (record.replyFailure !== undefined) {
1034
+ const terminal = (0, framework_errors_internal_1.internalFrameworkWireReply)(rejection);
1035
+ requireMeshSpotReply(record.replyFailure(terminal.terminalResult, terminal.failureCode));
1036
+ }
1037
+ else {
1038
+ requireMeshSpotReply(record.replyActorJoin(1, []));
1039
+ }
1040
+ return;
1041
+ }
1042
+ await this.dispatchMeshActorJoinCore(meshName, owner, record);
1043
+ });
1044
+ return;
1045
+ }
1046
+ await this.dispatchMeshActorJoinCore(meshName, owner, record);
1047
+ }
1048
+ async dispatchMeshActorJoinCore(meshName, owner, record) {
1077
1049
  const spotId = owner.spotId;
1078
1050
  const control = record.kindData;
1079
1051
  if (control?.kind !== 'actorControl') {
@@ -1157,7 +1129,7 @@ class DefaultZLinkSpotManager {
1157
1129
  let ownedCallbackRequest;
1158
1130
  let materialized = false;
1159
1131
  let reply;
1160
- let deferredJoinRoot;
1132
+ let deferredJoinCompletion;
1161
1133
  let preparedTransferState;
1162
1134
  let admissionOutcome;
1163
1135
  let accepted = false;
@@ -1286,13 +1258,13 @@ class DefaultZLinkSpotManager {
1286
1258
  if (this.options.dispatchEntryActorJoin !== undefined &&
1287
1259
  transferRequest.completionOperationId !== undefined &&
1288
1260
  this.options.actorTransferRuntime !== undefined) {
1289
- deferredJoinRoot =
1290
- await this.options.actorTransferRuntime.prepareDeferredJoinAccepted(actorId, transferRequest.completionOperationId, actorRef, Buffer.alloc(0));
1261
+ deferredJoinCompletion =
1262
+ this.options.actorTransferRuntime.prepareDeferredJoinAccepted(actorId, transferRequest.completionOperationId, actorRef, Buffer.alloc(0));
1291
1263
  }
1292
1264
  admissionOutcome = {
1293
1265
  accepted: this.options.dispatchEntryActorJoin !== undefined,
1294
1266
  actorRef,
1295
- ...(deferredJoinRoot === undefined ? {} : { deferredJoinRoot })
1267
+ ...(deferredJoinCompletion === undefined ? {} : { deferredJoinCompletion })
1296
1268
  };
1297
1269
  }
1298
1270
  else if (activation === undefined) {
@@ -1313,8 +1285,8 @@ class DefaultZLinkSpotManager {
1313
1285
  if (response.accepted &&
1314
1286
  transferRequest.completionOperationId !== undefined &&
1315
1287
  this.options.actorTransferRuntime !== undefined) {
1316
- deferredJoinRoot =
1317
- await this.options.actorTransferRuntime.prepareDeferredJoinAccepted(actorId, transferRequest.completionOperationId, actorRef, encodedReply?.data() ?? Buffer.alloc(0), encodedReply === undefined
1288
+ deferredJoinCompletion =
1289
+ this.options.actorTransferRuntime.prepareDeferredJoinAccepted(actorId, transferRequest.completionOperationId, actorRef, encodedReply?.data() ?? Buffer.alloc(0), encodedReply === undefined
1318
1290
  ? undefined
1319
1291
  : (0, payload_codec_1.frameworkPayloadContentType)(encodedReply));
1320
1292
  this.options.runtimeEventPublisher?.publish({
@@ -1327,7 +1299,7 @@ class DefaultZLinkSpotManager {
1327
1299
  admissionOutcome = {
1328
1300
  accepted: response.accepted,
1329
1301
  actorRef,
1330
- ...(deferredJoinRoot === undefined ? {} : { deferredJoinRoot }),
1302
+ ...(deferredJoinCompletion === undefined ? {} : { deferredJoinCompletion }),
1331
1303
  ...(encodedReply === undefined
1332
1304
  ? {}
1333
1305
  : {
@@ -1366,7 +1338,7 @@ class DefaultZLinkSpotManager {
1366
1338
  if ('error' in admissionOutcome) {
1367
1339
  throw admissionOutcome.error;
1368
1340
  }
1369
- deferredJoinRoot = admissionOutcome.deferredJoinRoot;
1341
+ deferredJoinCompletion = admissionOutcome.deferredJoinCompletion;
1370
1342
  }
1371
1343
  if (actor === undefined &&
1372
1344
  transferRequest !== undefined &&
@@ -1436,7 +1408,7 @@ class DefaultZLinkSpotManager {
1436
1408
  if (accepted &&
1437
1409
  transferRequest?.completionOperationId !== undefined &&
1438
1410
  this.options.actorTransferRuntime !== undefined) {
1439
- deferredJoinRoot = await this.options.actorTransferRuntime.prepareDeferredJoinAccepted(actorId, transferRequest.completionOperationId, actorRef, reply?.data() ?? Buffer.alloc(0), reply === undefined ? undefined : (0, payload_codec_1.frameworkPayloadContentType)(reply));
1411
+ deferredJoinCompletion = this.options.actorTransferRuntime.prepareDeferredJoinAccepted(actorId, transferRequest.completionOperationId, actorRef, reply?.data() ?? Buffer.alloc(0), reply === undefined ? undefined : (0, payload_codec_1.frameworkPayloadContentType)(reply));
1440
1412
  this.options.runtimeEventPublisher?.publish({
1441
1413
  sourceName: 'zlink.framework.actor-handoff',
1442
1414
  timestamp: new Date(),
@@ -1481,7 +1453,7 @@ class DefaultZLinkSpotManager {
1481
1453
  // A lightweight Core actor-join notification can create the Actor
1482
1454
  // before the formal transfer request reaches this branch. Preserve
1483
1455
  // the full relocation fence in that race so a later Session bind
1484
- // refresh cannot erase the seal or accepted journal.
1456
+ // refresh cannot erase the seal.
1485
1457
  this.options.actorTransferRuntime.rememberRoutedActorTransferTarget(actorId, transferRequest.remoteBoundSessionTarget);
1486
1458
  }
1487
1459
  if (accepted &&
@@ -1494,7 +1466,7 @@ class DefaultZLinkSpotManager {
1494
1466
  spotId,
1495
1467
  transferId: transferRequest.transferId,
1496
1468
  handoffBacklog: transferRequest.handoffBacklog,
1497
- deferredJoinRoot
1469
+ deferredJoinCompletion
1498
1470
  });
1499
1471
  if (isRemoteCommit && admissionRecord !== undefined) {
1500
1472
  this.formalRemoteActorAdmissions.markCommitted(transferRequest.transferId, actor);
@@ -1583,9 +1555,6 @@ class DefaultZLinkSpotManager {
1583
1555
  if (control.canonicalActorJoin !== undefined) {
1584
1556
  this.formalRemoteActorAdmissions.fail(control.canonicalActorJoin.handoffId, error);
1585
1557
  }
1586
- if (deferredJoinRoot !== undefined && targetCommitPublished !== true) {
1587
- await this.options.actorTransferRuntime?.discardDeferredJoinAccepted(deferredJoinRoot);
1588
- }
1589
1558
  if (targetCommitPublished !== true) {
1590
1559
  this.formalRemoteTransfers.delete(actorId);
1591
1560
  }
@@ -1602,6 +1571,33 @@ class DefaultZLinkSpotManager {
1602
1571
  }
1603
1572
  }
1604
1573
  async dispatchMeshSpotControl(meshName, owner, record) {
1574
+ const spotId = owner.spotId;
1575
+ const activation = spotId === null ? undefined : this.activations.resolve(meshName, spotId);
1576
+ if (activation?.domain.kind === 'user') {
1577
+ const transfer = { terminal: undefined };
1578
+ const operation = activation.serial.executeLifecycleOperation(async () => {
1579
+ await this.dispatchMeshSpotControlCore(meshName, owner, record, (terminal) => {
1580
+ transfer.terminal = terminal;
1581
+ });
1582
+ await transfer.terminal;
1583
+ });
1584
+ const control = record.kindData;
1585
+ const actorId = control?.kind === 'actorControl' ? control.currentActor?.actorId : undefined;
1586
+ if (control?.kind === 'actorControl' &&
1587
+ control.lifecycleKind === service_runtime_contracts_1.ActorLifecycleKind.Joined &&
1588
+ actorId !== undefined &&
1589
+ this.formalRemoteTransfers.get(actorId) !== undefined) {
1590
+ this.options.detachedTaskRunner?.runDetached(`actor transfer target commit ${actorId}`, async () => await operation);
1591
+ if (this.options.detachedTaskRunner === undefined)
1592
+ void operation.catch(() => undefined);
1593
+ return;
1594
+ }
1595
+ await operation;
1596
+ return;
1597
+ }
1598
+ await this.dispatchMeshSpotControlCore(meshName, owner, record);
1599
+ }
1600
+ async dispatchMeshSpotControlCore(meshName, owner, record, onDetachedTerminal) {
1605
1601
  const spotId = owner.spotId;
1606
1602
  const control = record.kindData;
1607
1603
  if (control?.kind !== 'actorControl') {
@@ -1664,15 +1660,15 @@ class DefaultZLinkSpotManager {
1664
1660
  await activation.spot.onJoinedActor(actor);
1665
1661
  };
1666
1662
  const completePublicJoin = async () => {
1667
- const deferredJoinRoot = pendingTransfer?.deferredJoinRoot;
1668
- if (deferredJoinRoot !== undefined) {
1663
+ const deferredJoinCompletion = pendingTransfer?.deferredJoinCompletion;
1664
+ if (deferredJoinCompletion !== undefined) {
1669
1665
  const currentRef = this.options.actorTransferRuntime === undefined
1670
1666
  ? null
1671
1667
  : control.currentActor;
1672
1668
  if (currentRef === null) {
1673
1669
  throw new Error(`Actor '${actor.context.actorId}' has no target ref for deferred Join completion.`);
1674
1670
  }
1675
- await this.options.actorTransferRuntime?.commitAndDeliverDeferredJoinAccepted(deferredJoinRoot, actor, currentRef, (operation) => activation.executeActor(actor.context.actorId, async () => await operation()));
1671
+ await this.options.actorTransferRuntime?.deliverDeferredJoinAccepted(deferredJoinCompletion, actor, currentRef, (operation) => activation.executeActor(actor.context.actorId, async () => await operation()));
1676
1672
  }
1677
1673
  };
1678
1674
  const completeTargetReady = async () => {
@@ -1730,9 +1726,15 @@ class DefaultZLinkSpotManager {
1730
1726
  // them only after the lifecycle turn above has been released.
1731
1727
  await completeTargetReady();
1732
1728
  };
1733
- this.options.detachedTaskRunner?.runDetached(`actor transfer target commit ${actor.context.actorId}`, resume);
1734
- if (this.options.detachedTaskRunner === undefined) {
1735
- void resume().catch(() => undefined);
1729
+ const terminal = resume();
1730
+ if (onDetachedTerminal !== undefined) {
1731
+ onDetachedTerminal(terminal);
1732
+ }
1733
+ else {
1734
+ this.options.detachedTaskRunner?.runDetached(`actor transfer target commit ${actor.context.actorId}`, async () => await terminal);
1735
+ if (this.options.detachedTaskRunner === undefined) {
1736
+ void terminal.catch(() => undefined);
1737
+ }
1736
1738
  }
1737
1739
  return;
1738
1740
  }
@@ -1793,7 +1795,8 @@ class DefaultZLinkSpotManager {
1793
1795
  await activation.serial.execute(() => activation.spot.onJoinedActor(actor));
1794
1796
  }
1795
1797
  await submitSourceLeave(admission.admission.actorRef.nodeRid);
1796
- if (outcome.deferredJoinRoot !== undefined && this.options.actorTransferRuntime !== undefined) {
1798
+ if (outcome.deferredJoinCompletion !== undefined &&
1799
+ this.options.actorTransferRuntime !== undefined) {
1797
1800
  const submitMailbox = targetsEntry
1798
1801
  ? (operation) => operation()
1799
1802
  : (operation) => {
@@ -1803,12 +1806,12 @@ class DefaultZLinkSpotManager {
1803
1806
  }
1804
1807
  return activation.executeActor(actor.context.actorId, operation);
1805
1808
  };
1806
- await this.options.actorTransferRuntime.commitAndDeliverDeferredJoinAccepted(outcome.deferredJoinRoot, actor, actorRef, submitMailbox, signal);
1809
+ await this.options.actorTransferRuntime.deliverDeferredJoinAccepted(outcome.deferredJoinCompletion, actor, actorRef, submitMailbox, signal);
1807
1810
  }
1808
1811
  this.formalRemoteActorAdmissions.markCommitted(relocationId, actor);
1809
1812
  return true;
1810
1813
  }
1811
- async restoreCanonicalActorJoinRecovery(recovery, signal, canonicalInventoryDigest) {
1814
+ async restoreCanonicalActorJoinRecovery(recovery, signal) {
1812
1815
  (0, abort_1.throwIfAborted)(signal);
1813
1816
  const admission = this.formalRemoteActorAdmissions.get(recovery.request.handoffId);
1814
1817
  if (admission === undefined) {
@@ -1850,15 +1853,14 @@ class DefaultZLinkSpotManager {
1850
1853
  }
1851
1854
  // Command 28 carries a present reply in one outer framework-multipart
1852
1855
  // part. The accepted target admission still owns the inner typed content
1853
- // type, which the durable completion must retain for application decode.
1856
+ // type, which the process-local completion keeps for application decode.
1854
1857
  const admittedContentType = admittedReply.byteLength === 0 ? recovery.replyContentType : outcome.replyContentType;
1855
1858
  const transfer = this.options.actorTransferRuntime;
1856
1859
  if (transfer === undefined) {
1857
1860
  throw new Error('Canonical Actor Join recovery requires the Actor transfer runtime.');
1858
1861
  }
1859
- const deferred = await transfer.prepareDeferredJoinAccepted(recovery.request.actorId, recovery.operationId, admitted.actorRef, recovery.reply, admittedContentType, signal, canonicalInventoryDigest);
1860
- this.formalRemoteActorAdmissions.attachDeferredJoinRoot(recovery.request.handoffId, deferred);
1861
- return deferred;
1862
+ const deferred = transfer.prepareDeferredJoinAccepted(recovery.request.actorId, recovery.operationId, admitted.actorRef, recovery.reply, admittedContentType);
1863
+ this.formalRemoteActorAdmissions.attachDeferredJoinCompletion(recovery.request.handoffId, deferred);
1862
1864
  }
1863
1865
  encodeMeshActorReply(requestHeaderPart, kind, payload) {
1864
1866
  // Spec 27 §4/§7: the decode gate strips the observation-only flow pair at