@zlink-systems/framework 0.29.1 → 0.30.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 (152) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Configuration/Builders.d.ts +1 -2
  3. package/dist/contracts/Configuration/Configs.d.ts +0 -5
  4. package/dist/contracts/Configuration/RegistrationBuilders.js +6 -4
  5. package/dist/contracts/Configuration/RegistrationTypes.d.ts +1 -6
  6. package/dist/contracts/Configuration/RegistrationValidators.js +4 -4
  7. package/dist/contracts/Configuration/index.d.ts +1 -1
  8. package/dist/internal.d.ts +1 -0
  9. package/dist/internal.js +3 -1
  10. package/dist/runtime/actors/actor-handoff.d.ts +3 -2
  11. package/dist/runtime/actors/actor-handoff.js +9 -4
  12. package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +3 -1
  13. package/dist/runtime/application-jobs/application-ingress-record-owner.js +6 -1
  14. package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +3 -1
  15. package/dist/runtime/application-jobs/application-job-queue-scope.js +5 -0
  16. package/dist/runtime/application-jobs/contracts.d.ts +5 -1
  17. package/dist/runtime/backend/contracts/index.d.ts +10 -19
  18. package/dist/runtime/backend/mesh-completion-table.d.ts +6 -4
  19. package/dist/runtime/backend/mesh-completion-table.js +28 -18
  20. package/dist/runtime/backend/mesh-dispatch-pump.js +21 -25
  21. package/dist/runtime/backend/node/node-backend-adapter-factory.js +6 -1
  22. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +3 -3
  23. package/dist/runtime/backend/node/node-backend-adapter-support.js +24 -29
  24. package/dist/runtime/backend/node/node-mesh-backend-adapter.d.ts +1 -9
  25. package/dist/runtime/backend/node/node-mesh-backend-adapter.js +1 -1
  26. package/dist/runtime/backend/node/node-raw-binding-port.js +11 -6
  27. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +10 -12
  28. package/dist/runtime/backend/node/node-raw-mesh-backend.js +64 -98
  29. package/dist/runtime/backend/node/node-socket-backend-adapter.js +10 -5
  30. package/dist/runtime/backend/raw-binding-port.d.ts +9 -2
  31. package/dist/runtime/channels/channel-receive-loops.js +3 -3
  32. package/dist/runtime/channels/channel-runtime-lifecycle.js +2 -1
  33. package/dist/runtime/channels/channel-socket-registry.d.ts +10 -3
  34. package/dist/runtime/channels/channel-socket-registry.js +59 -82
  35. package/dist/runtime/diagnostics/message-flow.d.ts +1 -0
  36. package/dist/runtime/diagnostics/message-flow.js +3 -3
  37. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +15 -1
  38. package/dist/runtime/foundation/raw-service-mesh-runtime.js +193 -64
  39. package/dist/runtime/foundation/service-liveness-registry.d.ts +20 -8
  40. package/dist/runtime/foundation/service-liveness-registry.js +57 -44
  41. package/dist/runtime/foundation/service-mailbox.d.ts +3 -0
  42. package/dist/runtime/foundation/service-mailbox.js +2 -1
  43. package/dist/runtime/foundation/service-stateful-runtime.d.ts +7 -6
  44. package/dist/runtime/foundation/service-stateful-runtime.js +34 -23
  45. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +5 -2
  46. package/dist/runtime/foundation/service-stateful-wire-codec.js +43 -61
  47. package/dist/runtime/foundation/service-topology-registry.d.ts +5 -3
  48. package/dist/runtime/foundation/service-topology-registry.js +9 -4
  49. package/dist/runtime/host/actor-packet-relay.js +7 -7
  50. package/dist/runtime/host/actor-runtime-options-factory.js +1 -1
  51. package/dist/runtime/host/application-job-queue.d.ts +11 -4
  52. package/dist/runtime/host/application-job-queue.js +66 -10
  53. package/dist/runtime/host/entry-actor-runtime.js +1 -1
  54. package/dist/runtime/host/index.js +15 -11
  55. package/dist/runtime/host/instance-activation-authority.d.ts +2 -1
  56. package/dist/runtime/host/instance-activation-authority.js +89 -25
  57. package/dist/runtime/host/service-relocation-host-runtime.js +2 -2
  58. package/dist/runtime/host/spot-address-transport.d.ts +1 -0
  59. package/dist/runtime/host/spot-address-transport.js +25 -12
  60. package/dist/runtime/host/spot-runtime-options-factory.js +2 -2
  61. package/dist/runtime/host/user-spot-creation-coordinator.d.ts +1 -0
  62. package/dist/runtime/host/user-spot-creation-coordinator.js +1 -0
  63. package/dist/runtime/locations/domain-store-contract.d.ts +2 -2
  64. package/dist/runtime/locations/in-memory-authority-store.d.ts +4 -2
  65. package/dist/runtime/locations/in-memory-authority-store.js +80 -8
  66. package/dist/runtime/locations/in-memory-location-store.d.ts +2 -2
  67. package/dist/runtime/locations/in-memory-location-store.js +19 -3
  68. package/dist/runtime/locations/internal-location-contracts.d.ts +3 -0
  69. package/dist/runtime/locations/location-store-repository.d.ts +3 -2
  70. package/dist/runtime/locations/location-store-repository.js +165 -22
  71. package/dist/runtime/locations/resolvers.d.ts +2 -1
  72. package/dist/runtime/locations/resolvers.js +10 -1
  73. package/dist/runtime/messaging/submission-result.d.ts +1 -1
  74. package/dist/runtime/messaging/submission-result.js +2 -2
  75. package/dist/runtime/spots/formal-remote-actor-admission-registry.d.ts +2 -0
  76. package/dist/runtime/spots/index.d.ts +4 -3
  77. package/dist/runtime/spots/index.js +14 -14
  78. package/dist/runtime/spots/spot-activation.d.ts +2 -1
  79. package/dist/runtime/spots/spot-activation.js +4 -4
  80. package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +2 -1
  81. package/dist/runtime/spots/spot-actor-admission-coordinator.js +11 -10
  82. package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +4 -1
  83. package/dist/runtime/spots/spot-actor-packet-dispatch.js +7 -5
  84. package/dist/runtime/spots/spot-actor-packet-drain.js +2 -0
  85. package/dist/runtime/spots/spot-actor-packet-relay-dispatch.js +1 -0
  86. package/dist/runtime/spots/spot-entry-activation.js +3 -1
  87. package/dist/runtime/spots/spot-node-runtime-manager.d.ts +2 -1
  88. package/dist/runtime/spots/spot-node-runtime-manager.js +4 -11
  89. package/dist/runtime/spots/spot-routing-internal.d.ts +6 -0
  90. package/dist/runtime/spots/spot-runtime-ports.d.ts +2 -1
  91. package/dist/runtime/streams/stream-session-runtime.js +1 -1
  92. package/package.json +3 -3
  93. package/src/contracts/Configuration/Builders.ts +1 -2
  94. package/src/contracts/Configuration/Configs.ts +0 -6
  95. package/src/contracts/Configuration/RegistrationBuilders.ts +8 -12
  96. package/src/contracts/Configuration/RegistrationTypes.ts +1 -6
  97. package/src/contracts/Configuration/RegistrationValidators.ts +9 -10
  98. package/src/contracts/Configuration/index.ts +1 -6
  99. package/src/internal.ts +1 -0
  100. package/src/runtime/actors/actor-handoff.ts +13 -4
  101. package/src/runtime/application-jobs/application-ingress-record-owner.ts +12 -2
  102. package/src/runtime/application-jobs/application-job-queue-scope.ts +6 -1
  103. package/src/runtime/application-jobs/contracts.ts +14 -1
  104. package/src/runtime/backend/contracts/index.ts +10 -18
  105. package/src/runtime/backend/mesh-completion-table.ts +36 -19
  106. package/src/runtime/backend/mesh-dispatch-pump.ts +19 -22
  107. package/src/runtime/backend/node/node-backend-adapter-factory.ts +12 -5
  108. package/src/runtime/backend/node/node-backend-adapter-support.ts +31 -29
  109. package/src/runtime/backend/node/node-mesh-backend-adapter.ts +3 -10
  110. package/src/runtime/backend/node/node-raw-binding-port.ts +19 -10
  111. package/src/runtime/backend/node/node-raw-mesh-backend.ts +81 -111
  112. package/src/runtime/backend/node/node-socket-backend-adapter.ts +12 -8
  113. package/src/runtime/backend/raw-binding-port.ts +12 -2
  114. package/src/runtime/channels/channel-receive-loops.ts +3 -3
  115. package/src/runtime/channels/channel-runtime-lifecycle.ts +7 -1
  116. package/src/runtime/channels/channel-socket-registry.ts +84 -130
  117. package/src/runtime/diagnostics/message-flow.ts +1 -1
  118. package/src/runtime/foundation/raw-service-mesh-runtime.ts +268 -70
  119. package/src/runtime/foundation/service-liveness-registry.ts +68 -59
  120. package/src/runtime/foundation/service-mailbox.ts +7 -1
  121. package/src/runtime/foundation/service-stateful-runtime.ts +61 -35
  122. package/src/runtime/foundation/service-stateful-wire-codec.ts +55 -79
  123. package/src/runtime/foundation/service-topology-registry.ts +13 -7
  124. package/src/runtime/host/actor-packet-relay.ts +7 -0
  125. package/src/runtime/host/actor-runtime-options-factory.ts +1 -0
  126. package/src/runtime/host/application-job-queue.ts +77 -10
  127. package/src/runtime/host/entry-actor-runtime.ts +1 -0
  128. package/src/runtime/host/index.ts +24 -5
  129. package/src/runtime/host/instance-activation-authority.ts +132 -27
  130. package/src/runtime/host/service-relocation-host-runtime.ts +2 -0
  131. package/src/runtime/host/spot-address-transport.ts +45 -22
  132. package/src/runtime/host/spot-runtime-options-factory.ts +2 -0
  133. package/src/runtime/host/user-spot-creation-coordinator.ts +8 -0
  134. package/src/runtime/locations/domain-store-contract.ts +6 -2
  135. package/src/runtime/locations/in-memory-authority-store.ts +113 -9
  136. package/src/runtime/locations/in-memory-location-store.ts +25 -3
  137. package/src/runtime/locations/internal-location-contracts.ts +4 -0
  138. package/src/runtime/locations/location-store-repository.ts +220 -15
  139. package/src/runtime/locations/resolvers.ts +25 -2
  140. package/src/runtime/messaging/submission-result.ts +2 -2
  141. package/src/runtime/spots/formal-remote-actor-admission-registry.ts +2 -0
  142. package/src/runtime/spots/index.ts +16 -0
  143. package/src/runtime/spots/spot-activation.ts +5 -0
  144. package/src/runtime/spots/spot-actor-admission-coordinator.ts +13 -0
  145. package/src/runtime/spots/spot-actor-packet-dispatch.ts +10 -0
  146. package/src/runtime/spots/spot-actor-packet-drain.ts +2 -0
  147. package/src/runtime/spots/spot-actor-packet-relay-dispatch.ts +1 -0
  148. package/src/runtime/spots/spot-entry-activation.ts +3 -0
  149. package/src/runtime/spots/spot-node-runtime-manager.ts +7 -12
  150. package/src/runtime/spots/spot-routing-internal.ts +14 -0
  151. package/src/runtime/spots/spot-runtime-ports.ts +2 -0
  152. package/src/runtime/streams/stream-session-runtime.ts +1 -1
