@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
@@ -72,7 +72,7 @@ import { ZLinkWorkerRuntime } from '../workers';
72
72
  import {
73
73
  type ZLinkActorHandoffPrefixAdmission,
74
74
  type ZLinkActorHandoffPrefixRecord,
75
- type ZLinkDeferredJoinAcceptedRoot,
75
+ type ZLinkDeferredJoinCompletion,
76
76
  type ZLinkRemoteBoundSessionTarget
77
77
  } from '../actors';
78
78
  import type { ZLinkLocationLifecycle } from '../locations';
@@ -130,7 +130,7 @@ export {
130
130
  } from './spot-handler-registry';
131
131
  export { ZLinkRuntimeSpotPublisherTransport } from './spot-publisher-transport';
132
132
  import { ZLinkSpotActivationRegistry } from './spot-activation-registry';
133
- import { ZLinkSpotCloseOccupiedError, type ZLinkSpotActivation } from './spot-activation-state';
133
+ import type { ZLinkSpotActivation } from './spot-activation-state';
134
134
  import { ZLinkSpotActivationLifecycle, type ZLinkNativeSpotAuthority } from './spot-activation';
135
135
  import { ZLinkSpotActorMembership, type ZLinkActorJoinRollback } from './spot-actor-membership';
136
136
  import { ZLinkFormalRemoteActorTransferRegistry } from './formal-remote-actor-transfer-registry';
@@ -145,6 +145,7 @@ import type {
145
145
  ZLinkSpotBoundSessionRuntime
146
146
  } from './spot-runtime-ports';
147
147
  import type { ZLinkRuntimeAdmissionGate } from '../admission';
148
+ import type { ZLinkActivationAdmission } from '../activation-admission';
148
149
  import type { ZLinkDetachedTaskRunner } from './spot-actor-join-dispatch';
149
150
  import type { ZLinkLocalSpotCreateResult } from './spot-manager-internal-contracts';
150
151
  export type { ZLinkLocalSpotCreateResult } from './spot-manager-internal-contracts';
