@zlink-systems/framework 0.31.0 → 0.32.1

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 (181) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Actors/ZLinkActor.d.ts +2 -0
  3. package/dist/contracts/Actors/ZLinkSessionBindingIdentity.d.ts +6 -0
  4. package/dist/contracts/Actors/ZLinkSessionBindingIdentity.js +2 -0
  5. package/dist/contracts/Actors/index.d.ts +1 -0
  6. package/dist/contracts/Actors/index.js +1 -0
  7. package/dist/contracts/Common/payload-slab.d.ts +4 -0
  8. package/dist/contracts/Common/payload-slab.js +30 -0
  9. package/dist/contracts/Handlers/Contexts.d.ts +1 -0
  10. package/dist/internal.d.ts +9 -0
  11. package/dist/internal.js +23 -1
  12. package/dist/runtime/actors/actor-client.d.ts +1 -1
  13. package/dist/runtime/actors/actor-client.js +32 -23
  14. package/dist/runtime/actors/actor-context.js +1 -1
  15. package/dist/runtime/actors/actor-creation.js +1 -1
  16. package/dist/runtime/actors/actor-mailbox.d.ts +1 -1
  17. package/dist/runtime/actors/actor-runtime-state.d.ts +3 -2
  18. package/dist/runtime/actors/actor-runtime-state.js +24 -8
  19. package/dist/runtime/actors/index.d.ts +3 -3
  20. package/dist/runtime/actors/index.js +19 -32
  21. package/dist/runtime/actors/spot-actor-dispatch.d.ts +4 -2
  22. package/dist/runtime/actors/spot-actor-dispatch.js +2 -1
  23. package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +1 -0
  24. package/dist/runtime/application-jobs/application-ingress-record-owner.js +3 -0
  25. package/dist/runtime/backend/contracts/index.d.ts +2 -0
  26. package/dist/runtime/backend/mesh-completion-table.d.ts +1 -0
  27. package/dist/runtime/backend/mesh-completion-table.js +1 -0
  28. package/dist/runtime/backend/node/node-mesh-backend-adapter.js +5 -1
  29. package/dist/runtime/backend/node/node-raw-binding-port.d.ts +7 -2
  30. package/dist/runtime/backend/node/node-raw-binding-port.js +17 -11
  31. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +2 -2
  32. package/dist/runtime/backend/node/node-raw-mesh-backend.js +15 -8
  33. package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +7 -6
  34. package/dist/runtime/channels/channel-dispatch-pipeline.js +1 -0
  35. package/dist/runtime/channels/channel-dispatchers.d.ts +7 -3
  36. package/dist/runtime/channels/channel-dispatchers.js +27 -19
  37. package/dist/runtime/channels/channel-envelope.js +543 -23
  38. package/dist/runtime/channels/channel-receive-loops.d.ts +9 -6
  39. package/dist/runtime/channels/channel-receive-loops.js +59 -18
  40. package/dist/runtime/channels/channel-runtime-lifecycle.d.ts +22 -3
  41. package/dist/runtime/channels/channel-socket-registry.d.ts +22 -4
  42. package/dist/runtime/diagnostics/runtime-metrics.d.ts +1 -1
  43. package/dist/runtime/diagnostics/runtime-metrics.js +2 -1
  44. package/dist/runtime/execution/serial-execution-queue.d.ts +26 -13
  45. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +27 -11
  46. package/dist/runtime/foundation/raw-service-mesh-runtime.js +29 -25
  47. package/dist/runtime/foundation/service-liveness-registry.d.ts +1 -1
  48. package/dist/runtime/foundation/service-mailbox.d.ts +3 -1
  49. package/dist/runtime/foundation/service-mailbox.js +4 -1
  50. package/dist/runtime/foundation/service-runtime-contracts.d.ts +3 -1
  51. package/dist/runtime/foundation/service-stateful-registry.d.ts +2 -0
  52. package/dist/runtime/foundation/service-stateful-registry.js +28 -12
  53. package/dist/runtime/foundation/service-stateful-runtime.d.ts +51 -19
  54. package/dist/runtime/foundation/service-stateful-runtime.js +54 -5
  55. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +3 -0
  56. package/dist/runtime/foundation/service-stateful-wire-codec.js +15 -0
  57. package/dist/runtime/foundation/service-wire-constants.generated.d.ts +1 -0
  58. package/dist/runtime/foundation/service-wire-constants.generated.js +12 -0
  59. package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +2 -0
  60. package/dist/runtime/foundation/service-wire-m6a-codec.js +17 -7
  61. package/dist/runtime/host/actor-placement-coordinator.d.ts +1 -1
  62. package/dist/runtime/host/actor-transfer-runtime.d.ts +3 -3
  63. package/dist/runtime/host/application-job-queue.d.ts +1 -1
  64. package/dist/runtime/host/index.d.ts +89 -36
  65. package/dist/runtime/host/index.js +101 -7
  66. package/dist/runtime/host/location-runtime-owner.d.ts +2 -2
  67. package/dist/runtime/host/service-relocation-host-runtime.d.ts +181 -44
  68. package/dist/runtime/host/service-relocation-host-runtime.js +47 -13
  69. package/dist/runtime/host/spot-address-transport.js +50 -42
  70. package/dist/runtime/locations/actor-location-claims.js +9 -10
  71. package/dist/runtime/locations/auto-connect-loop.d.ts +0 -1
  72. package/dist/runtime/locations/auto-connect-loop.js +25 -19
  73. package/dist/runtime/locations/auto-connect-planner.js +2 -5
  74. package/dist/runtime/locations/auto-connect-reconciler.d.ts +1 -0
  75. package/dist/runtime/locations/auto-connect-reconciler.js +35 -23
  76. package/dist/runtime/locations/in-memory-location-store.d.ts +0 -2
  77. package/dist/runtime/locations/in-memory-location-store.js +1 -38
  78. package/dist/runtime/locations/location-store-repository.d.ts +0 -1
  79. package/dist/runtime/locations/location-store-repository.js +13 -115
  80. package/dist/runtime/locations/runtime.d.ts +3 -3
  81. package/dist/runtime/locations/runtime.js +12 -16
  82. package/dist/runtime/messaging/creation-payload-codec.js +2 -2
  83. package/dist/runtime/protocol/service_wire_codec.generated.d.ts +4 -0
  84. package/dist/runtime/protocol/service_wire_codec.generated.js +17 -7
  85. package/dist/runtime/spots/index.d.ts +20 -5
  86. package/dist/runtime/spots/index.js +14 -0
  87. package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +3 -3
  88. package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +1 -0
  89. package/dist/runtime/spots/spot-actor-packet-dispatch.js +13 -7
  90. package/dist/runtime/spots/spot-actor-packet-drain.d.ts +1 -0
  91. package/dist/runtime/spots/spot-actor-packet-drain.js +12 -5
  92. package/dist/runtime/spots/spot-entry-activation.d.ts +2 -1
  93. package/dist/runtime/spots/spot-node-runtime-manager.d.ts +8 -6
  94. package/dist/runtime/spots/spot-node-runtime-manager.js +2 -0
  95. package/dist/runtime/spots/spot-route-packet-dispatch.js +7 -4
  96. package/dist/runtime/spots/spot-routed-spot-packet-dispatch.js +3 -1
  97. package/dist/runtime/spots/spot-serial-executor.d.ts +2 -1
  98. package/dist/runtime/spots/spot-subscription-dispatch.js +3 -1
  99. package/dist/runtime/spots/spot-timer.d.ts +1 -1
  100. package/dist/runtime/streams/actor-session-binding-registry.d.ts +9 -3
  101. package/dist/runtime/streams/actor-session-binding-registry.js +17 -0
  102. package/dist/runtime/streams/index.d.ts +3 -3
  103. package/dist/runtime/streams/index.js +22 -2
  104. package/dist/runtime/streams/managed-stream.js +2 -1
  105. package/dist/runtime/streams/session-calls.d.ts +1 -1
  106. package/dist/runtime/streams/session-calls.js +3 -9
  107. package/dist/runtime/streams/session-context.d.ts +2 -2
  108. package/dist/runtime/streams/session-context.js +2 -2
  109. package/dist/runtime/streams/stream-session-runtime.d.ts +1 -0
  110. package/dist/runtime/streams/stream-session-runtime.js +37 -12
  111. package/package.json +3 -3
  112. package/src/contracts/Actors/ZLinkActor.ts +6 -0
  113. package/src/contracts/Actors/ZLinkSessionBindingIdentity.ts +7 -0
  114. package/src/contracts/Actors/index.ts +1 -0
  115. package/src/contracts/Common/payload-slab.ts +29 -0
  116. package/src/contracts/Handlers/Contexts.ts +1 -0
  117. package/src/internal.ts +17 -0
  118. package/src/runtime/actors/actor-client.ts +48 -23
  119. package/src/runtime/actors/actor-context.ts +1 -1
  120. package/src/runtime/actors/actor-creation.ts +1 -1
  121. package/src/runtime/actors/actor-mailbox.ts +1 -1
  122. package/src/runtime/actors/actor-runtime-state.ts +28 -10
  123. package/src/runtime/actors/index.ts +30 -35
  124. package/src/runtime/actors/spot-actor-dispatch.ts +4 -3
  125. package/src/runtime/application-jobs/application-ingress-record-owner.ts +4 -0
  126. package/src/runtime/backend/contracts/index.ts +2 -0
  127. package/src/runtime/backend/mesh-completion-table.ts +2 -0
  128. package/src/runtime/backend/node/node-mesh-backend-adapter.ts +5 -1
  129. package/src/runtime/backend/node/node-raw-binding-port.ts +20 -7
  130. package/src/runtime/backend/node/node-raw-mesh-backend.ts +18 -10
  131. package/src/runtime/channels/channel-dispatch-pipeline.ts +6 -5
  132. package/src/runtime/channels/channel-dispatchers.ts +39 -19
  133. package/src/runtime/channels/channel-envelope.ts +547 -20
  134. package/src/runtime/channels/channel-receive-loops.ts +93 -36
  135. package/src/runtime/channels/channel-runtime-lifecycle.ts +2 -2
  136. package/src/runtime/channels/channel-socket-registry.ts +3 -3
  137. package/src/runtime/diagnostics/runtime-metrics.ts +3 -1
  138. package/src/runtime/execution/serial-execution-queue.ts +13 -13
  139. package/src/runtime/foundation/raw-service-mesh-runtime.ts +40 -39
  140. package/src/runtime/foundation/service-liveness-registry.ts +1 -1
  141. package/src/runtime/foundation/service-mailbox.ts +6 -1
  142. package/src/runtime/foundation/service-runtime-contracts.ts +7 -1
  143. package/src/runtime/foundation/service-stateful-registry.ts +39 -14
  144. package/src/runtime/foundation/service-stateful-runtime.ts +87 -21
  145. package/src/runtime/foundation/service-stateful-wire-codec.ts +18 -0
  146. package/src/runtime/foundation/service-wire-constants.generated.ts +10 -0
  147. package/src/runtime/foundation/service-wire-m6a-codec.ts +29 -7
  148. package/src/runtime/host/actor-placement-coordinator.ts +1 -1
  149. package/src/runtime/host/actor-transfer-runtime.ts +3 -3
  150. package/src/runtime/host/application-job-queue.ts +1 -1
  151. package/src/runtime/host/index.ts +183 -44
  152. package/src/runtime/host/location-runtime-owner.ts +2 -2
  153. package/src/runtime/host/service-relocation-host-runtime.ts +101 -65
  154. package/src/runtime/host/spot-address-transport.ts +67 -61
  155. package/src/runtime/locations/actor-location-claims.ts +14 -14
  156. package/src/runtime/locations/auto-connect-loop.ts +24 -16
  157. package/src/runtime/locations/auto-connect-planner.ts +2 -5
  158. package/src/runtime/locations/auto-connect-reconciler.ts +37 -24
  159. package/src/runtime/locations/in-memory-location-store.ts +1 -74
  160. package/src/runtime/locations/location-store-repository.ts +32 -153
  161. package/src/runtime/locations/runtime.ts +16 -28
  162. package/src/runtime/messaging/creation-payload-codec.ts +3 -3
  163. package/src/runtime/protocol/service_wire_codec.generated.ts +18 -8
  164. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
  165. package/src/runtime/spots/index.ts +28 -4
  166. package/src/runtime/spots/spot-actor-admission-coordinator.ts +1 -1
  167. package/src/runtime/spots/spot-actor-packet-dispatch.ts +16 -6
  168. package/src/runtime/spots/spot-actor-packet-drain.ts +21 -6
  169. package/src/runtime/spots/spot-entry-activation.ts +1 -1
  170. package/src/runtime/spots/spot-node-runtime-manager.ts +8 -6
  171. package/src/runtime/spots/spot-route-packet-dispatch.ts +8 -4
  172. package/src/runtime/spots/spot-routed-spot-packet-dispatch.ts +3 -1
  173. package/src/runtime/spots/spot-serial-executor.ts +1 -1
  174. package/src/runtime/spots/spot-subscription-dispatch.ts +3 -1
  175. package/src/runtime/spots/spot-timer.ts +1 -1
  176. package/src/runtime/streams/actor-session-binding-registry.ts +30 -3
  177. package/src/runtime/streams/index.ts +30 -4
  178. package/src/runtime/streams/managed-stream.ts +2 -1
  179. package/src/runtime/streams/session-calls.ts +9 -14
  180. package/src/runtime/streams/session-context.ts +6 -11
  181. package/src/runtime/streams/stream-session-runtime.ts +49 -20