@@ -1,4 +1,10 @@
1
- import { ServiceRelocationAuthorityPayloadCodec } from '../foundation/service-relocation-runtime';
1
+ import {
2
+ ServiceRelocationAuthorityPayloadCodec,
3
+ serviceRelocationAuthorityApplicationPayload
4
+ } from '../foundation/service-relocation-runtime';
5
+ import { decodeServiceReadySpotAuthority } from '../foundation/service-authority-payload-codec';
6
+ import { decodeActorAuthorityPayload } from '../actors/actor-authority-payload-codec';
7
+ import { ZLinkSpotKind } from '../../contracts';
2
8
  import { UINT64_MAX } from '@zlink-systems/stream-wire';
3
9
  import { createHash, randomUUID } from 'node:crypto';
4
10
  import { zlinkRuntimeDefaultLocationOptions } from '../../contracts/Locations/Options';
@@ -76,6 +82,7 @@ import {
76
82
  type ZLinkObjectCommitResult,
77
83
  type ZLinkObjectCreationCompleteRequest,
78
84
  type ZLinkObjectCreationCompleteResult,
85
+ type ZLinkEndedOwnerReleaseIntent,
79
86
  type ZLinkObjectReserveRequest,
80
87
  type ZLinkObjectReserveResult,
81
88
  type ZLinkOwnerLeaseClaimResult,
@@ -429,10 +436,39 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
429
436
  payload: Buffer.from(mutation.payload)
430
437
  }