@@ -227,6 +228,7 @@ export interface ZLinkSpotManagerOptions {
227
228
  readonly channelMeshNameForChannel?: (channelName: string) => string | undefined;
228
229
  readonly providerResolver?: ZLinkProviderResolver;
229
230
  readonly dispatchErrors?: ZLinkDispatchErrorReporter;
231
+ readonly closeErrorSink?: import('../diagnostics/dispatch-error-port').ZLinkDispatchErrorSink;
230
232
  readonly runtimeEventPublisher?: ZLinkRuntimeEventPublisher;
231
233
  readonly workerRuntime?: ZLinkWorkerRuntime;
232
234
  readonly messageSerializers?: ReadonlyMap<string, ZLinkMessageSerializer>;
@@ -238,12 +240,21 @@ export interface ZLinkSpotManagerOptions {
238
240
  ) => Promise<void>;
239
241
  readonly beginInstanceIdleClosingAuthority?: (
240
242
  meshName: string,
241
- spotId: RoutingId
242
- ) => Promise<{ restoreReady(): Promise<void> } | undefined>;
243
+ spotId: RoutingId,
244
+ onCommitted: () => void
245
+ ) => Promise<{ release(): Promise<void> } | undefined>;
243
246
  readonly beginInstanceClosingAuthority?: (
244
247
  meshName: string,
245
- spotId: RoutingId
246
- ) => Promise<{ restoreReady(): Promise<void> } | undefined>;
248
+ spotId: RoutingId,
249
+ onCommitted: () => void
250
+ ) => Promise<{ release(): Promise<void> } | undefined>;
251
+ /** Moves a local User Spot incarnation's authority to Closing for a context Close. */
252
+ readonly beginUserClosingAuthority?: (
253
+ meshName: string,
254
+ spotId: RoutingId,
255
+ objectGeneration: bigint,
256
+ onCommitted: () => void
257
+ ) => Promise<{ release(): Promise<void> } | undefined>;
247
258
  readonly instanceSpotApplicationTargetProvider?: (
248
259
  meshName: string,
249
260
  spotId: RoutingId
@@ -261,7 +272,6 @@ export interface ZLinkSpotManagerOptions {
261
272
  spotId: RoutingId,
262
273
  authority?: ZLinkNativeSpotAuthority
263
274
  ) => ZLinkBackendSpot | undefined;
264
- readonly createReceived?: () => import('../backend').ZLinkBackendReceived;
265
275
  readonly createTopicMessage?: () => import('../backend').ZLinkBackendTopicMessage;
266
276
  readonly nativeSpotNodeProvider?: (meshName: string) => ZLinkBackendSpotNode | undefined;
267
277
  readonly actorResolver?: (actorId: string) => ZLinkActor | undefined;
@@ -281,8 +291,19 @@ export interface ZLinkSpotManagerOptions {
281
291
  readonly metrics?: import('../diagnostics').ZLinkRuntimeMetrics;
282
292
  readonly admission?: ZLinkRuntimeAdmissionGate;
283
293
  readonly statefulExecutionAllowed?: () => boolean;
284
- readonly activationConcurrencyLimitProvider?: (meshName: string) => number;
285
- readonly onInstanceActivationConcurrencyChanged?: (meshName: string) => void;
294
+ readonly activationAdmission?: ZLinkActivationAdmission;
295
+ }
296
+
297
+ interface ZLinkTargetSpotCloseOperation {
298
+ readonly activation: ZLinkSpotActivation;
299
+ readonly reason: ZLinkSpotCloseReason;
300
+ readonly beginAuthority: (
301
+ onCommitted: () => void
302
+ ) => Promise<{ release(): Promise<void> } | undefined>;
303
+ committed: boolean;
304
+ authority?: { release(): Promise<void> };
305
+ authorityDecision?: Promise<void>;
306
+ ready?: Promise<boolean>;
286
307
  }
287
308
 
288
309
  export class DefaultZLinkSpotManager {
@@ -306,27 +327,7 @@ export class DefaultZLinkSpotManager {
306
327
  string,
307
328
  Map<string, Promise<ZLinkSpotActivation>>
308
329
  >();
309
- private readonly instanceActivationGates = new Map<
310
- string,
311
- {
312
- readonly meshName: string;
313
- readonly limit: number;
314
- active: number;
315
- // Tombstone slots + head cursor keep abort O(1) and admit O(1) amortized
316
- // instead of indexOf/splice + shift scans under a cancellation storm.
317
- readonly waiters: Array<
318
- | {
319
- resolve: (release: () => void) => void;
320
- reject: (error: unknown) => void;
321
- signal?: AbortSignal;
322
- abort?: () => void;
323
- }
324
- | undefined
325
- >;
326
- waiterHead: number;
327
- }
328
- >();
329
- private readonly pendingInstanceCloses = new Map<string, Promise<boolean>>();
330
+ private readonly closeOperations = new Map<string, ZLinkTargetSpotCloseOperation>();
330
331
  private readonly pendingInstanceTerminals = new Map<string, number>();
331
332
  private readonly pendingInstanceTerminalGenerations = new Map<string, Map<string, number>>();
332
333
  private readonly deferredInstanceAuthorityReleases = new Map<string, Set<bigint>>();
@@ -337,7 +338,9 @@ export class DefaultZLinkSpotManager {
337
338
  private readonly options: ZLinkSpotManagerOptions,
338
339
  timerClock?: import('./spot-timer').ZLinkTimerClock
339
340
  ) {
340
- this.activations = new ZLinkSpotActivationRegistry(options.metrics);
341
+ this.activations = new ZLinkSpotActivationRegistry(options.metrics, (meshName, spotId) =>
342
+ this.isSpotClosing(meshName, spotId)
343
+ );
341
344
  this.factories = new Set(options.spotFactories);
342
345
  this.workerRuntime = options.workerRuntime ?? new ZLinkWorkerRuntime();
343
346
  this.locationClaim = new ZLinkSpotLocationClaim({
@@ -356,6 +359,9 @@ export class DefaultZLinkSpotManager {
356
359
  dispatchErrors: options.dispatchErrors
357
360
  });
358
361
  this.actorMembership = new ZLinkSpotActorMembership({
362
+ isSpotClosing: (activation) =>
363
+ this.isSpotClosing(activation.meshName, activation.spotId) ||
364
+ this.activations.resolve(activation.meshName, activation.spotId) !== activation,
359
365
  resolveActivation: (spotId, meshName) =>
360
366
  meshName === undefined
361
367
  ? this.activations.resolveUnique(spotId)
@@ -392,12 +398,12 @@ export class DefaultZLinkSpotManager {
392
398
  spotRouterChannelIdForMesh: options.spotRouterChannelIdForMesh,
393
399
  providerResolver: options.providerResolver,
394
400
  dispatchErrors: options.dispatchErrors,
401
+ closeErrorSink: options.closeErrorSink,
395
402
  runtimeEventPublisher: options.runtimeEventPublisher,
396
403
  workerRuntime: this.workerRuntime,
397
404
  messageSerializers: options.messageSerializers,
398
405
  locationClaim: this.locationClaim,
399
406
  createNativeSpot: options.createNativeSpot,
400
- createReceived: options.createReceived,
401
407
  createTopicMessage: options.createTopicMessage,
402
408
  nativeSpotNodeProvider: options.nativeSpotNodeProvider,
403
409
  actorResolver: options.actorResolver,
@@ -409,8 +415,11 @@ export class DefaultZLinkSpotManager {
409
415
  statefulExecutionAllowed: options.statefulExecutionAllowed,
410
416
  leaveActor: (spotId, actor, signal, meshName) =>
411
417
  this.actorMembership.leaveActor(spotId, actor, signal, meshName),
412
- closeSpot: (meshName, spotId, signal, reason) =>
413
- this.closeWithReason(meshName, spotId, signal, reason),
418
+ requestContextClose: (activation, objectGeneration, signal) =>
419
+ this.closeFromContext(activation, objectGeneration, signal),
420
+ isSpotClosing: (activation) =>
421
+ this.isSpotClosing(activation.meshName, activation.spotId) ||
422
+ this.activations.resolve(activation.meshName, activation.spotId) !== activation,
414
423
  registerActivation: (activation) => {
415
424
  this.activations.register(activation);
416
425
  this.scheduleIdleSweep();
@@ -470,14 +479,6 @@ export class DefaultZLinkSpotManager {
470
479
  return this.activations.list(meshName).length;
471
480
  }
472
481
 
473
- instanceActivationConcurrency(meshName: string): {
474
- readonly active: number;
475
- readonly limit: number;
476
- } {
477
- const limit = this.options.activationConcurrencyLimitProvider?.(meshName) ?? 128;
478
- return { active: this.instanceActivationGates.get(meshName)?.active ?? 0, limit };
479
- }
480
-
481
482
  resolveRelocationActivation(
482
483
  meshName: string,
483
484
  spotId: RoutingId
@@ -548,7 +549,7 @@ export class DefaultZLinkSpotManager {
548
549
  await awaitWithAbort(existing, signal);
549
550
  return;
550
551
  }
551
- const release = await this.acquireInstanceActivation(meshName, signal);
552
+ const release = await this.options.activationAdmission?.acquire(meshName, signal);
552
553
  try {
553
554
  await this.materializeInstanceCore(
554
555
  meshName,
@@ -559,7 +560,7 @@ export class DefaultZLinkSpotManager {
559
560
  signal
560
561
  );
561
562
  } finally {
562
- release();
563
+ release?.();
563
564
  }
564
565
  }
565
566
 
@@ -590,11 +591,16 @@ export class DefaultZLinkSpotManager {
590
591
  // explicit Instance intent arriving at that boundary must wait for the
591
592
  // old resources to be released before materializing the replacement.
592
593
  // This is lifecycle convergence; it does not resubmit application work.
593
- const pendingClose = this.pendingInstanceCloses.get(key);
594
+ const closeOperation = this.closeOperations.get(key);
595
+ const pendingClose = closeOperation?.ready;
594
596
  if (pendingClose !== undefined) {
595
597
  await awaitWithAbort(pendingClose, signal);
596
598
  continue;
597
599
  }
600
+ if (closeOperation?.committed === true) {
601
+ await awaitWithAbort(this.closeWithReason(meshName, spotId), signal);
602
+ continue;
603
+ }
598
604
  const closing = this.activations.closingOperation(meshName, spotId);
599
605
  if (closing !== undefined) {
600
606
  await awaitWithAbort(closing.ready, signal);
@@ -687,73 +693,6 @@ export class DefaultZLinkSpotManager {
687
693
  }
688
694
  }
689
695
 
690
- private async acquireInstanceActivation(
691
- meshName: string,
692
- signal?: AbortSignal
693
- ): Promise<() => void> {
694
- let gate = this.instanceActivationGates.get(meshName);
695
- if (gate === undefined) {
696
- gate = {
697
- meshName,
698
- limit: this.options.activationConcurrencyLimitProvider?.(meshName) ?? 128,
699
- active: 0,
700
- waiters: [],
701
- waiterHead: 0
702
- };
703
- this.instanceActivationGates.set(meshName, gate);
704
- }
705
- if (signal?.aborted === true) throw signal.reason;
706
- if (gate.active < gate.limit) {
707
- gate.active += 1;
708
- this.options.onInstanceActivationConcurrencyChanged?.(meshName);
709
- return () => this.releaseInstanceActivation(gate!);
710
- }
711
- return new Promise<() => void>((resolve, reject) => {
712
- const waiter = { resolve, reject, signal, abort: undefined as (() => void) | undefined };
713
- const slot = gate!.waiters.length;
714
- waiter.abort = () => {
715
- if (gate!.waiters[slot] === waiter) gate!.waiters[slot] = undefined;
716
- reject(signal?.reason);
717
- };
718
- signal?.addEventListener('abort', waiter.abort, { once: true });
719
- gate!.waiters.push(waiter);
720
- });
721
- }
722
-
723
- private releaseInstanceActivation(gate: {
724
- readonly meshName: string;
725
- readonly limit: number;
726
- active: number;
727
- readonly waiters: Array<
728
- | {
729
- resolve: (release: () => void) => void;
730
- reject: (error: unknown) => void;
731
- signal?: AbortSignal;
732
- abort?: () => void;
733
- }
734
- | undefined
735
- >;
736
- waiterHead: number;
737
- }): void {
738
- while (gate.waiterHead < gate.waiters.length) {
739
- const waiter = gate.waiters[gate.waiterHead];
740
- gate.waiters[gate.waiterHead] = undefined;
741
- gate.waiterHead += 1;
742
- if (gate.waiterHead >= gate.waiters.length) {
743
- gate.waiters.length = 0;
744
- gate.waiterHead = 0;
745
- }
746
- if (waiter === undefined) continue;
747
- if (waiter.abort !== undefined) waiter.signal?.removeEventListener('abort', waiter.abort);
748
- waiter.resolve(() => this.releaseInstanceActivation(gate));
749
- return;
750
- }
751
- gate.waiters.length = 0;
752
- gate.waiterHead = 0;
753
- gate.active -= 1;
754
- this.options.onInstanceActivationConcurrencyChanged?.(gate.meshName);
755
- }
756
-
757
696
  isInstanceMaterialized(meshName: string, instanceType: string, spotId: RoutingId): boolean {
758
697
  const activation = this.activations.resolve(meshName, spotId);
759
698
  return (
@@ -767,12 +706,17 @@ export class DefaultZLinkSpotManager {
767
706
  return (this.pendingInstanceMaterializationsByPrefix.get(prefix)?.size ?? 0) > 0;
768
707
  }
769
708
 
770
- isInstanceClosing(meshName: string, spotId: RoutingId): boolean {
709
+ isSpotClosing(meshName: string, spotId: RoutingId): boolean {
771
710
  const key = `${meshName}\0${String(spotId)}`;
772
- return (
773
- this.pendingInstanceCloses.has(key) ||
774
- this.activations.closingOperation(meshName, spotId) !== undefined
775
- );
711
+ return this.closeOperations.get(key)?.committed === true;
712
+ }
713
+
714
+ pendingSpotCloseDecision(meshName: string, spotId: RoutingId): Promise<void> | undefined {
715
+ return this.closeOperations.get(`${meshName}\0${String(spotId)}`)?.authorityDecision;
716
+ }
717
+
718
+ isInstanceClosing(meshName: string, spotId: RoutingId): boolean {
719
+ return this.closeOperations.has(`${meshName}\0${String(spotId)}`);
776
720
  }
777
721
 
778
722
  async discardInstance(meshName: string, spotId: RoutingId): Promise<void> {
@@ -835,7 +779,7 @@ export class DefaultZLinkSpotManager {
835
779
  }
836
780
  this.pendingInstanceTerminals.delete(key);
837
781
  this.pendingInstanceTerminalGenerations.delete(key);
838
- const pendingClose = this.pendingInstanceCloses.get(key);
782
+ const pendingClose = this.closeOperations.get(key)?.ready;
839
783
  if (pendingClose !== undefined) {
840
784
  this.deferInstanceAuthorityRelease(key, objectGeneration);
841
785
  await pendingClose;
@@ -950,20 +894,6 @@ export class DefaultZLinkSpotManager {
950
894
  ) {
951
895
  continue;
952
896
  }
953
- let durableClosing: { restoreReady(): Promise<void> } | undefined;
954
- try {
955
- durableClosing = await (this.options.beginInstanceIdleClosingAuthority?.(
956
- activation.meshName,
957
- activation.spotId
958
- ) ?? Promise.resolve({ restoreReady: async () => undefined }));
959
- } catch (error) {
960
- activation.abortIdleEviction();
961
- throw error;
962
- }
963
- if (durableClosing === undefined) {
964
- activation.abortIdleEviction();
965
- continue;
966
- }
967
897
  const close = this.closeWithReason(
968
898
  activation.meshName,
969
899
  activation.spotId,
@@ -973,15 +903,10 @@ export class DefaultZLinkSpotManager {
973
903
  const run = async () => {
974
904
  try {
975
905
  const closed = await close;
976
- if (!closed) {
977
- await durableClosing.restoreReady();
978
- activation.abortIdleEviction();
979
- }
906
+ if (!closed) activation.abortIdleEviction();
980
907
  } catch (error) {
981
- if (error instanceof ZLinkSpotCloseOccupiedError) {
982
- await durableClosing.restoreReady();
983
- }
984
- activation.abortIdleEviction();
908
+ if (!this.isSpotClosing(activation.meshName, activation.spotId))
909
+ activation.abortIdleEviction();
985
910
  throw error;
986
911
  }
987
912
  };
@@ -1087,6 +1012,7 @@ export class DefaultZLinkSpotManager {
1087
1012
  throwIfAborted(args.signal);
1088
1013
  const operation = this.activations.getOrBegin(meshName, spotType, spotId, async () => {
1089
1014
  this.options.admission?.requireRequest('SPOT create', meshName);
1015
+ const release = await this.options.activationAdmission?.acquire(meshName, args.signal);
1090
1016
  const ownedRequest =
1091
1017
  args.request === undefined
1092
1018
  ? RuntimeMessage.from(Buffer.alloc(0))
@@ -1102,6 +1028,7 @@ export class DefaultZLinkSpotManager {
1102
1028
  );
1103
1029
  } finally {
1104
1030
  ownedRequest.close();
1031
+ release?.();
1105
1032
  }
1106
1033
  });
1107
1034
  return await awaitWithAbort(operation.ready, args.signal);
@@ -1143,120 +1070,182 @@ export class DefaultZLinkSpotManager {
1143
1070
  return await this.closeWithReason(meshName, spotId, signal, ZLinkSpotCloseReason.ExplicitClose);
1144
1071
  }
1145
1072
 
1073
+ async closeUserWithAuthority(
1074
+ meshName: string,
1075
+ spotId: RoutingId,
1076
+ beginAuthority: (onCommitted: () => void) => Promise<{ release(): Promise<void> }>,
1077
+ signal?: AbortSignal
1078
+ ): Promise<boolean> {
1079
+ return await this.closeWithReason(
1080
+ meshName,
1081
+ spotId,
1082
+ signal,
1083
+ ZLinkSpotCloseReason.ExplicitClose,
1084
+ undefined,
1085
+ beginAuthority
1086
+ );
1087
+ }
1088
+
1089
+ private closeFromContext(
1090
+ activation: ZLinkSpotActivation,
1091
+ objectGeneration: bigint,
1092
+ signal?: AbortSignal
1093
+ ): Promise<boolean> {
1094
+ const { meshName, spotId } = activation;
1095
+ const operation = this.closeOperations.get(`${meshName}\0${String(spotId)}`);
1096
+ const current = operation?.activation ?? this.activations.resolve(meshName, spotId);
1097
+ if (current === undefined) {
1098
+ return Promise.resolve(false);
1099
+ }
1100
+ if (current !== activation || current.objectGeneration !== objectGeneration) {
1101
+ return Promise.reject(
1102
+ createInternalFrameworkException(
1103
+ ZLinkFrameworkInternalErrorKind.InvalidOperation,
1104
+ `Spot '${String(spotId)}' generation is stale.`
1105
+ )
1106
+ );
1107
+ }
1108
+ const beginUserAuthority =
1109
+ activation.domain.kind === 'user' && this.options.beginUserClosingAuthority !== undefined
1110
+ ? (onCommitted: () => void) =>
1111
+ this.options.beginUserClosingAuthority!(meshName, spotId, objectGeneration, onCommitted)
1112
+ : undefined;
1113
+ return this.closeWithReason(
1114
+ meshName,
1115
+ spotId,
1116
+ signal,
1117
+ ZLinkSpotCloseReason.ExplicitClose,
1118
+ undefined,
1119
+ beginUserAuthority
1120
+ );
1121
+ }
1122
+
1146
1123
  private async closeWithReason(
1147
1124
  meshName: string,
1148
1125
  spotId: RoutingId,
1149
1126
  signal?: AbortSignal,
1150
1127
  reason = ZLinkSpotCloseReason.ExplicitClose,
1151
- deadline?: Date
1128
+ deadline?: Date,
1129
+ beginUserAuthority?: (
1130
+ onCommitted: () => void
1131
+ ) => Promise<{ release(): Promise<void> } | undefined>
1152
1132
  ): Promise<boolean> {
1153
1133
  requireMeshName(meshName);
1154
1134
  const key = `${meshName}\0${String(spotId)}`;
1155
- const closing = this.activations.closingOperation(meshName, spotId);
1156
- if (closing !== undefined) {
1157
- return await closing.ready;
1158
- }
1159
- const pendingClose = this.pendingInstanceCloses.get(key);
1160
- if (pendingClose !== undefined) {
1161
- const pendingActivation = this.activations.resolve(meshName, spotId);
1162
- if (pendingActivation?.serial.isCurrentTurn === true) return true;
1163
- return await pendingClose;
1164
- }
1165
- const activation = this.activations.resolve(meshName, spotId);
1166
- if (activation === undefined) {
1167
- return false;
1135
+ let operation = this.closeOperations.get(key);
1136
+ if (operation === undefined) {
1137
+ const activation = this.activations.resolve(meshName, spotId);
1138
+ if (activation === undefined) return false;
1139
+ if (
1140
+ activation.domain.kind === 'user' &&
1141
+ reason === ZLinkSpotCloseReason.ExplicitClose &&
1142
+ beginUserAuthority === undefined
1143
+ ) {
1144
+ throw createInternalFrameworkException(
1145
+ ZLinkFrameworkInternalErrorKind.InvalidOperation,
1146
+ `User Spot '${String(spotId)}' Close requires its authority owner.`
1147
+ );
1148
+ }
1149
+ const beginAuthority =
1150
+ beginUserAuthority ??
1151
+ (activation.domain.kind === 'instance' &&
1152
+ reason === ZLinkSpotCloseReason.ExplicitClose &&
1153
+ this.options.beginInstanceClosingAuthority !== undefined
1154
+ ? (onCommitted) =>
1155
+ this.options.beginInstanceClosingAuthority!(meshName, spotId, onCommitted)
1156
+ : activation.domain.kind === 'instance' &&
1157
+ reason === ZLinkSpotCloseReason.IdleEvicted &&
1158
+ this.options.beginInstanceIdleClosingAuthority !== undefined
1159
+ ? (onCommitted) =>
1160
+ this.options.beginInstanceIdleClosingAuthority!(meshName, spotId, onCommitted)
1161
+ : async (onCommitted) => {
1162
+ onCommitted();
1163
+ return { release: async () => undefined };
1164
+ });
1165
+ operation = {
1166
+ activation,
1167
+ reason,
1168
+ beginAuthority,
1169
+ committed: false
1170
+ };
1171
+ this.closeOperations.set(key, operation);
1168
1172
  }
1169
- const currentTurn = activation.serial.isCurrentTurn;
1170
- const isInstance = activation.domain.kind === 'instance';
1171
- const beginClose = () => {
1172
- const seal = activation.sealExecution();
1173
- const operation = this.activations.startClose(
1174
- meshName,
1175
- spotId,
1176
- (target) => this.activationLifecycle.closeAfterSeal(target, seal, signal, reason, deadline),
1177
- (target) => this.activationLifecycle.resourcesReleased(target),
1178
- reason
1173
+ if (operation.ready === undefined) {
1174
+ const active = operation;
1175
+ const run = active.activation.serial.executeLifecycleOperation(() =>
1176
+ this.runCloseOperation(meshName, spotId, active, signal, deadline)
1179
1177
  );
1180
- if (operation === undefined) {
1181
- activation.abortExecutionSeal(seal);
1182
- }
1183
- return operation;
1184
- };
1185
-
1186
- if (isInstance) {
1187
- const beginAuthorityClose =
1188
- reason === ZLinkSpotCloseReason.ExplicitClose
1189
- ? this.options.beginInstanceClosingAuthority
1190
- : undefined;
1191
- // Register the close gate before waiting for the current application
1192
- // and activation-authority terminal completions. A close requested from
1193
- // a handler cannot publish Closing until that handler's durable inbox
1194
- // completion has removed the recovery fence.
1195
- const trackedClosePromise = (async () => {
1196
- const waitForApplication = this.options.instanceSpotApplicationQuiescenceProvider?.(
1197
- meshName,
1198
- spotId,
1199
- signal
1200
- );
1201
- if (waitForApplication !== undefined) {
1202
- await waitForApplication;
1203
- }
1204
- const closingAuthority =
1205
- beginAuthorityClose === undefined
1206
- ? undefined
1207
- : await beginAuthorityClose(meshName, spotId);
1208
- if (beginAuthorityClose !== undefined && closingAuthority === undefined) {
1209
- return false;
1210
- }
1211
- const operation = beginClose();
1212
- if (operation === undefined) {
1213
- await closingAuthority?.restoreReady();
1214
- return false;
1215
- }
1216
- const closed = await operation.ready;
1217
- if (!closed) await closingAuthority?.restoreReady();
1218
- return closed;
1219
- })().finally(() => {
1220
- if (this.pendingInstanceCloses.get(key) === trackedClosePromise) {
1221
- this.pendingInstanceCloses.delete(key);
1178
+ active.ready = run.finally(() => {
1179
+ active.ready = undefined;
1180
+ if (this.closeOperations.get(key) === active) {
1181
+ this.closeOperations.delete(key);
1222
1182
  }
1223
1183
  });
1224
- this.pendingInstanceCloses.set(key, trackedClosePromise);
1225
- if (currentTurn) {
1226
- // The current turn must return before its terminal completion can
1227
- // release the activation recovery fence and allow the durable close
1228
- // CAS. The registered close gate prevents a second admission while
1229
- // that detached close finishes.
1230
- this.options.detachedTaskRunner?.runDetached(
1231
- `instance spot close ${String(spotId)}`,
1232
- async () => {
1233
- await trackedClosePromise;
1234
- }
1235
- );
1236
- if (this.options.detachedTaskRunner === undefined) {
1237
- void trackedClosePromise.catch(() => undefined);
1238
- }
1239
- return true;
1240
- }
1241
- return await trackedClosePromise;
1242
1184
  }
1185
+ return await operation.ready!;
1186
+ }
1243
1187
 
1244
- const operation = beginClose();
1245
- if (operation === undefined) {
1246
- return false;
1188
+ private async runCloseOperation(
1189
+ meshName: string,
1190
+ spotId: RoutingId,
1191
+ operation: ZLinkTargetSpotCloseOperation,
1192
+ signal?: AbortSignal,
1193
+ deadline?: Date
1194
+ ): Promise<boolean> {
1195
+ const began = await this.beginCloseAuthority(meshName, spotId, operation, signal);
1196
+ if (!began) return false;
1197
+ const seal = operation.activation.sealExecution(true);
1198
+ await this.activationLifecycle.sealForClose(operation.activation, seal);
1199
+ const closingFailure = await this.activationLifecycle.cleanupClosedActivation(
1200
+ operation.activation,
1201
+ operation.reason,
1202
+ deadline
1203
+ );
1204
+ await operation.authority?.release();
1205
+ // The incarnation has no further lifecycle item once its authority is
1206
+ // released, so its lifecycle lane admission closes with this Close.
1207
+ operation.activation.serial.closeAdmission();
1208
+ this.activations.finishClose(meshName, spotId);
1209
+ this.closeOperations.delete(`${meshName}\0${String(spotId)}`);
1210
+ // An OnClosing failure never changes a Close result; host shutdown alone
1211
+ // reports it as its teardown outcome after cleanup has finished.
1212
+ if (closingFailure !== undefined && operation.reason === ZLinkSpotCloseReason.HostShutdown) {
1213
+ throw closingFailure;
1247
1214
  }
1248
- if (currentTurn) {
1249
- if (operation.started) {
1250
- this.options.detachedTaskRunner?.runDetached(`spot close ${String(spotId)}`, async () => {
1251
- await operation.ready;
1252
- });
1253
- if (this.options.detachedTaskRunner === undefined) {
1254
- void operation.ready.catch(() => undefined);
1255
- }
1256
- }
1257
- return true;
1215
+ return true;
1216
+ }
1217
+
1218
+ private async beginCloseAuthority(
1219
+ meshName: string,
1220
+ spotId: RoutingId,
1221
+ operation: ZLinkTargetSpotCloseOperation,
1222
+ signal?: AbortSignal
1223
+ ): Promise<boolean> {
1224
+ await this.options.instanceSpotApplicationQuiescenceProvider?.(meshName, spotId, signal);
1225
+ if (!operation.activation.canClose(operation.reason)) return false;
1226
+ if (operation.activation.executionBarrier.isSealed) {
1227
+ throw createInternalFrameworkException(
1228
+ ZLinkFrameworkInternalErrorKind.SpotMoving,
1229
+ `Spot '${String(spotId)}' is sealed for relocation.`,
1230
+ true
1231
+ );
1232
+ }
1233
+ const authorityPromise = Promise.resolve().then(() =>
1234
+ operation.beginAuthority(() => {
1235
+ operation.committed = true;
1236
+ })
1237
+ );
1238
+ operation.authorityDecision = authorityPromise.then(
1239
+ () => undefined,
1240
+ () => undefined
1241
+ );
1242
+ const authority = await authorityPromise;
1243
+ if (authority === undefined) return false;
1244
+ operation.authority = authority;
1245
+ if (!this.isSpotClosing(meshName, spotId)) {
1246
+ throw new Error(`Spot '${String(spotId)}' Closing authority did not confirm its commit.`);
1258
1247
  }
1259
- return await operation.ready;
1248
+ return true;
1260
1249
  }
1261
1250
 
1262
1251
  async executeOnSpot<TSpot extends ZLinkSpot, TResult>(
@@ -1286,10 +1275,6 @@ export class DefaultZLinkSpotManager {
1286
1275
  return this.activations.resolveUnique(spotId) !== undefined;
1287
1276
  }
1288
1277
 
1289
- canCloseUserSpot(meshName: string, spotId: RoutingId): boolean {
1290
- return this.activations.canClose(meshName, spotId);
1291
- }
1292
-
1293
1278
  beginUserSpotPublication(meshName: string, spotId: RoutingId): void {
1294
1279
  this.activations.stage(meshName, spotId);
1295
1280
  }
@@ -1562,14 +1547,7 @@ export class DefaultZLinkSpotManager {
1562
1547
  record.reply(encodeChannelReplyParts(envelope.header, response, codecs))
1563
1548
  );
1564
1549
  } catch (error) {
1565
- requireMeshSpotReply(
1566
- record.reply(
1567
- encodeChannelErrorReplyParts(
1568
- envelope.header,
1569
- error instanceof Error ? error.message : String(error)
1570
- )
1571
- )
1572
- );
1550
+ requireMeshSpotReply(record.reply(encodeChannelErrorReplyParts(envelope.header, error)));
1573
1551
  }
1574
1552
  }
1575
1553
 
@@ -1761,14 +1739,7 @@ export class DefaultZLinkSpotManager {
1761
1739
  action: ZLinkDispatchErrorAction.ReplyError,
1762
1740
  error
1763
1741
  });
1764
- requireMeshSpotReply(
1765
- record.reply(
1766
- encodeChannelErrorReplyParts(
1767
- envelope.header,
1768
- error instanceof Error ? error.message : String(error)
1769
- )
1770
- )
1771
- );
1742
+ requireMeshSpotReply(record.reply(encodeChannelErrorReplyParts(envelope.header, error)));
1772
1743
  }
1773
1744
  });
1774
1745
  }
@@ -1932,6 +1903,40 @@ export class DefaultZLinkSpotManager {
1932
1903
  meshName: string,
1933
1904
  owner: ReadyRecord,
1934
1905
  record: ReceiveRecord
1906
+ ): Promise<void> {
1907
+ const spotId = owner.spotId as unknown as RoutingId | null;
1908
+ const activation = spotId === null ? undefined : this.activations.resolve(meshName, spotId);
1909
+ if (activation?.domain.kind === 'user') {
1910
+ await activation.serial.executeLifecycleOperation(async () => {
1911
+ if (
1912
+ this.isSpotClosing(meshName, activation.spotId) ||
1913
+ this.activations.resolve(meshName, activation.spotId) !== activation
1914
+ ) {
1915
+ const rejection = createInternalFrameworkException(
1916
+ ZLinkFrameworkInternalErrorKind.RequestRejected,
1917
+ `User Spot '${String(spotId)}' is closing.`
1918
+ );
1919
+ if (record.replyFailure !== undefined) {
1920
+ const terminal = internalFrameworkWireReply(rejection);
1921
+ requireMeshSpotReply(
1922
+ record.replyFailure(terminal.terminalResult, terminal.failureCode)
1923
+ );
1924
+ } else {
1925
+ requireMeshSpotReply(record.replyActorJoin(1, []));
1926
+ }
1927
+ return;
1928
+ }
1929
+ await this.dispatchMeshActorJoinCore(meshName, owner, record);
1930
+ });
1931
+ return;
1932
+ }
1933
+ await this.dispatchMeshActorJoinCore(meshName, owner, record);
1934
+ }
1935
+
1936
+ private async dispatchMeshActorJoinCore(
1937
+ meshName: string,
1938
+ owner: ReadyRecord,
1939
+ record: ReceiveRecord
1935
1940
  ): Promise<void> {
1936
1941
  const spotId = owner.spotId as unknown as RoutingId | null;
1937
1942
  const control = record.kindData;
@@ -2027,7 +2032,7 @@ export class DefaultZLinkSpotManager {
2027
2032
  let ownedCallbackRequest: Message | undefined;
2028
2033
  let materialized = false;
2029
2034
  let reply: Message | undefined;
2030
- let deferredJoinRoot: ZLinkDeferredJoinAcceptedRoot | undefined;
2035
+ let deferredJoinCompletion: ZLinkDeferredJoinCompletion | undefined;
2031
2036
  let preparedTransferState: Message | undefined;
2032
2037
  let admissionOutcome: ZLinkFormalRemoteActorAdmissionResult | undefined;
2033
2038
  let accepted = false;
@@ -2176,8 +2181,8 @@ export class DefaultZLinkSpotManager {
2176
2181
  transferRequest.completionOperationId !== undefined &&
2177
2182
  this.options.actorTransferRuntime !== undefined
2178
2183
  ) {
2179
- deferredJoinRoot =
2180
- await this.options.actorTransferRuntime.prepareDeferredJoinAccepted(
2184
+ deferredJoinCompletion =
2185
+ this.options.actorTransferRuntime.prepareDeferredJoinAccepted(
2181
2186
  actorId,
2182
2187
  transferRequest.completionOperationId,
2183
2188
  actorRef,
@@ -2187,7 +2192,7 @@ export class DefaultZLinkSpotManager {
2187
2192
  admissionOutcome = {
2188
2193
  accepted: this.options.dispatchEntryActorJoin !== undefined,
2189
2194
  actorRef,
2190
- ...(deferredJoinRoot === undefined ? {} : { deferredJoinRoot })
2195
+ ...(deferredJoinCompletion === undefined ? {} : { deferredJoinCompletion })
2191
2196
  };
2192
2197
  } else if (activation === undefined) {
2193
2198
  admissionOutcome = { accepted: false, actorRef };
@@ -2220,8 +2225,8 @@ export class DefaultZLinkSpotManager {
2220
2225
  transferRequest.completionOperationId !== undefined &&
2221
2226
  this.options.actorTransferRuntime !== undefined
2222
2227
  ) {
2223
- deferredJoinRoot =
2224
- await this.options.actorTransferRuntime.prepareDeferredJoinAccepted(
2228
+ deferredJoinCompletion =
2229
+ this.options.actorTransferRuntime.prepareDeferredJoinAccepted(
2225
2230
  actorId,
2226
2231
  transferRequest.completionOperationId,
2227
2232
  actorRef,
@@ -2240,7 +2245,7 @@ export class DefaultZLinkSpotManager {
2240
2245
  admissionOutcome = {
2241
2246
  accepted: response.accepted,
2242
2247
  actorRef,
2243
- ...(deferredJoinRoot === undefined ? {} : { deferredJoinRoot }),
2248
+ ...(deferredJoinCompletion === undefined ? {} : { deferredJoinCompletion }),
2244
2249
  ...(encodedReply === undefined
2245
2250
  ? {}
2246
2251
  : {
@@ -2277,7 +2282,7 @@ export class DefaultZLinkSpotManager {
2277
2282
  if ('error' in admissionOutcome) {
2278
2283
  throw admissionOutcome.error;
2279
2284
  }
2280
- deferredJoinRoot = admissionOutcome.deferredJoinRoot;
2285
+ deferredJoinCompletion = admissionOutcome.deferredJoinCompletion;
2281
2286
  }
2282
2287
  if (
2283
2288
  actor === undefined &&
@@ -2361,7 +2366,7 @@ export class DefaultZLinkSpotManager {
2361
2366
  transferRequest?.completionOperationId !== undefined &&
2362
2367
  this.options.actorTransferRuntime !== undefined
2363
2368
  ) {
2364
- deferredJoinRoot = await this.options.actorTransferRuntime.prepareDeferredJoinAccepted(
2369
+ deferredJoinCompletion = this.options.actorTransferRuntime.prepareDeferredJoinAccepted(
2365
2370
  actorId,
2366
2371
  transferRequest.completionOperationId,
2367
2372
  actorRef,
@@ -2433,7 +2438,7 @@ export class DefaultZLinkSpotManager {
2433
2438
  // A lightweight Core actor-join notification can create the Actor
2434
2439
  // before the formal transfer request reaches this branch. Preserve
2435
2440
  // the full relocation fence in that race so a later Session bind
2436
- // refresh cannot erase the seal or accepted journal.
2441
+ // refresh cannot erase the seal.
2437
2442
  this.options.actorTransferRuntime.rememberRoutedActorTransferTarget(
2438
2443
  actorId,
2439
2444
  transferRequest.remoteBoundSessionTarget
@@ -2451,7 +2456,7 @@ export class DefaultZLinkSpotManager {
2451
2456
  spotId,
2452
2457
  transferId: transferRequest.transferId,
2453
2458
  handoffBacklog: transferRequest.handoffBacklog,
2454
- deferredJoinRoot
2459
+ deferredJoinCompletion
2455
2460
  });
2456
2461
  if (isRemoteCommit && admissionRecord !== undefined) {
2457
2462
  this.formalRemoteActorAdmissions.markCommitted(transferRequest.transferId, actor);
@@ -2558,9 +2563,6 @@ export class DefaultZLinkSpotManager {
2558
2563
  if (control.canonicalActorJoin !== undefined) {
2559
2564
  this.formalRemoteActorAdmissions.fail(control.canonicalActorJoin.handoffId, error);
2560
2565
  }
2561
- if (deferredJoinRoot !== undefined && targetCommitPublished !== true) {
2562
- await this.options.actorTransferRuntime?.discardDeferredJoinAccepted(deferredJoinRoot);
2563
- }
2564
2566
  if (targetCommitPublished !== true) {
2565
2567
  this.formalRemoteTransfers.delete(actorId);
2566
2568
  }
@@ -2580,6 +2582,43 @@ export class DefaultZLinkSpotManager {
2580
2582
  meshName: string,
2581
2583
  owner: ReadyRecord,
2582
2584
  record: ReceiveRecord
2585
+ ): Promise<void> {
2586
+ const spotId = owner.spotId as unknown as RoutingId | null;
2587
+ const activation = spotId === null ? undefined : this.activations.resolve(meshName, spotId);
2588
+ if (activation?.domain.kind === 'user') {
2589
+ const transfer = { terminal: undefined as Promise<void> | undefined };
2590
+ const operation = activation.serial.executeLifecycleOperation(async () => {
2591
+ await this.dispatchMeshSpotControlCore(meshName, owner, record, (terminal) => {
2592
+ transfer.terminal = terminal;
2593
+ });
2594
+ await transfer.terminal;
2595
+ });
2596
+ const control = record.kindData;
2597
+ const actorId = control?.kind === 'actorControl' ? control.currentActor?.actorId : undefined;
2598
+ if (
2599
+ control?.kind === 'actorControl' &&
2600
+ control.lifecycleKind === ActorLifecycleKind.Joined &&
2601
+ actorId !== undefined &&
2602
+ this.formalRemoteTransfers.get(actorId) !== undefined
2603
+ ) {
2604
+ this.options.detachedTaskRunner?.runDetached(
2605
+ `actor transfer target commit ${actorId}`,
2606
+ async () => await operation
2607
+ );
2608
+ if (this.options.detachedTaskRunner === undefined) void operation.catch(() => undefined);
2609
+ return;
2610
+ }
2611
+ await operation;
2612
+ return;
2613
+ }
2614
+ await this.dispatchMeshSpotControlCore(meshName, owner, record);
2615
+ }
2616
+
2617
+ private async dispatchMeshSpotControlCore(
2618
+ meshName: string,
2619
+ owner: ReadyRecord,
2620
+ record: ReceiveRecord,
2621
+ onDetachedTerminal?: (terminal: Promise<void>) => void
2583
2622
  ): Promise<void> {
2584
2623
  const spotId = owner.spotId as unknown as RoutingId | null;
2585
2624
  const control = record.kindData;
@@ -2664,8 +2703,8 @@ export class DefaultZLinkSpotManager {
2664
2703
  await activation.spot.onJoinedActor(actor);
2665
2704
  };
2666
2705
  const completePublicJoin = async (): Promise<void> => {
2667
- const deferredJoinRoot = pendingTransfer?.deferredJoinRoot;
2668
- if (deferredJoinRoot !== undefined) {
2706
+ const deferredJoinCompletion = pendingTransfer?.deferredJoinCompletion;
2707
+ if (deferredJoinCompletion !== undefined) {
2669
2708
  const currentRef =
2670
2709
  this.options.actorTransferRuntime === undefined
2671
2710
  ? null
@@ -2675,8 +2714,8 @@ export class DefaultZLinkSpotManager {
2675
2714
  `Actor '${actor.context.actorId}' has no target ref for deferred Join completion.`
2676
2715
  );
2677
2716
  }
2678
- await this.options.actorTransferRuntime?.commitAndDeliverDeferredJoinAccepted(
2679
- deferredJoinRoot,
2717
+ await this.options.actorTransferRuntime?.deliverDeferredJoinAccepted(
2718
+ deferredJoinCompletion,
2680
2719
  actor,
2681
2720
  currentRef,
2682
2721
  (operation) =>
@@ -2760,12 +2799,17 @@ export class DefaultZLinkSpotManager {
2760
2799
  // them only after the lifecycle turn above has been released.
2761
2800
  await completeTargetReady();
2762
2801
  };
2763
- this.options.detachedTaskRunner?.runDetached(
2764
- `actor transfer target commit ${actor.context.actorId}`,
2765
- resume
2766
- );
2767
- if (this.options.detachedTaskRunner === undefined) {
2768
- void resume().catch(() => undefined);
2802
+ const terminal = resume();
2803
+ if (onDetachedTerminal !== undefined) {
2804
+ onDetachedTerminal(terminal);
2805
+ } else {
2806
+ this.options.detachedTaskRunner?.runDetached(
2807
+ `actor transfer target commit ${actor.context.actorId}`,
2808
+ async () => await terminal
2809
+ );
2810
+ if (this.options.detachedTaskRunner === undefined) {
2811
+ void terminal.catch(() => undefined);
2812
+ }
2769
2813
  }
2770
2814
  return;
2771
2815
  }
@@ -2842,7 +2886,10 @@ export class DefaultZLinkSpotManager {
2842
2886
  await activation.serial.execute(() => activation.spot.onJoinedActor(actor));
2843
2887
  }
2844
2888
  await submitSourceLeave(admission.admission.actorRef.nodeRid);
2845
- if (outcome.deferredJoinRoot !== undefined && this.options.actorTransferRuntime !== undefined) {
2889
+ if (
2890
+ outcome.deferredJoinCompletion !== undefined &&
2891
+ this.options.actorTransferRuntime !== undefined
2892
+ ) {
2846
2893
  const submitMailbox = targetsEntry
2847
2894
  ? <T>(operation: () => Promise<T>): Promise<T> => operation()
2848
2895
  : <T>(operation: () => Promise<T>): Promise<T> => {
@@ -2852,8 +2899,8 @@ export class DefaultZLinkSpotManager {
2852
2899
  }
2853
2900
  return activation.executeActor(actor.context.actorId, operation);
2854
2901
  };
2855
- await this.options.actorTransferRuntime.commitAndDeliverDeferredJoinAccepted(
2856
- outcome.deferredJoinRoot,
2902
+ await this.options.actorTransferRuntime.deliverDeferredJoinAccepted(
2903
+ outcome.deferredJoinCompletion,
2857
2904
  actor,
2858
2905
  actorRef,
2859
2906
  submitMailbox,
@@ -2866,9 +2913,8 @@ export class DefaultZLinkSpotManager {
2866
2913
 
2867
2914
  async restoreCanonicalActorJoinRecovery(
2868
2915
  recovery: CanonicalActorJoinRecovery,
2869
- signal?: AbortSignal,
2870
- canonicalInventoryDigest?: string
2871
- ): Promise<ZLinkDeferredJoinAcceptedRoot | undefined> {
2916
+ signal?: AbortSignal
2917
+ ): Promise<void> {
2872
2918
  throwIfAborted(signal);
2873
2919
  const admission = this.formalRemoteActorAdmissions.get(recovery.request.handoffId);
2874
2920
  if (admission === undefined) {
@@ -2930,24 +2976,24 @@ export class DefaultZLinkSpotManager {
2930
2976
  }
2931
2977
  // Command 28 carries a present reply in one outer framework-multipart
2932
2978
  // part. The accepted target admission still owns the inner typed content
2933
- // type, which the durable completion must retain for application decode.
2979
+ // type, which the process-local completion keeps for application decode.
2934
2980
  const admittedContentType =
2935
2981
  admittedReply.byteLength === 0 ? recovery.replyContentType : outcome.replyContentType;
2936
2982
  const transfer = this.options.actorTransferRuntime;
2937
2983
  if (transfer === undefined) {
2938
2984
  throw new Error('Canonical Actor Join recovery requires the Actor transfer runtime.');
2939
2985
  }
2940
- const deferred = await transfer.prepareDeferredJoinAccepted(
2986
+ const deferred = transfer.prepareDeferredJoinAccepted(
2941
2987
  recovery.request.actorId,
2942
2988
  recovery.operationId,
2943
2989
  admitted.actorRef,
2944
2990
  recovery.reply,
2945
- admittedContentType,
2946
- signal,
2947
- canonicalInventoryDigest
2991
+ admittedContentType
2992
+ );
2993
+ this.formalRemoteActorAdmissions.attachDeferredJoinCompletion(
2994
+ recovery.request.handoffId,
2995
+ deferred
2948
2996
  );
2949
- this.formalRemoteActorAdmissions.attachDeferredJoinRoot(recovery.request.handoffId, deferred);
2950
- return deferred;
2951
2997
  }
2952
2998
 
2953
2999
  private encodeMeshActorReply(