@@ -4,7 +4,7 @@ import { inspectRuntime } from '../internal-observation';
4
4
  import { encodeActorReply } from '../actors/actor-reply';
5
5
  import { ZLinkStreamMessageKind } from '../streams/protocol';
6
6
  import {
7
- decodeApplicationPayloadView,
7
+ decodeApplicationPayload,
8
8
  encodeMultipartApplicationPayload
9
9
  } from '../foundation/service-wire-m6a-codec';
10
10
  import {
@@ -80,6 +80,7 @@ import {
80
80
  flowIfEnabled,
81
81
  type ZLinkMessageFlowTracer
82
82
  } from '../diagnostics/message-flow';
83
+ import { currentFlowContext } from '../diagnostics/flow-context';
83
84
  import { dispatchErrorDetails } from '../diagnostics/dispatch-error-details';
84
85
  import { isStructuralGuardEnabled, trackDiagnosticCompletion } from '../execution/state-lane';
85
86
  import {
@@ -276,7 +277,7 @@ interface ZLinkHostRelocationOptions {
276
277
  clear?(): void | Promise<void>;
277
278
  };
278
279
  readonly trackInstanceSpot?: (input: ZLinkTrackedInstanceAuthority) => void;
279
- readonly invalidateActorRoute?: (actorId: string) => void;
280
+ readonly observeActorAuthorityVersion?: (actorId: string, storeVersion: string) => void;
280
281
  readonly reconcileStatefulAuthorityRoutes?: (signal?: AbortSignal) => Promise<void>;
281
282
  readonly runtimeEventPublisher?: ZLinkRuntimeEventPublisher;
282
283
  readonly metrics?: ZLinkRuntimeMetrics;
@@ -437,13 +438,13 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
437
438
  });