431
438
  };
439
+ const actor =
440
+ mutation.kind !== 'delete' && record.snapshot.allocation.objectKind === 'actor'
441
+ ? decodeActorAuthorityPayload(
442
+ serviceRelocationAuthorityApplicationPayload(mutation.payload)
443
+ )
444
+ : undefined;
445
+ const previousActor =
446
+ actor === undefined
447
+ ? undefined
448
+ : decodeActorAuthorityPayload(
449
+ serviceRelocationAuthorityApplicationPayload(record.snapshot.payload)
450
+ );
451
+ const membershipConditions =
452
+ actor?.currentSpotKind === ZLinkSpotKind.User &&
453
+ (previousActor?.currentSpotKind !== ZLinkSpotKind.User ||
454
+ previousActor.currentSpotId !== actor.currentSpotId ||
455
+ previousActor.currentSpotGeneration !== actor.currentSpotGeneration)
456
+ ? await this.userSpotMembershipConditions(
457
+ actor.currentSpotId,
458
+ actor.currentSpotGeneration,
459
+ signal,
460
+ {
461
+ ownerId: record.snapshot.ownerId,
462
+ leaseGeneration: record.snapshot.ownerLeaseGeneration
463
+ }
464
+ )
465
+ : [];
466
+ if (membershipConditions === undefined) return { kind: 'conflict', current: snapshot };
432
467
  const result = await this.provider.write(
433
468
  {
434
469
  conditions: [
435
470
  { kind: 'version', key: rowKey, expected: current.value.version },
471
+ ...membershipConditions,
436
472
  ...(mutation.kind === 'restore'
437
473
  ? []
438
474
  : [
@@ -945,7 +981,7 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
945
981
  ) {
946
982
  throw new Error('Aggregate participant staging checksum does not match inventory.');
947
983
  }
948
- authorityRows.push({ key: rowKeyForParticipant, current, record, entry });
984
+ authorityRows.push({ key: rowKeyForParticipant, current, record, entry, payload });
949
985
  }
950
986
 
951
987
  const targetDescriptorKey = meshKey(
@@ -1024,12 +1060,52 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1024
1060
  bytes: encodeJson(capacity)
1025
1061
  });
1026
1062
  }
1063
+ const membershipConditions: ZLinkStoreCondition[] = [];
1064
+ for (const row of authorityRows) {
1065
+ if (row.record.snapshot.allocation.objectKind !== 'actor') continue;
1066
+ const actor = decodeActorAuthorityPayload(
1067
+ serviceRelocationAuthorityApplicationPayload(row.payload)
1068
+ );
1069
+ if (actor?.currentSpotKind !== ZLinkSpotKind.User) continue;
1070
+ const spotKey = encodeAuthorityKey('user_spot', actor.currentSpotId);
1071
+ const participantSpot = authorityRows.find(
1072
+ (candidate) => candidate.entry.authorityKey === spotKey.value
1073
+ );
1074
+ if (participantSpot !== undefined) {
1075
+ if (
1076
+ participantSpot.record.snapshot.allocation.objectKind !== 'user_spot' ||
1077
+ participantSpot.record.snapshot.objectGeneration !== actor.currentSpotGeneration
1078
+ )
1079
+ return { kind: 'stale' };
1080
+ continue;
1081
+ }
1082
+ if (
1083
+ membershipConditions.some(
1084
+ (condition) => condition.key.value === authorityKey(spotKey.value).value
1085
+ )
1086
+ )
1087
+ continue;
1088
+ const conditions = await this.userSpotMembershipConditions(
1089
+ actor.currentSpotId,
1090
+ actor.currentSpotGeneration,
1091
+ signal,
1092
+ aggregate.targetOwner
1093
+ );
1094
+ if (conditions === undefined) return { kind: 'stale' };
1095
+ membershipConditions.push(
1096
+ ...conditions.filter(
1097
+ (condition) =>
1098
+ !membershipConditions.some((existing) => existing.key.value === condition.key.value)
1099
+ )
1100
+ );
1101
+ }
1027
1102
  const published = await this.provider.write(
1028
1103
  {
1029
1104
  conditions: [
1030
1105
  { kind: 'version', key: rowKey, expected: aggregateRead.value.version },
1031
1106
  versionCondition(targetDescriptorKey, descriptorRead),
1032
1107
  leaseValueCondition(targetLeaseKey, aggregate.targetOwner),
1108
+ ...membershipConditions,
1033
1109
  ...[...capacityReads.entries()].map(([value, read]) => ({
1034
1110
  kind: 'version' as const,
1035
1111
  key: capacityKeys.get(value)!,
@@ -1128,10 +1204,12 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1128
1204
  override async releaseEndedReservation(
1129
1205
  key: ZLinkAuthorityKey,
1130
1206
  expectedStoreVersion: string,
1131
- signal?: AbortSignal
1207
+ signal?: AbortSignal,
1208
+ request?: ZLinkEndedOwnerReleaseIntent
1132
1209
  ): Promise<boolean> {
1133
1210
  return (
1134
- (await this.tryReleaseEndedReservation(key, expectedStoreVersion, signal)) === 'reclaimed'
1211
+ (await this.tryReleaseEndedReservation(key, expectedStoreVersion, signal, request)) ===
1212
+ 'reclaimed'
1135
1213
  );
1136
1214
  }
1137
1215
 
@@ -1139,7 +1217,7 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1139
1217
  key: ZLinkAuthorityKey,
1140
1218
  expectedStoreVersion: string,
1141
1219
  signal?: AbortSignal,
1142
- request?: ZLinkObjectReserveRequest
1220
+ request?: ZLinkEndedOwnerReleaseIntent
1143
1221
  ): Promise<'notReclaimable' | 'conflict' | 'reclaimed'> {
1144
1222
  const rowKey = authorityKey(key.value);
1145
1223
  const current = await this.provider.read(rowKey, signal);
@@ -1148,11 +1226,9 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1148
1226
  const record = decodeAuthorityRecord(current.value.bytes);
1149
1227
  const snapshot = record.snapshot;
1150
1228
  if (
1151
- (snapshot.allocation.state === 'active'
1152
- ? request?.key.kind !== 'actor'
1153
- : snapshot.pendingCreation === undefined) ||
1154
- record.aggregate !== undefined ||
1155
- hasRelocationAuthority(snapshot.payload)
1229
+ snapshot.allocation.state === 'active'
1230
+ ? request === undefined
1231
+ : snapshot.pendingCreation === undefined
1156
1232
  )
1157
1233
  return 'notReclaimable';
1158
1234
  const leaseKey = ownerKey(snapshot.ownerId);
@@ -1174,10 +1250,60 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1174
1250
  const ownerLive = sameLiveOwner(lease, snapshot);
1175
1251
  if (snapshot.allocation.state === 'active') {
1176
1252
  if (ownerLive) return 'notReclaimable';
1177
- if (request?.actorRelocationPolicy !== 'disabled')
1253
+ let unavailable = record.aggregate !== undefined || hasRelocationAuthority(snapshot.payload);
1254
+ if (snapshot.allocation.objectKind !== 'actor') {
1255
+ const spot = decodeServiceReadySpotAuthority(snapshot.payload);
1256
+ unavailable ||=
1257
+ spot === undefined ||
1258
+ spot.kind !== request?.key.kind ||
1259
+ spot.stableType !== request.intent.stableType ||
1260
+ spot.spotId !== request.key.globalId ||
1261
+ spot.activationRecovery !== undefined;
1262
+ if (spot?.kind === 'user_spot') {
1263
+ // The snapshot starts only after the old owner's lease was found invalid.
1264
+ for (const prefix of [`${PREFIX}actor:`, `${AUTHORITY_PREIMAGE_PREFIX}actor\0`]) {
1265
+ membershipSnapshot: for (;;) {
1266
+ let snapshotHasMember = false;
1267
+ let cursor: ZLinkStoreScanCursor | undefined;
1268
+ do {
1269
+ const page = await this.provider.scan(
1270
+ { prefix, cursor, limit: ZLINK_PROVIDER_MAX_PAGE_SIZE },
1271
+ signal
1272
+ );
1273
+ if (page.kind === 'expired') continue membershipSnapshot;
1274
+ for (const item of page.value.items) {
1275
+ if (prefix === `${PREFIX}actor:`) {
1276
+ const actor = normalizeActor(
1277
+ decodeJson<DescriptorRecord<ZLinkActorLocation>>(item.value.bytes).descriptor
1278
+ );
1279
+ snapshotHasMember ||=
1280
+ actor.spotKind === ZLinkSpotKind.User &&
1281
+ String(actor.spotId) === spot.spotId &&
1282
+ actor.spotGeneration === snapshot.objectGeneration;
1283
+ } else {
1284
+ const authority = decodeAuthorityRecord(item.value.bytes).snapshot;
1285
+ if (authority.allocation.objectKind !== 'actor') continue;
1286
+ const actor = decodeActorAuthorityPayload(
1287
+ serviceRelocationAuthorityApplicationPayload(authority.payload)
1288
+ );
1289
+ snapshotHasMember ||=
1290
+ actor?.currentSpotKind === ZLinkSpotKind.User &&
1291
+ actor.currentSpotId === spot.spotId &&
1292
+ actor.currentSpotGeneration === snapshot.objectGeneration;
1293
+ }
1294
+ }
1295
+ cursor = page.value.nextCursor;
1296
+ } while (cursor !== undefined);
1297
+ unavailable ||= snapshotHasMember;
1298
+ break;
1299
+ }
1300
+ }
1301
+ }
1302
+ }
1303
+ if (unavailable || request?.actorRelocationPolicy !== 'disabled')
1178
1304
  throw new ZLinkFrameworkException(
1179
1305
  ZLinkFrameworkErrorKind.Unavailable,
1180
- 'Actor owner lease is unavailable.'
1306
+ 'Object owner lease is unavailable.'
1181
1307
  );
1182
1308
  } else if (
1183
1309
  ownerLive &&
@@ -1187,6 +1313,11 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1187
1313
  ).lifecycleGeneration === snapshot.allocation.descriptorLifecycleGeneration
1188
1314
  )
1189
1315
  return 'notReclaimable';
1316
+ if (
1317
+ snapshot.allocation.state !== 'active' &&
1318
+ (record.aggregate !== undefined || hasRelocationAuthority(snapshot.payload))
1319
+ )
1320
+ return 'notReclaimable';
1190
1321
  const mutations: ZLinkStoreWriteRequest['mutations'][number][] = [
1191
1322
  { kind: 'delete', key: rowKey }
1192
1323
  ];
@@ -1601,6 +1732,22 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1601
1732
  'Creation terminal expiry must be the live operation deadline plus five minutes.'
1602
1733
  );
1603
1734
  }
1735
+ const actor =
1736
+ request.completion.kind === 'created'
1737
+ ? decodeActorAuthorityPayload(
1738
+ serviceRelocationAuthorityApplicationPayload(request.completion.readyPayload)
1739
+ )
1740
+ : undefined;
1741
+ const membershipConditions =
1742
+ actor?.currentSpotKind === ZLinkSpotKind.User
1743
+ ? await this.userSpotMembershipConditions(
1744
+ actor.currentSpotId,
1745
+ actor.currentSpotGeneration,
1746
+ signal,
1747
+ request.target.owner
1748
+ )
1749
+ : [];
1750
+ if (membershipConditions === undefined) return { kind: 'stale' };
1604
1751
  const mutations =
1605
1752
  request.completion.kind === 'created'
1606
1753
  ? [
@@ -1642,6 +1789,7 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1642
1789
  conditions: [
1643
1790
  { kind: 'version', key: rowKey, expected: current.value.version },
1644
1791
  { kind: 'missing', key: terminalRowKey },
1792
+ ...membershipConditions,
1645
1793
  versionCondition(descriptorKey, descriptorRead),
1646
1794
  leaseValueCondition(leaseKey, request.target.owner),
1647
1795
  conditionFor(capacityRowKey, capacityRead)
@@ -2073,7 +2221,8 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2073
2221
  actorKey(location.meshName, location.actorId),
2074
2222
  normalizeActor(location),
2075
2223
  intent,
2076
- signal
2224
+ signal,
2225
+ location
2077
2226
  );
2078
2227
  }
2079
2228
 
@@ -2615,11 +2764,48 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2615
2764
  };
2616
2765
  }
2617
2766
 
2767
+ private async userSpotMembershipConditions(
2768
+ spotId: string,
2769
+ objectGeneration: bigint,
2770
+ signal?: AbortSignal,
2771
+ conditionedOwner?: ZLinkLocationOwnerToken
2772
+ ): Promise<ZLinkStoreCondition[] | undefined> {
2773
+ const key = authorityKey(encodeAuthorityKey('user_spot', spotId).value);
2774
+ const read = await this.provider.read(key, signal);
2775
+ if (read.kind === 'missing') return undefined;
2776
+ const record = decodeAuthorityRecord(read.value.bytes);
2777
+ const snapshot = record.snapshot;
2778
+ if (
2779
+ record.aggregate !== undefined ||
2780
+ snapshot.allocation.objectKind !== 'user_spot' ||
2781
+ snapshot.allocation.state !== 'active' ||
2782
+ snapshot.objectGeneration !== objectGeneration ||
2783
+ decodeServiceReadySpotAuthority(snapshot.payload)?.kind !== 'user_spot'
2784
+ )
2785
+ return undefined;
2786
+ const leaseKey = ownerKey(snapshot.ownerId);
2787
+ const lease = await this.provider.read(leaseKey, signal);
2788
+ if (!sameLiveOwner(lease, snapshot)) return undefined;
2789
+ return [
2790
+ { kind: 'version', key, expected: read.value.version },
2791
+ ...(conditionedOwner?.ownerId === snapshot.ownerId &&
2792
+ conditionedOwner.leaseGeneration === snapshot.ownerLeaseGeneration
2793
+ ? []
2794
+ : [
2795
+ leaseValueCondition(leaseKey, {
2796
+ ownerId: snapshot.ownerId,
2797
+ leaseGeneration: snapshot.ownerLeaseGeneration
2798
+ })
2799
+ ])
2800
+ ];
2801
+ }
2802
+
2618
2803
  private async updateOwnedLocation<T extends LeaseOwnedLocation>(
2619
2804
  rowKey: ZLinkStoreKey,
2620
2805
  location: T,
2621
2806
  intent: ZLinkLocationWriteIntent,
2622
- signal?: AbortSignal
2807
+ signal?: AbortSignal,
2808
+ membership?: Pick<ZLinkActorLocation, 'spotKind' | 'spotId' | 'spotGeneration'>
2623
2809
  ): Promise<ZLinkLocationWriteResult> {
2624
2810
  const leaseKey = ownerKey(location.ownerId);
2625
2811
  const [lease, current] = await Promise.all([
@@ -2673,6 +2859,24 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2673
2859
  return rejected(lease.value.storeNow);
2674
2860
  }
2675
2861
 
2862
+ const previousMembership =
2863
+ current.kind === 'found' && membership !== undefined
2864
+ ? decodeJson<DescriptorRecord<ZLinkActorLocation>>(current.value.bytes).descriptor
2865
+ : undefined;
2866
+ const membershipConditions =
2867
+ membership?.spotKind === ZLinkSpotKind.User &&
2868
+ (previousMembership?.spotKind !== ZLinkSpotKind.User ||
2869
+ String(previousMembership.spotId) !== String(membership.spotId) ||
2870
+ BigInt(previousMembership.spotGeneration) !== membership.spotGeneration)
2871
+ ? await this.userSpotMembershipConditions(
2872
+ String(membership.spotId),
2873
+ membership.spotGeneration,
2874
+ signal,
2875
+ { ownerId: location.ownerId, leaseGeneration: locationOwnerGeneration(location) }
2876
+ )
2877
+ : [];
2878
+ if (membershipConditions === undefined) return rejected(lease.value.storeNow);
2879
+
2676
2880
  const result = await this.provider.write(
2677
2881
  {
2678
2882
  conditions: [
@@ -2680,7 +2884,8 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2680
2884
  ownerId: location.ownerId,
2681
2885
  leaseGeneration: locationOwnerGeneration(location)
2682
2886
  }),
2683
- rowCondition
2887
+ rowCondition,
2888
+ ...membershipConditions
2684
2889
  ],
2685
2890
  mutations: [
2686
2891
  {
@@ -26,7 +26,11 @@ import {
26
26
  type ResolvedSpotHandle
27
27
  } from '../spots/spot-handle';
28
28
 
29
- import type { ZLinkSpotRouteResolver, ZLinkSpotRouteTarget } from '../spots/spot-routing-internal';
29
+ import type {
30
+ ZLinkSpotRouteResolver,
31
+ ZLinkSpotRouteTarget,
32
+ ZLinkInstanceReactivationRoute
33
+ } from '../spots/spot-routing-internal';
30
34
  import { encodeAuthorityKey } from './authority-key-codec';
31
35
  import { isKnownZLinkLocationAutoConnectType, isKnownZLinkLocationRole } from './canonical-codec';
32
36
  import {
@@ -951,7 +955,17 @@ export class ZLinkAuthoritySpotRouteResolver implements ZLinkSpotRouteResolver {
951
955
  ) => Promise<ZLinkFrameworkRuntimeState | undefined>
952
956
  ) {}
953
957
 
954
- async resolve(spotId: RoutingId, signal?: AbortSignal): Promise<ZLinkSpotRouteTarget> {
958
+ resolve(
959
+ spotId: RoutingId,
960
+ signal: AbortSignal | undefined,
961
+ instanceIntent: true
962
+ ): Promise<ZLinkSpotRouteTarget | ZLinkInstanceReactivationRoute>;
963
+ resolve(spotId: RoutingId, signal?: AbortSignal): Promise<ZLinkSpotRouteTarget>;
964
+ async resolve(
965
+ spotId: RoutingId,
966
+ signal?: AbortSignal,
967
+ instanceIntent?: true
968
+ ): Promise<ZLinkSpotRouteTarget | ZLinkInstanceReactivationRoute> {
955
969
  const key = String(spotId);
956
970
  const prepared = await this.lane.run(() => this.beginResolutionCore(key));
957
971
  try {
@@ -980,6 +994,15 @@ export class ZLinkAuthoritySpotRouteResolver implements ZLinkSpotRouteResolver {
980
994
  signal
981
995
  );
982
996
  if (remainingLeaseMs !== undefined && remainingLeaseMs <= 0) {
997
+ if (instanceIntent === true && ready?.kind === 'instance_spot') {
998
+ return {
999
+ kind: 'coldActivation',
1000
+ spotId,
1001
+ spotKind: ZLinkSpotKind.Instance,
1002
+ meshName: ready.ownerMeshName,
1003
+ stableType: ready.stableType
1004
+ };
1005
+ }
983
1006
  throw new ZLinkFrameworkException(
984
1007
  ZLinkFrameworkErrorKind.Unavailable,
985
1008
  `SPOT '${spotId}' Ready authority owner lease is unavailable.`
@@ -18,13 +18,13 @@ export interface ZLinkSubmitResult {
18
18
  readonly status: ZLinkSubmitStatus;
19
19
  }
20
20
 
21
- /** Projects immediate refusal and pending admission failure from their Core phase. */
21
+ /** Projects typed submit failure; capacity refusal has no admission wait token. */
22
22
  export function submitToRequestResult(result: number, phase: 'submit' | 'completion'): number {
23
23
  switch (result) {
24
24
  case SubmitResult.Ok:
25
25
  return RequestResult.Ok;
26
26
  case SubmitResult.Backpressured:
27
- return phase === 'submit' ? RequestResult.NotConnected : RequestResult.TimedOut;
27
+ return RequestResult.NotConnected;
28
28
  case SubmitResult.NotConnected:
29
29
  return RequestResult.NotConnected;
30
30
  case SubmitResult.NotFound:
@@ -1,3 +1,4 @@
1
+ import type { ApplicationJobOrigin } from '../application-jobs/contracts';
1
2
  import type { ActorRef, RoutingId, ZLinkActor, ZLinkActorJoinOperationId } from '../../contracts';
2
3
  import type { ZLinkRemoteBoundSessionTarget } from '../actors/actor-runtime-state';
3
4
  import type { ZLinkDeferredJoinCompletion } from '../actors/deferred-join-completion';
@@ -13,6 +14,7 @@ const ADMISSION_RETENTION_MS = 30_000;
13
14
  * how long the underlying buffer stays valid.
14
15
  */
15
16
  export interface ZLinkParkedActorArrival {
17
+ readonly origin: ApplicationJobOrigin;
16
18
  readonly header: Buffer;
17
19
  readonly payload: Buffer;
18
20
  readonly returnResponse: boolean;
@@ -1,3 +1,4 @@
1
+ import type { ApplicationJobOrigin } from '../application-jobs/contracts';
1
2
  import { dispatchReasonFromError } from '../diagnostics/dispatch-error-details';
2
3
  import type {
3
4
  ActorRef,
@@ -190,6 +191,7 @@ export interface ZLinkSpotManagerOptions {
190
191
  ): Promise<void>;
191
192
  };
192
193
  readonly dispatchEntryActorPacket?: (
194
+ origin: ApplicationJobOrigin,
193
195
  actorId: string,
194
196
  parts: readonly Message[],
195
197
  returnResponse?: boolean,
@@ -1532,6 +1534,7 @@ export class DefaultZLinkSpotManager {
1532
1534
  }
1533
1535
 
1534
1536
  dispatchRoutedActorPacket(
1537
+ origin: ApplicationJobOrigin,
1535
1538
  spotId: RoutingId,
1536
1539
  actorId: string,
1537
1540
  parts: readonly Message[],
@@ -1544,6 +1547,7 @@ export class DefaultZLinkSpotManager {
1544
1547
  throw new ZLinkConfigurationException(`Spot '${spotId}' is not active.`);
1545
1548
  }
1546
1549
  return this.dispatchActorPacket(
1550
+ origin,
1547
1551
  activation,
1548
1552
  actorId,
1549
1553
  parts,
@@ -1555,6 +1559,7 @@ export class DefaultZLinkSpotManager {
1555
1559
 
1556
1560
  /** @internal Replays a record already owned by the Actor handoff queue. */
1557
1561
  dispatchRoutedActorPacketDirect(
1562
+ origin: ApplicationJobOrigin,
1558
1563
  spotId: RoutingId,
1559
1564
  actorId: string,
1560
1565
  parts: readonly Message[],
@@ -1567,6 +1572,7 @@ export class DefaultZLinkSpotManager {
1567
1572
  throw new ZLinkConfigurationException(`Spot '${spotId}' is not active.`);
1568
1573
  }
1569
1574
  return this.activationLifecycle.dispatchActorPacketDirect(
1575
+ origin,
1570
1576
  activation,
1571
1577
  actorId,
1572
1578
  parts,
@@ -1965,6 +1971,7 @@ export class DefaultZLinkSpotManager {
1965
1971
  }
1966
1972
 
1967
1973
  async dispatchMeshActor(
1974
+ origin: ApplicationJobOrigin,
1968
1975
  meshName: string,
1969
1976
  owner: ReadyRecord,
1970
1977
  record: ReceiveRecord
@@ -2027,6 +2034,7 @@ export class DefaultZLinkSpotManager {
2027
2034
  }
2028
2035
  const response = targetsEntrySpot
2029
2036
  ? await this.options.dispatchEntryActorPacket?.(
2037
+ origin,
2030
2038
  actor.actorId,
2031
2039
  record.parts,
2032
2040
  request,
@@ -2036,6 +2044,7 @@ export class DefaultZLinkSpotManager {
2036
2044
  record.messageFollowOrigin
2037
2045
  )
2038
2046
  : await this.dispatchMeshActorPacket(
2047
+ origin,
2039
2048
  meshName,
2040
2049
  spotId,
2041
2050
  actor.actorId,
@@ -2693,6 +2702,7 @@ export class DefaultZLinkSpotManager {
2693
2702
  );
2694
2703
  }
2695
2704
  return await this.options.dispatchEntryActorPacket(
2705
+ 'remote',
2696
2706
  entryActor.context.actorId,
2697
2707
  parts,
2698
2708
  returnResponse,
@@ -2922,6 +2932,7 @@ export class DefaultZLinkSpotManager {
2922
2932
  pendingTransfer.handoffBacklog,
2923
2933
  (parts, returnResponse, remoteBoundSessionTarget, _fallbackActorRef) =>
2924
2934
  this.dispatchActorPacket(
2935
+ 'remote',
2925
2936
  activation,
2926
2937
  actor.context.actorId,
2927
2938
  parts,
@@ -3271,6 +3282,7 @@ export class DefaultZLinkSpotManager {
3271
3282
  }
3272
3283
 
3273
3284
  private async dispatchActorPacket(
3285
+ origin: ApplicationJobOrigin,
3274
3286
  activation: ZLinkSpotActivation,
3275
3287
  actorId: string,
3276
3288
  parts: readonly Message[],
@@ -3281,6 +3293,7 @@ export class DefaultZLinkSpotManager {
3281
3293
  messageFollowOrigin?: ZLinkMessageFollowOrigin
3282
3294
  ): Promise<unknown> {
3283
3295
  return await this.activationLifecycle.dispatchActorPacket(
3296
+ origin,
3284
3297
  activation,
3285
3298
  actorId,
3286
3299
  parts,
@@ -3311,6 +3324,7 @@ export class DefaultZLinkSpotManager {
3311
3324
  for (const arrival of drained) {
3312
3325
  try {
3313
3326
  const response = await this.dispatchActorPacket(
3327
+ arrival.origin,
3314
3328
  activation,
3315
3329
  actor.context.actorId,
3316
3330
  [RuntimeMessage.from(arrival.header), RuntimeMessage.from(arrival.payload)],
@@ -3327,6 +3341,7 @@ export class DefaultZLinkSpotManager {
3327
3341
  }
3328
3342
 
3329
3343
  private async dispatchMeshActorPacket(
3344
+ origin: ApplicationJobOrigin,
3330
3345
  meshName: string,
3331
3346
  spotId: RoutingId,
3332
3347
  actorId: string,
@@ -3343,6 +3358,7 @@ export class DefaultZLinkSpotManager {
3343
3358
  );
3344
3359
  }
3345
3360
  return await this.dispatchActorPacket(
3361
+ origin,
3346
3362
  activation,
3347
3363
  actorId,
3348
3364
  parts,
@@ -1,3 +1,4 @@
1
+ import type { ApplicationJobOrigin } from '../application-jobs/contracts';
1
2
  import type {
2
3
  ActorRef,
3
4
  RoutingId,
@@ -857,6 +858,7 @@ export class ZLinkSpotActivationLifecycle {
857
858
  }
858
859
 
859
860
  async dispatchActorPacket(
861
+ origin: ApplicationJobOrigin,
860
862
  activation: ZLinkSpotActivation,
861
863
  actorId: string,
862
864
  parts: readonly Message[],
@@ -867,6 +869,7 @@ export class ZLinkSpotActivationLifecycle {
867
869
  messageFollowOrigin?: ZLinkMessageFollowOrigin
868
870
  ): Promise<unknown> {
869
871
  return await this.actorAdmission.dispatchActorPacket(
872
+ origin,
870
873
  activation,
871
874
  actorId,
872
875
  parts,
@@ -880,6 +883,7 @@ export class ZLinkSpotActivationLifecycle {
880
883
 
881
884
  /** @internal Dispatches a held Actor record after its handoff owner releases it. */
882
885
  dispatchActorPacketDirect(
886
+ origin: ApplicationJobOrigin,
883
887
  activation: ZLinkSpotActivation,
884
888
  actorId: string,
885
889
  parts: readonly Message[],
@@ -888,6 +892,7 @@ export class ZLinkSpotActivationLifecycle {
888
892
  fallbackActorRef?: ActorRef
889
893
  ): Promise<unknown> {
890
894
  return this.actorAdmission.dispatchActorPacketDirect(
895
+ origin,
891
896
  activation,
892
897
  actorId,
893
898
  parts,
@@ -1,3 +1,5 @@
1
+ import { currentApplicationJobOrigin } from '../application-jobs/application-job-queue-scope';
2
+ import type { ApplicationJobOrigin } from '../application-jobs/contracts';
1
3
  import type { ActorRef, ZLinkActor } from '../../contracts';
2
4
  import type { Message } from '../../contracts/Common/Message';
3
5
  import type { ZLinkMessageFollowOrigin } from '../foundation/service-runtime-contracts';
@@ -39,6 +41,7 @@ export class ZLinkSpotActorAdmissionCoordinator {
39
41
  constructor(private readonly options: AdmissionOptions) {}
40
42
 
41
43
  async dispatchActorPacket(
44
+ origin: ApplicationJobOrigin,
42
45
  activation: ZLinkSpotActivation,
43
46
  actorId: string,
44
47
  parts: readonly Message[],
@@ -49,6 +52,7 @@ export class ZLinkSpotActorAdmissionCoordinator {
49
52
  messageFollowOrigin?: ZLinkMessageFollowOrigin
50
53
  ): Promise<unknown> {
51
54
  const handoff = this.options.actorHandoffRuntime?.capture(
55
+ origin,
52
56
  actorId,
53
57
  parts,
54
58
  returnResponse,
@@ -63,6 +67,7 @@ export class ZLinkSpotActorAdmissionCoordinator {
63
67
  replayFallbackActorRef
64
68
  ) =>
65
69
  this.dispatchActorPacketDirect(
70
+ origin,
66
71
  activation,
67
72
  actorId,
68
73
  replayedParts,
@@ -73,6 +78,7 @@ export class ZLinkSpotActorAdmissionCoordinator {
73
78
  );
74
79
  if (handoff !== undefined) return await handoff;
75
80
  return await this.dispatchActorPacketDirect(
81
+ origin,
76
82
  activation,
77
83
  actorId,
78
84
  parts,
@@ -116,6 +122,7 @@ export class ZLinkSpotActorAdmissionCoordinator {
116
122
  packets: {
117
123
  handle: (delivery) =>
118
124
  this.dispatchActorPacket(
125
+ 'remote',
119
126
  activation,
120
127
  delivery.actorId,
121
128
  delivery.parts,
@@ -149,6 +156,7 @@ export class ZLinkSpotActorAdmissionCoordinator {
149
156
 
150
157
  /** @internal Dispatches a coordinator-owned replay without recapturing it. */
151
158
  dispatchActorPacketDirect(
159
+ origin: ApplicationJobOrigin,
152
160
  activation: ZLinkSpotActivation,
153
161
  actorId: string,
154
162
  parts: readonly Message[],
@@ -159,6 +167,7 @@ export class ZLinkSpotActorAdmissionCoordinator {
159
167
  ): Promise<unknown> {
160
168
  return activation.executeActor(actorId, (actorSerial) =>
161
169
  this.dispatchActorPacketInActorTurn(
170
+ origin,
162
171
  activation,
163
172
  actorSerial,
164
173
  actorId,
@@ -190,6 +199,7 @@ export class ZLinkSpotActorAdmissionCoordinator {
190
199
  record.drain((parts, returnResponse, remoteBoundSessionTarget, fallbackActorRef) =>
191
200
  executeChild((actorSerial) =>
192
201
  this.dispatchActorPacketInActorTurn(
202
+ currentApplicationJobOrigin(),
193
203
  activation,
194
204
  actorSerial,
195
205
  actorId,
@@ -217,6 +227,7 @@ export class ZLinkSpotActorAdmissionCoordinator {
217
227
  }
218
228
 
219
229
  private dispatchActorPacketInActorTurn(
230
+ origin: ApplicationJobOrigin,
220
231
  activation: ZLinkSpotActivation,
221
232
  actorSerial: ZLinkSpotSerialTurnExecutor,
222
233
  actorId: string,
@@ -250,6 +261,7 @@ export class ZLinkSpotActorAdmissionCoordinator {
250
261
  dispatchErrors: this.options.dispatchErrors,
251
262
  routeToActorJoinPrewarm: this.options.routeToActorJoinPrewarm
252
263
  }).dispatch(
264
+ origin,
253
265
  actorId,
254
266
  parts,
255
267
  returnResponse,
@@ -305,6 +317,7 @@ export class ZLinkSpotActorAdmissionCoordinator {
305
317
  backlog,
306
318
  (parts, returnResponse, remoteBoundSessionTarget, fallbackActorRef) =>
307
319
  this.dispatchActorPacketDirect(
320
+ 'remote',
308
321
  activation,
309
322
  actor.context.actorId,
310
323
  parts,