438
439
  }
439
440
 
440
- private readonly targetStages = new Map<string, LocalStage>();
441
+ readonly targetStages = new Map<string, LocalStage>();
441
442
  private readonly terminalTargets = new BoundedReplayMap<string, string>(
442
443
  RELOCATION_TARGET_TOMBSTONE_LIMIT
443
444
  );
444
- private readonly targetPrepareOperations = new Map<string, TargetPrepareOperation>();
445
+ readonly targetPrepareOperations = new Map<string, TargetPrepareOperation>();
445
446
  private readonly pendingReplyRelays = new Map<string, PendingRelocationReplyRelay>();
446
- private readonly pendingSessionRelocations = new Map<string, PendingSessionRelocation>();
447
+ readonly pendingSessionRelocations = new Map<string, PendingSessionRelocation>();
447
448
  private readonly terminalSessionRelocations = new BoundedReplayMap<
448
449
  string,
449
450
  TerminalSessionRelocationControl
@@ -457,7 +458,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
457
458
  SERVICE_CONTROL_TERMINAL_CAPACITY
458
459
  );
459
460
  private readonly sourceRelocationIds = new Set<string>();
460
- private readonly targetAssemblies = new Map<string, TargetPayloadAssembly>();
461
+ readonly targetAssemblies = new Map<string, TargetPayloadAssembly>();
461
462
  /**
462
463
  * Terminal restore failures (assembly/checksum/factory/restore error): the
463
464
  * target already sent relocationFailed (command 53) once. An exact-identity
@@ -484,7 +485,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
484
485
  private readonly terminalActorJoinSourceLeaves = new BoundedReplayMap<string, string>(
485
486
  SERVICE_CONTROL_TERMINAL_CAPACITY
486
487
  );
487
- private readonly codec = new ServiceRelocationAuthorityPayloadCodec();
488
+ readonly codec = new ServiceRelocationAuthorityPayloadCodec();
488
489
  /**
489
490
  * Set once dispose() starts tearing assemblies down. A payload() rejection
490
491
  * observed after this point is the shutdown itself, not a chunk-assembly or
@@ -493,7 +494,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
493
494
  private disposed = false;
494
495
  private readonly targetPreparationStop = new AbortController();
495
496
 
496
- constructor(private readonly options: ZLinkHostRelocationOptions) {}
497
+ constructor(readonly options: ZLinkHostRelocationOptions) {}
497
498
 
498
499
  async relocateActorJoin(
499
500
  input: Parameters<ZLinkActorJoinRelocation['relocateActorJoin']>[0]
@@ -1129,7 +1130,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
1129
1130
  }
1130
1131
 
1131
1132
  /** Sends the Ready reply once the async restore finished; failures replay on resend. */
1132
- private async deliverTargetReady(
1133
+ async deliverTargetReady(
1133
1134
  meshName: string,
1134
1135
  operationKey: string,
1135
1136
  request: ServiceMaintenanceRelocationPrepare,
@@ -1681,7 +1682,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
1681
1682
  }
1682
1683
  }
1683
1684
 
1684
- private async relocateSpotAggregate(
1685
+ async relocateSpotAggregate(
1685
1686
  meshName: string,
1686
1687
  activation: ZLinkSpotActivation,
1687
1688
  kind: 'user_spot' | 'instance_spot',
@@ -1962,7 +1963,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
1962
1963
  const relocationMetric =
1963
1964
  spotRegistration.relocation.kind === 'disabled'
1964
1965
  ? undefined
1965
- : this.options.metrics?.startRelocation(meshName, kind, spotRegistration.relocation.kind);
1966
+ : this.startSourceRelocationMetric(meshName, kind, spotRegistration.relocation.kind);
1966
1967
  try {
1967
1968
  const waitForRelocationBoundary = (
1968
1969
  activation as unknown as {
@@ -2041,7 +2042,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2041
2042
  }
2042
2043
  }
2043
2044
 
2044
- private async relocateStandaloneActor(
2045
+ async relocateStandaloneActor(
2045
2046
  meshName: string,
2046
2047
  state: ZLinkActorRuntimeState,
2047
2048
  target: ZLinkMeshNodeDescriptor | undefined,
@@ -2115,7 +2116,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2115
2116
  const relocationMetric =
2116
2117
  actorPolicy === 'disabled'
2117
2118
  ? undefined
2118
- : this.options.metrics?.startRelocation(meshName, 'actor', actorPolicy);
2119
+ : this.startSourceRelocationMetric(meshName, 'actor', actorPolicy, state.actorId);
2119
2120
  try {
2120
2121
  if (isRelocationDebugEnabled()) {
2121
2122
  relocationDebug('standalone_actor.wait_budget', {
@@ -2158,7 +2159,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2158
2159
  }
2159
2160
  }
2160
2161
 
2161
- private async relocatePerActorSpotShell(
2162
+ async relocatePerActorSpotShell(
2162
2163
  meshName: string,
2163
2164
  activation: ZLinkSpotActivation,
2164
2165
  target: ZLinkMeshNodeDescriptor | undefined,
@@ -2277,11 +2278,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2277
2278
  const relocationMetric =
2278
2279
  spotRegistration.relocation.kind === 'disabled'
2279
2280
  ? undefined
2280
- : this.options.metrics?.startRelocation(
2281
- meshName,
2282
- 'user_spot',
2283
- spotRegistration.relocation.kind
2284
- );
2281
+ : this.startSourceRelocationMetric(meshName, 'user_spot', spotRegistration.relocation.kind);
2285
2282
  try {
2286
2283
  await this.waitForRelocationBudgetHeadroom(String(target.rid), signal);
2287
2284
  const captured = await new ServiceRelocationObjectCaptureOwner().captureUserSpotAggregate(
@@ -2473,7 +2470,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2473
2470
  this.sourceRelocationIds.add(id);
2474
2471
  }
2475
2472
 
2476
- private async runCoordinator(
2473
+ async runCoordinator(
2477
2474
  meshName: string,
2478
2475
  target: ZLinkMeshNodeDescriptor,
2479
2476
  primary: ZLinkAuthoritySnapshot,
@@ -2802,6 +2799,50 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2802
2799
  );
2803
2800
  }
2804
2801
 
2802
+ startSourceRelocationMetric(
2803
+ meshName: string,
2804
+ objectKind: 'actor' | 'user_spot' | 'instance_spot',
2805
+ policy: 'recreate' | 'snapshot',
2806
+ actorId?: string
2807
+ ): ZLinkRelocationMetricOperation | undefined {
2808
+ const flow = objectKind === 'actor' ? this.options.messageFlow?.() : undefined;
2809
+ const flowContext = flow?.flowCreationEnabled() === true ? currentFlowContext() : undefined;
2810
+ const traceTerminal = (outcome: string): void => {
2811
+ if (flow === undefined || flowContext === undefined) return;
2812
+ const result =
2813
+ outcome === 'completed' ? 'succeeded' : outcome === 'shutdown' ? 'shutdown' : 'failed';
2814
+ flowIfEnabled(flow, ZLinkMessageFlowOutcome.Completed, result, flowContext.flowId)?.trace({
2815
+ outcome: ZLinkMessageFlowOutcome.Completed,
2816
+ result,
2817
+ surface: ZLinkDispatchErrorSurface.ActorRelocation,
2818
+ messageKind: ZLinkDispatchMessageKind.Control,
2819
+ meshName,
2820
+ actorId,
2821
+ flowId: flowContext.flowId,
2822
+ flowOrigin: flowContext.flowOrigin,
2823
+ errorMessage: result === 'failed' ? `source_relocation_${outcome}` : undefined
2824
+ });
2825
+ };
2826
+ const metric = this.options.metrics?.startRelocation(
2827
+ meshName,
2828
+ objectKind,
2829
+ policy,
2830
+ traceTerminal
2831
+ );
2832
+ if (metric !== undefined) return metric;
2833
+ if (flow === undefined || flowContext === undefined) return undefined;
2834
+
2835
+ let completed = false;
2836
+ return {
2837
+ recordBytes: (): void => {},
2838
+ complete: (outcome: string): void => {
2839
+ if (completed) return;
2840
+ completed = true;
2841
+ traceTerminal(outcome);
2842
+ }
2843
+ };
2844
+ }
2845
+
2805
2846
  /** Streams the payload chunks of one stage under the effective in-flight budgets. */
2806
2847
  private async sendRelocationStateChunks(
2807
2848
  meshName: string,
@@ -2864,10 +2905,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2864
2905
  }
2865
2906
 
2866
2907
  /** Pre-seal admission wait — a full budget delays the next unit before seal. */
2867
- private async waitForRelocationBudgetHeadroom(
2868
- targetRid: string,
2869
- signal?: AbortSignal
2870
- ): Promise<void> {
2908
+ async waitForRelocationBudgetHeadroom(targetRid: string, signal?: AbortSignal): Promise<void> {
2871
2909
  await this.requireNodePayloadBudget().waitForHeadroom(signal);
2872
2910
  await this.requirePeerPayloadBudget(targetRid).waitForHeadroom(signal);
2873
2911
  }
@@ -2954,7 +2992,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2954
2992
  }
2955
2993
 
2956
2994
  /** Relocation flow §4.4: replay the whole batch on a different ready route. */
2957
- private async resendSourceCutover(window: SourceCutoverWindow): Promise<void> {
2995
+ async resendSourceCutover(window: SourceCutoverWindow): Promise<void> {
2958
2996
  const frames = window.frames;
2959
2997
  if (frames === undefined) return;
2960
2998
  const route = this.requireMeshNode(window.meshName).observePeerRoute?.(window.targetNodeRid);
@@ -2986,7 +3024,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
2986
3024
  * A committed later owner has no Follow snapshot (Location runtime §10). An uncertain
2987
3025
  * Store or lease result keeps the work retained and repeats the same decision.
2988
3026
  */
2989
- private async settleSourceAuthority(
3027
+ async settleSourceAuthority(
2990
3028
  envelope: ServiceRelocationEnvelope,
2991
3029
  primary: ZLinkAuthoritySnapshot,
2992
3030
  target: ServiceWireRelocationTarget,
@@ -3098,7 +3136,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3098
3136
  return this.sourceCutoverWindows.size === 0;
3099
3137
  }
3100
3138
 
3101
- private async releaseParticipantAuthorities(
3139
+ async releaseParticipantAuthorities(
3102
3140
  envelope: ServiceRelocationEnvelope,
3103
3141
  primaryKey: string,
3104
3142
  signal?: AbortSignal
@@ -3122,11 +3160,17 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3122
3160
  if (result.kind !== 'stored') {
3123
3161
  throw new Error(`Participant '${participant.key}' recovery pointer release failed.`);
3124
3162
  }
3163
+ if (participant.objectKind === 'actor') {
3164
+ this.options.observeActorAuthorityVersion?.(
3165
+ decodeAuthorityKey(key).globalId,
3166
+ result.storeVersion.value
3167
+ );
3168
+ }
3125
3169
  }
3126
3170
  }
3127
3171
 
3128
3172
  /** Restores the hidden object and its saved-work prefix before Ready. */
3129
- private async handlePrepareControl(
3173
+ async handlePrepareControl(
3130
3174
  meshName: string,
3131
3175
  stagingId: string,
3132
3176
  request: ServiceMaintenanceRelocationPrepare,
@@ -3384,7 +3428,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3384
3428
  * whole (the retransmission always resends the entire batch, so the exact
3385
3429
  * span is the staged suffix of `boundaryRecordCount` records).
3386
3430
  */
3387
- private reconcileBoundaryRelay(
3431
+ reconcileBoundaryRelay(
3388
3432
  stage: LocalStage,
3389
3433
  request: ServiceMaintenanceRelocationCutover,
3390
3434
  stagingId: string
@@ -3415,7 +3459,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3415
3459
  );
3416
3460
  }
3417
3461
 
3418
- private beginTargetFinalize(
3462
+ beginTargetFinalize(
3419
3463
  meshName: string,
3420
3464
  stagingId: string,
3421
3465
  stage: LocalStage,
@@ -3432,7 +3476,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3432
3476
  return finalize;
3433
3477
  }
3434
3478
 
3435
- private async finalizeTargetStage(
3479
+ async finalizeTargetStage(
3436
3480
  meshName: string,
3437
3481
  stagingId: string,
3438
3482
  stage: LocalStage,
@@ -3484,15 +3528,6 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3484
3528
  );
3485
3529
  }
3486
3530
  await stage.owner.normalize(stage.staging, authority, signal);
3487
- // The target may have visited this Actor before and still retain that
3488
- // tenure in its private Actor-client resolver. The target-only CAS is
3489
- // now authoritative, so expire the old projection before OnJoinedActor
3490
- // can issue an Actor request and deadlock behind source leave.
3491
- for (const hidden of stage.staging.hidden.values()) {
3492
- if (hidden.actor !== undefined) {
3493
- this.options.invalidateActorRoute?.(hidden.actor.context.actorId);
3494
- }
3495
- }
3496
3531
  await this.finalizeActorJoinProfiles(meshName, stage, signal);
3497
3532
  await this.publishSessionRoutes(stage.staging);
3498
3533
  stage.phase = 'open';
@@ -3572,7 +3607,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3572
3607
  );
3573
3608
  }
3574
3609
 
3575
- private async finalizeActorJoinProfiles(
3610
+ async finalizeActorJoinProfiles(
3576
3611
  meshName: string,
3577
3612
  stage: LocalStage,
3578
3613
  signal?: AbortSignal
@@ -3605,7 +3640,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3605
3640
  }
3606
3641
  }
3607
3642
 
3608
- private async submitActorJoinSourceLeave(
3643
+ async submitActorJoinSourceLeave(
3609
3644
  meshName: string,
3610
3645
  sourceNodeRid: RoutingId,
3611
3646
  relocationId: string,
@@ -3631,7 +3666,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3631
3666
  }
3632
3667
  }
3633
3668
 
3634
- private acceptActorJoinSourceLeave(input: {
3669
+ acceptActorJoinSourceLeave(input: {
3635
3670
  readonly transferId: string;
3636
3671
  readonly actorId: string;
3637
3672
  readonly succeeded: boolean;
@@ -3671,7 +3706,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3671
3706
  );
3672
3707
  }
3673
3708
 
3674
- private async submitControlFrame(
3709
+ async submitControlFrame(
3675
3710
  meshName: string,
3676
3711
  targetNodeRid: RoutingId,
3677
3712
  frame: Buffer,
@@ -3685,7 +3720,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3685
3720
  }
3686
3721
  }
3687
3722
 
3688
- private async clearTargetRelocationPublication(
3723
+ async clearTargetRelocationPublication(
3689
3724
  stage: LocalStage,
3690
3725
  primary: ZLinkAuthoritySnapshot,
3691
3726
  signal?: AbortSignal
@@ -3714,6 +3749,12 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3714
3749
  if (result.kind !== 'stored') {
3715
3750
  throw new Error('Relocation target authority normalization CAS failed.');
3716
3751
  }
3752
+ if (primary.allocation.objectKind === 'actor') {
3753
+ this.options.observeActorAuthorityVersion?.(
3754
+ decodeAuthorityKey(stage.staging.primaryAuthorityKey).globalId,
3755
+ result.storeVersion.value
3756
+ );
3757
+ }
3717
3758
  return { ...result, kind: 'snapshot' };
3718
3759
  }
3719
3760
 
@@ -3793,7 +3834,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3793
3834
  }
3794
3835
  }
3795
3836
 
3796
- private async sendReplyRelay(
3837
+ async sendReplyRelay(
3797
3838
  meshName: string,
3798
3839
  ackTargetNodeRid: RoutingId,
3799
3840
  request: ServiceMaintenanceReplyRelay,
@@ -3911,7 +3952,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3911
3952
  }
3912
3953
  }
3913
3954
 
3914
- private targetReplyRelayCoordinator(
3955
+ targetReplyRelayCoordinator(
3915
3956
  meshName: string,
3916
3957
  stage: LocalStage,
3917
3958
  authority: ZLinkAuthoritySnapshot
@@ -3937,7 +3978,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3937
3978
  };
3938
3979
  }
3939
3980
 
3940
- private async relayTerminalReply(
3981
+ async relayTerminalReply(
3941
3982
  meshName: string,
3942
3983
  stage: LocalStage,
3943
3984
  coordinator: ServiceWireRelocationCoordinatorFence,
@@ -3987,9 +4028,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
3987
4028
  );
3988
4029
  }
3989
4030
 
3990
- private async publishSessionRoutes(
3991
- staging: ServiceObjectRelocationStaging<LocalHidden>
3992
- ): Promise<void> {
4031
+ async publishSessionRoutes(staging: ServiceObjectRelocationStaging<LocalHidden>): Promise<void> {
3993
4032
  for (const [key, hidden] of staging.hidden) {
3994
4033
  if (hidden.actor === undefined) continue;
3995
4034
  // The Session seal travels with the relocation envelope, not the Actor.
@@ -4001,7 +4040,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
4001
4040
  }
4002
4041
  }
4003
4042
 
4004
- private async sendControl(
4043
+ async sendControl(
4005
4044
  meshName: string,
4006
4045
  targetNodeRid: RoutingId,
4007
4046
  request: ZLinkServiceRelocationControlRequest,
@@ -4132,7 +4171,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
4132
4171
  return authorities;
4133
4172
  }
4134
4173
 
4135
- private async reserveTargetForPrepare(
4174
+ async reserveTargetForPrepare(
4136
4175
  meshName: string,
4137
4176
  prepare: ServiceMaintenanceRelocationPrepare,
4138
4177
  envelope: ServiceRelocationEnvelope,
@@ -4206,7 +4245,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
4206
4245
  };
4207
4246
  }
4208
4247
 
4209
- private authorityPayloadForPublication(
4248
+ authorityPayloadForPublication(
4210
4249
  payload: Uint8Array,
4211
4250
  publication: ServiceRelocationPublication,
4212
4251
  target?: {
@@ -4337,7 +4376,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
4337
4376
  }
4338
4377
  }
4339
4378
 
4340
- private async commitTargetReservation(
4379
+ async commitTargetReservation(
4341
4380
  stage: LocalStage,
4342
4381
  reservation: TargetRelocationReservation,
4343
4382
  signal?: AbortSignal
@@ -4412,7 +4451,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
4412
4451
  }
4413
4452
  }
4414
4453
 
4415
- private async readAggregateForCommitRetry(
4454
+ async readAggregateForCommitRetry(
4416
4455
  prepared: ServicePreparedRelocationAggregate,
4417
4456
  signal?: AbortSignal
4418
4457
  ): Promise<
@@ -4460,7 +4499,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
4460
4499
  return source === prepared.plan.participants.length ? { kind: 'source' } : { kind: 'stale' };
4461
4500
  }
4462
4501
 
4463
- private async materializeTargetEnvelope(
4502
+ async materializeTargetEnvelope(
4464
4503
  meshName: string,
4465
4504
  envelope: ServiceRelocationEnvelope,
4466
4505
  signal?: AbortSignal
@@ -4478,7 +4517,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
4478
4517
  }
4479
4518
 
4480
4519
  /** Maps schema ordinals to the authoritative Location Store inventory. */
4481
- private async resolveTargetParticipantInventory(
4520
+ async resolveTargetParticipantInventory(
4482
4521
  meshName: string,
4483
4522
  request: ServiceMaintenanceRelocationPrepare,
4484
4523
  envelope: ServiceRelocationEnvelope,
@@ -4635,7 +4674,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
4635
4674
  return value;
4636
4675
  }
4637
4676
 
4638
- private async captureApplication<T>(
4677
+ async captureApplication<T>(
4639
4678
  policy: { readonly kind: 'disabled' | 'recreate' | 'snapshot'; readonly adapterType?: Type },
4640
4679
  value: T,
4641
4680
  signal?: AbortSignal
@@ -4776,11 +4815,7 @@ export class ZLinkHostServiceRelocationRuntime implements ZLinkActorJoinRelocati
4776
4815
  return value;
4777
4816
  }
4778
4817
 
4779
- private afterSpotCloseDecision<T>(
4780
- meshName: string,
4781
- spotId: RoutingId,
4782
- seal: () => T
4783
- ): T | Promise<T> {
4818
+ afterSpotCloseDecision<T>(meshName: string, spotId: RoutingId, seal: () => T): T | Promise<T> {
4784
4819
  const manager = this.requireSpotManager();
4785
4820
  const sealAfterDecision = (): T => {
4786
4821
  if (manager.isSpotClosing(meshName, spotId)) {
@@ -5129,6 +5164,7 @@ class LocalTargetPort implements ServiceRelocationTargetObjectPort<LocalHidden>
5129
5164
  value: hidden.authorityKey
5130
5165
  } as ZLinkAuthorityKey);
5131
5166
  const actorId = hidden.actor.context.actorId;
5167
+ this.options.observeActorAuthorityVersion?.(actorId, current.storeVersion.value);
5132
5168
  const node = this.options.meshNode(this.meshName);
5133
5169
  const restore = node?.restoreActorAuthority;
5134
5170
  if (node === undefined || restore === undefined) {
@@ -5836,7 +5872,7 @@ function maintenanceReplyRelay(
5836
5872
  const direct = packet.messageFollowOrigin?.requestSource !== undefined;
5837
5873
  const application =
5838
5874
  direct && completion.result.ok
5839
- ? decodeApplicationPayloadView(
5875
+ ? decodeApplicationPayload(
5840
5876
  encodeMultipartApplicationPayload(
5841
5877
  encodeActorReply(
5842
5878
  Buffer.from(packet.header, 'base64'),
@@ -263,55 +263,53 @@ export class ZLinkHostSpotAddressTransport implements ZLinkSpotAddressTransport
263
263
  return this.requests.request(timeoutMs, call.signal, async (requestOperation) => {
264
264
  const deadline = createSpotAddressDeadline(requestOperation, call.signal);
265
265
  try {
266
- const resolution = await waitRequestReply(
267
- this.resolveExisting(spotId, deadline.signal, call),
268
- 'Spot address resolve',
269
- requestOperation,
270
- () => deadline.close()
271
- );
272
- const existing =
273
- resolution !== undefined && !('kind' in resolution) ? resolution : undefined;
274
- if (resolution !== undefined && 'kind' in resolution)
275
- call = this.reactivationCall(resolution, call);
276
- deadline.requireRemaining();
277
- if (existing === undefined) {
278
- return await this.requestToMissingInstance(spotId, request, call, deadline, resolution);
279
- }
280
- this.validateExisting(existing, call);
281
- try {
282
- return await this.requestToExistingSpot(spotId, request, call, existing, deadline);
283
- } catch (error) {
284
- if (isSpotRouteRefreshError(error)) {
285
- this.options.resolver()?.invalidate?.(spotId);
286
- }
287
- // Failover policy §4.4: SpotMoving is the owner's Ready owner-fence
288
- // refusal, made before admission. With Instance intent, one authority
289
- // read under the same deadline decides: Missing means Close released
290
- // the incarnation, so the request activates a new one. Any other
291
- // answer, and any message the owner admitted, keeps this terminal.
292
- if (
293
- call.instanceSpot &&
294
- isOwnerFenceRefusal(error) &&
295
- (await waitRequestReply(
296
- this.resolveExisting(spotId, deadline.signal),
297
- 'Spot address resolve',
298
- requestOperation,
299
- () => deadline.close()
300
- )) === undefined
301
- ) {
302
- deadline.requireRemaining();
303
- return await this.requestToMissingInstance(spotId, request, call, deadline);
304
- }
305
- this.reportInstanceRequestError(
306
- spotId,
307
- request,
308
- error,
309
- existing.routerChannelId,
310
- existing.targetNodeRid,
311
- existing.stableType,
312
- addressedInstanceErrorReason(error)
266
+ for (;;) {
267
+ deadline.requireRemaining();
268
+ const resolution = await waitRequestReply(
269
+ this.resolveExisting(spotId, deadline.signal, call),
270
+ 'Spot address resolve',
271
+ requestOperation,
272
+ () => deadline.close()
313
273
  );
314
- throw error;
274
+ const existing =
275
+ resolution !== undefined && !('kind' in resolution) ? resolution : undefined;
276
+ if (resolution !== undefined && 'kind' in resolution)
277
+ call = this.reactivationCall(resolution, call);
278
+ deadline.requireRemaining();
279
+ try {
280
+ if (existing === undefined) {
281
+ return await this.requestToMissingInstance(
282
+ spotId,
283
+ request,
284
+ call,
285
+ deadline,
286
+ resolution
287
+ );
288
+ }
289
+ this.validateExisting(existing, call);
290
+ return await this.requestToExistingSpot(spotId, request, call, existing, deadline);
291
+ } catch (error) {
292
+ if (
293
+ (existing !== undefined || isPreAdmissionStaleRoute(error)) &&
294
+ isSpotRouteRefreshError(error)
295
+ ) {
296
+ this.options.resolver()?.invalidate?.(spotId);
297
+ }
298
+ // §9 keeps admission-before-stale-route handling in this logical request.
299
+ if (call.instanceSpot && isPreAdmissionStaleRoute(error)) continue;
300
+ if (existing !== undefined) {
301
+ this.reportInstanceRequestError(
302
+ spotId,
303
+ request,
304
+ error,
305
+ existing.routerChannelId,
306
+ existing.targetNodeRid,
307
+ existing.stableType,
308
+ addressedInstanceErrorReason(error)
309
+ );
310
+ }
311
+ throw error;
312
+ }
315
313
  }
316
314
  } catch (error) {
317
315
  if (
@@ -348,9 +346,13 @@ export class ZLinkHostSpotAddressTransport implements ZLinkSpotAddressTransport
348
346
  existing.stableType,
349
347
  existing.targetNodeRid
350
348
  );
351
- const reply = await waitRequestReply(
352
- mapZLinkRequestReply(
353
- this.options.routed.requestToSpot<TReply>(existing, request, {
349
+ // The address request owns the terminal entry. Observe the wire stage so
350
+ // a pre-admission refusal can reach the authority decision in §9.
351
+ const stage = new Promise<TReply>((resolve, reject) => {
352
+ const result = this.options.routed.requestToSpot<TReply>(
353
+ existing,
354
+ request,
355
+ {
354
356
  instanceSpot: call.instanceSpot,
355
357
  timeoutMs: deadline.requireRemaining(),
356
358
  deadlineMs: deadline.deadlineMs,
@@ -358,11 +360,15 @@ export class ZLinkHostSpotAddressTransport implements ZLinkSpotAddressTransport
358
360
  metadata: call.metadata,
359
361
  onAdmitted: call.onAdmitted,
360
362
  requestOperation: deadline.operation
361
- })
362
- ),
363
- 'Spot request',
364
- deadline.operation
365
- );
363
+ },
364
+ {
365
+ operationCompleted: (_id, value) => resolve(value as TReply),
366
+ operationFailed: (_id, error) => reject(error)
367
+ }
368
+ );
369
+ if (result.operation === undefined) void mapZLinkRequestReply(result).then(resolve, reject);
370
+ });
371
+ const reply = await waitRequestReply(stage, 'Spot request', deadline.operation);
366
372
  this.traceInstanceAddress(
367
373
  ZLinkMessageFlowOutcome.ReplyReceived,
368
374
  ZLinkDispatchMessageKind.Request,
@@ -778,19 +784,19 @@ function isSpotRouteRefreshError(error: unknown): error is ZLinkFrameworkExcepti
778
784
  if (!(error instanceof ZLinkFrameworkException)) return false;
779
785
  const kind = internalFrameworkErrorKind(error);
780
786
  return (
787
+ isPreAdmissionStaleRoute(error) ||
781
788
  kind === ZLinkFrameworkInternalErrorKind.SpotRouteNotFound ||
782
- kind === ZLinkFrameworkInternalErrorKind.SpotGenerationStale ||
783
- kind === ZLinkFrameworkInternalErrorKind.SpotMoving ||
784
789
  kind === ZLinkFrameworkInternalErrorKind.RequestTargetNotFound ||
785
- kind === ZLinkFrameworkInternalErrorKind.ActorLocationStale ||
786
790
  kind === ZLinkFrameworkInternalErrorKind.RouteNotConnected
787
791
  );
788
792
  }
789
793
 
790
- function isOwnerFenceRefusal(error: unknown): boolean {
794
+ function isPreAdmissionStaleRoute(error: unknown): boolean {
791
795
  return (
792
796
  error instanceof ZLinkFrameworkException &&
793
- internalFrameworkErrorKind(error) === ZLinkFrameworkInternalErrorKind.SpotMoving
797
+ (internalFrameworkErrorKind(error) === ZLinkFrameworkInternalErrorKind.SpotMoving ||
798
+ internalFrameworkErrorKind(error) === ZLinkFrameworkInternalErrorKind.ActorLocationStale ||
799
+ internalFrameworkErrorKind(error) === ZLinkFrameworkInternalErrorKind.SpotGenerationStale)
794
800
  );
795
801
  }
796
802