@zlink-systems/framework 0.11.0 → 0.12.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 (119) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Configuration/EndpointNotation.d.ts +5 -4
  3. package/dist/contracts/Configuration/EndpointNotation.js +5 -4
  4. package/dist/contracts/Configuration/InternalDefaults.d.ts +0 -1
  5. package/dist/contracts/Configuration/InternalDefaults.js +1 -2
  6. package/dist/contracts/Configuration/RegistrationNormalizers.js +1 -2
  7. package/dist/contracts/Configuration/RegistrationTypes.d.ts +2 -5
  8. package/dist/contracts/Configuration/RegistrationValidators.js +0 -1
  9. package/dist/contracts/Errors/ZLinkFrameworkException.d.ts +6 -7
  10. package/dist/contracts/Errors/ZLinkFrameworkException.js +6 -7
  11. package/dist/runtime/actors/actor-client.js +1 -1
  12. package/dist/runtime/actors/actor-mailbox.js +1 -5
  13. package/dist/runtime/backend/contracts/index.d.ts +2 -0
  14. package/dist/runtime/backend/mesh-completion-table.d.ts +1 -3
  15. package/dist/runtime/backend/mesh-completion-table.js +11 -15
  16. package/dist/runtime/backend/mesh-dispatch-pump.d.ts +11 -4
  17. package/dist/runtime/backend/mesh-dispatch-pump.js +201 -134
  18. package/dist/runtime/backend/node/node-backend-adapter-factory.d.ts +3 -4
  19. package/dist/runtime/backend/node/node-backend-adapter-factory.js +51 -37
  20. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +2 -2
  21. package/dist/runtime/backend/node/node-backend-adapter-support.js +2 -2
  22. package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +2 -2
  23. package/dist/runtime/backend/node/node-event-loop-poller.d.ts +16 -0
  24. package/dist/runtime/backend/node/node-event-loop-poller.js +49 -0
  25. package/dist/runtime/backend/node/node-raw-binding-port.js +24 -41
  26. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +8 -8
  27. package/dist/runtime/backend/node/node-raw-mesh-backend.js +100 -107
  28. package/dist/runtime/backend/node/node-socket-backend-adapter.d.ts +3 -2
  29. package/dist/runtime/backend/node/node-socket-backend-adapter.js +18 -67
  30. package/dist/runtime/backend/raw-binding-port.d.ts +1 -0
  31. package/dist/runtime/backend/runtime-values.d.ts +5 -1
  32. package/dist/runtime/channels/channel-dispatchers.js +2 -2
  33. package/dist/runtime/channels/channel-envelope.js +324 -121
  34. package/dist/runtime/channels/channel-multipart.d.ts +8 -1
  35. package/dist/runtime/channels/channel-outbound-operations.js +0 -2
  36. package/dist/runtime/channels/channel-receive-loops.d.ts +1 -3
  37. package/dist/runtime/channels/channel-receive-loops.js +25 -65
  38. package/dist/runtime/channels/channel-socket-registry.js +3 -1
  39. package/dist/runtime/channels/channel-transports.d.ts +2 -1
  40. package/dist/runtime/channels/channel-transports.js +12 -10
  41. package/dist/runtime/execution/serial-execution-queue.d.ts +5 -18
  42. package/dist/runtime/execution/serial-execution-queue.js +16 -80
  43. package/dist/runtime/foundation/event-loop-resources.d.ts +0 -3
  44. package/dist/runtime/foundation/event-loop-resources.js +2 -11
  45. package/dist/runtime/foundation/operation-registry.d.ts +2 -8
  46. package/dist/runtime/foundation/operation-registry.js +3 -21
  47. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +19 -7
  48. package/dist/runtime/foundation/raw-service-mesh-runtime.js +56 -10
  49. package/dist/runtime/foundation/service-mailbox.d.ts +1 -7
  50. package/dist/runtime/foundation/service-mailbox.js +3 -2
  51. package/dist/runtime/foundation/service-runtime-contracts.d.ts +2 -1
  52. package/dist/runtime/foundation/service-stateful-registry.d.ts +1 -1
  53. package/dist/runtime/foundation/service-stateful-registry.js +2 -2
  54. package/dist/runtime/foundation/service-stateful-runtime.js +14 -14
  55. package/dist/runtime/framework-errors-internal.d.ts +4 -4
  56. package/dist/runtime/framework-errors-internal.js +8 -10
  57. package/dist/runtime/host/actor-placement-coordinator.js +1 -1
  58. package/dist/runtime/host/index.js +1 -1
  59. package/dist/runtime/host/service-relocation-host-runtime.js +0 -4
  60. package/dist/runtime/host/spot-address-transport.js +1 -1
  61. package/dist/runtime/host/user-spot-creation-coordinator.js +1 -1
  62. package/dist/runtime/spots/spot-node-runtime-manager.d.ts +0 -12
  63. package/dist/runtime/spots/spot-node-runtime-manager.js +6 -166
  64. package/dist/runtime/spots/spot-serial-turn-executor.js +1 -4
  65. package/dist/runtime/spots/spot-timer.d.ts +3 -0
  66. package/dist/runtime/spots/spot-timer.js +87 -18
  67. package/dist/runtime/streams/session-serial-executor.js +1 -3
  68. package/dist/runtime/streams/stream-session-runtime.d.ts +2 -2
  69. package/dist/runtime/streams/stream-session-runtime.js +13 -19
  70. package/dist/runtime/workers/index.d.ts +0 -1
  71. package/dist/runtime/workers/index.js +1 -9
  72. package/package.json +3 -3
  73. package/src/contracts/Configuration/EndpointNotation.ts +5 -4
  74. package/src/contracts/Configuration/InternalDefaults.ts +0 -1
  75. package/src/contracts/Configuration/RegistrationNormalizers.ts +2 -4
  76. package/src/contracts/Configuration/RegistrationTypes.ts +2 -5
  77. package/src/contracts/Configuration/RegistrationValidators.ts +0 -1
  78. package/src/contracts/Errors/ZLinkFrameworkException.ts +6 -7
  79. package/src/runtime/actors/actor-client.ts +1 -1
  80. package/src/runtime/actors/actor-mailbox.ts +1 -12
  81. package/src/runtime/backend/contracts/index.ts +2 -0
  82. package/src/runtime/backend/mesh-completion-table.ts +12 -23
  83. package/src/runtime/backend/mesh-dispatch-pump.ts +206 -138
  84. package/src/runtime/backend/node/node-backend-adapter-factory.ts +66 -35
  85. package/src/runtime/backend/node/node-backend-adapter-support.ts +4 -4
  86. package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +2 -2
  87. package/src/runtime/backend/node/node-event-loop-poller.ts +55 -0
  88. package/src/runtime/backend/node/node-raw-binding-port.ts +30 -45
  89. package/src/runtime/backend/node/node-raw-mesh-backend.ts +97 -110
  90. package/src/runtime/backend/node/node-socket-backend-adapter.ts +23 -69
  91. package/src/runtime/backend/raw-binding-port.ts +1 -0
  92. package/src/runtime/backend/runtime-values.ts +7 -1
  93. package/src/runtime/channels/channel-dispatchers.ts +2 -2
  94. package/src/runtime/channels/channel-envelope.ts +379 -109
  95. package/src/runtime/channels/channel-multipart.ts +9 -1
  96. package/src/runtime/channels/channel-outbound-operations.ts +0 -1
  97. package/src/runtime/channels/channel-receive-loops.ts +24 -75
  98. package/src/runtime/channels/channel-socket-registry.ts +4 -1
  99. package/src/runtime/channels/channel-transports.ts +20 -10
  100. package/src/runtime/execution/serial-execution-queue.ts +16 -108
  101. package/src/runtime/foundation/event-loop-resources.ts +2 -11
  102. package/src/runtime/foundation/operation-registry.ts +2 -22
  103. package/src/runtime/foundation/raw-service-mesh-runtime.ts +74 -19
  104. package/src/runtime/foundation/service-mailbox.ts +3 -13
  105. package/src/runtime/foundation/service-runtime-contracts.ts +2 -1
  106. package/src/runtime/foundation/service-stateful-registry.ts +2 -2
  107. package/src/runtime/foundation/service-stateful-runtime.ts +14 -14
  108. package/src/runtime/framework-errors-internal.ts +8 -10
  109. package/src/runtime/host/actor-placement-coordinator.ts +1 -1
  110. package/src/runtime/host/index.ts +2 -1
  111. package/src/runtime/host/service-relocation-host-runtime.ts +0 -4
  112. package/src/runtime/host/spot-address-transport.ts +1 -1
  113. package/src/runtime/host/user-spot-creation-coordinator.ts +1 -1
  114. package/src/runtime/spots/spot-node-runtime-manager.ts +13 -233
  115. package/src/runtime/spots/spot-serial-turn-executor.ts +1 -8
  116. package/src/runtime/spots/spot-timer.ts +109 -18
  117. package/src/runtime/streams/session-serial-executor.ts +1 -7
  118. package/src/runtime/streams/stream-session-runtime.ts +13 -17
  119. package/src/runtime/workers/index.ts +2 -17
@@ -17,7 +17,7 @@ import {
17
17
  } from '../application-jobs/application-ingress-record-owner';
18
18
  import { OperationRegistry, type PendingOperation } from './operation-registry';
19
19
  import { ServiceLivenessRegistry, type ServiceLivenessTick } from './service-liveness-registry';
20
- import { ServiceMailbox, type ServiceMailboxLimits, type ServiceMailboxRecord } from './service-mailbox';
20
+ import { ServiceMailbox, type ServiceMailboxRecord } from './service-mailbox';
21
21
  import {
22
22
  ServiceTopologyRegistry,
23
23
  sameServiceNodeDescriptor,
@@ -28,7 +28,7 @@ import {
28
28
  type ServiceNodeDescriptor
29
29
  } from './service-topology-registry';
30
30
  import {
31
- decodeApplicationPayload,
31
+ decodeApplicationPayloadView,
32
32
  decodeChannelRequestHeader,
33
33
  decodeChannelSendHeader,
34
34
  decodeHeader,
@@ -72,6 +72,9 @@ export interface RawServiceRequestResult {
72
72
  readonly payload?: ServiceApplicationPayload;
73
73
  }
74
74
 
75
+ /** An M6A application frame already owned by the Framework runtime. */
76
+ type ServiceApplicationPayloadInput = ServiceApplicationPayload | Buffer;
77
+
75
78
  export interface RawServiceIngressRecord {
76
79
  readonly command: number;
77
80
  readonly flags: number;
@@ -91,7 +94,6 @@ export type RawServiceIngressHandler = (
91
94
  export interface RawServiceMeshRuntimeOptions {
92
95
  readonly descriptor: ServiceNodeDescriptor;
93
96
  readonly resolveAdvertisedEndpoint?: (boundEndpoint: string) => string;
94
- readonly mailbox?: Partial<ServiceMailboxLimits>;
95
97
  readonly probeIntervalMs?: number;
96
98
  readonly peerTimeoutMs?: number;
97
99
  readonly bindingPort: ZLinkRawBindingPort;
@@ -195,7 +197,7 @@ export class RawServiceMeshRuntime {
195
197
  constructor(options: RawServiceMeshRuntimeOptions) {
196
198
  this.descriptor = options.descriptor;
197
199
  this.topology = new ServiceTopologyRegistry(options.descriptor);
198
- this.mailbox = new ServiceMailbox(undefined, options.onMailboxReady);
200
+ this.mailbox = new ServiceMailbox(options.onMailboxReady);
199
201
  this.liveness = new ServiceLivenessRegistry(options.probeIntervalMs, options.peerTimeoutMs);
200
202
  this.bindingPort = options.bindingPort;
201
203
  if (options.applicationJobQueue === undefined) {
@@ -474,23 +476,24 @@ export class RawServiceMeshRuntime {
474
476
 
475
477
  async sendToNode(
476
478
  targetNodeRoutingId: string,
477
- payload: ServiceApplicationPayload
479
+ payload: ServiceApplicationPayloadInput
478
480
  ): Promise<boolean> {
479
481
  return this.send(
480
482
  targetNodeRoutingId,
481
- [encodeNodeSendHeader(), encodeApplicationPayload(payload)]
483
+ [encodeNodeSendHeader(), this.applicationFrame(payload)]
482
484
  );
483
485
  }
484
486
 
485
487
  async sendToChannel(
486
488
  channelName: string,
487
- payload: ServiceApplicationPayload
489
+ payload: ServiceApplicationPayloadInput
488
490
  ): Promise<boolean> {
489
491
  const selected = this.topology.selectChannel(
490
492
  channelName,
491
493
  peer => this.isLocalOrReadyPeer(peer.descriptor.nodeRoutingId)
492
494
  );
493
495
  if (selected === undefined) return false;
496
+ const applicationFrame = this.applicationFrame(payload);
494
497
  if (selected.descriptor.nodeRoutingId === this.descriptor.nodeRoutingId) {
495
498
  const applicationJobOwner = await this.reserveLocalIngress();
496
499
  try {
@@ -498,7 +501,7 @@ export class RawServiceMeshRuntime {
498
501
  const accepted = this.mailbox.tryEnqueue({
499
502
  owner: `channel:${channelName}`,
500
503
  domain: 'application',
501
- parts: [encodeChannelSendHeader(channelName), encodeApplicationPayload(payload)],
504
+ parts: [encodeChannelSendHeader(channelName), applicationFrame],
502
505
  sourceRoutingId: this.descriptor.nodeRoutingId,
503
506
  applicationJob
504
507
  });
@@ -510,13 +513,13 @@ export class RawServiceMeshRuntime {
510
513
  }
511
514
  return this.send(selected.descriptor.nodeRoutingId, [
512
515
  encodeChannelSendHeader(channelName),
513
- encodeApplicationPayload(payload)
516
+ applicationFrame
514
517
  ]);
515
518
  }
516
519
 
517
520
  requestToNode(
518
521
  targetNodeRoutingId: string,
519
- payload: ServiceApplicationPayload,
522
+ payload: ServiceApplicationPayloadInput,
520
523
  timeoutMs: number
521
524
  ): PendingOperation<RawServiceRequestResult> {
522
525
  return this.requestToTarget(targetNodeRoutingId, payload, timeoutMs);
@@ -524,7 +527,7 @@ export class RawServiceMeshRuntime {
524
527
 
525
528
  requestToChannel(
526
529
  channelName: string,
527
- payload: ServiceApplicationPayload,
530
+ payload: ServiceApplicationPayloadInput,
528
531
  timeoutMs: number
529
532
  ): PendingOperation<RawServiceRequestResult> | undefined {
530
533
  const selected = this.topology.selectChannel(
@@ -595,7 +598,7 @@ export class RawServiceMeshRuntime {
595
598
 
596
599
  reply(
597
600
  request: ServiceMailboxRecord,
598
- payload: ServiceApplicationPayload,
601
+ payload: ServiceApplicationPayloadInput,
599
602
  terminalResult = 0,
600
603
  failureCode = 0
601
604
  ): void {
@@ -603,7 +606,7 @@ export class RawServiceMeshRuntime {
603
606
  request.localReply(
604
607
  terminalResult,
605
608
  failureCode,
606
- terminalResult === 0 ? payload : undefined
609
+ terminalResult === 0 ? this.applicationPayload(payload) : undefined
607
610
  );
608
611
  return;
609
612
  }
@@ -612,7 +615,7 @@ export class RawServiceMeshRuntime {
612
615
  }
613
616
  request.reply([
614
617
  encodeReplyHeader(request.correlation, terminalResult, failureCode),
615
- ...(terminalResult === 0 ? [encodeApplicationPayload(payload)] : [])
618
+ ...(terminalResult === 0 ? [this.applicationFrame(payload)] : [])
616
619
  ]);
617
620
  }
618
621
 
@@ -620,8 +623,44 @@ export class RawServiceMeshRuntime {
620
623
  nowMs = performance.now(),
621
624
  observe?: RawServicePumpObserver
622
625
  ): Promise<RawServicePumpResult> {
623
- const router = this.requireStarted();
624
626
  this.pullMonitorEvents();
627
+ return this.receiveOne(nowMs, observe);
628
+ }
629
+
630
+ setReadableHandler(handler: () => void): void {
631
+ this.requireStarted().setReadableHandler(handler);
632
+ }
633
+
634
+ /** Returns whether a receive budget ended before no-data; idle ticks only maintain peers. */
635
+ async pumpBatch(receiveReady = true): Promise<boolean> {
636
+ await this.drainMonitorEvents();
637
+ const startedAtMs = performance.now();
638
+ let messages = 0;
639
+ let bytes = 0;
640
+ const observe: RawServicePumpObserver = (_source, byteCount) => { bytes += byteCount; };
641
+ while (receiveReady && messages < 64 && !this.closed) {
642
+ const result = await this.receiveOne(performance.now(), observe);
643
+ if (result === 'noData') {
644
+ receiveReady = false;
645
+ break;
646
+ }
647
+ messages += 1;
648
+ // Core owns the per-peer fair-queue cursor. The Framework limit bounds
649
+ // the entire round, so changing peers does not renew its byte/count budget.
650
+ if (bytes >= 4 * 1024 * 1024 || performance.now() - startedAtMs >= 2) break;
651
+ }
652
+ if (!this.closed) {
653
+ await this.announceExpectedPeers();
654
+ await this.tickLiveness();
655
+ }
656
+ return receiveReady && messages > 0;
657
+ }
658
+
659
+ private async receiveOne(
660
+ nowMs: number,
661
+ observe?: RawServicePumpObserver
662
+ ): Promise<RawServicePumpResult> {
663
+ const router = this.requireStarted();
625
664
  let permit: ApplicationJobPermitPort;
626
665
  try {
627
666
  permit = await this.applicationJobQueue.acquire(this.applicationJobStop.signal);
@@ -629,6 +668,10 @@ export class RawServiceMeshRuntime {
629
668
  if (this.closed || this.applicationJobStop.signal.aborted) return 'noData';
630
669
  throw error;
631
670
  }
671
+ if (this.closed) {
672
+ permit.releaseAfterInternalProcessing();
673
+ return 'noData';
674
+ }
632
675
  let received: ZLinkRawReceivedRecord | undefined;
633
676
  try {
634
677
  received = router.receive(true);
@@ -1122,7 +1165,7 @@ export class RawServiceMeshRuntime {
1122
1165
 
1123
1166
  private requestToTarget(
1124
1167
  targetNodeRoutingId: string,
1125
- payload: ServiceApplicationPayload,
1168
+ payload: ServiceApplicationPayloadInput,
1126
1169
  timeoutMs: number,
1127
1170
  channelName?: string
1128
1171
  ): PendingOperation<RawServiceRequestResult> {
@@ -1130,8 +1173,8 @@ export class RawServiceMeshRuntime {
1130
1173
  const header = channelName === undefined
1131
1174
  ? encodeNodeRequestHeader(correlation)
1132
1175
  : encodeChannelRequestHeader(correlation, channelName);
1133
- const encodedPayload = encodeApplicationPayload(payload);
1134
- const pending = this.operations.reserve(timeoutMs);
1176
+ const encodedPayload = this.applicationFrame(payload);
1177
+ const pending = this.operations.register(timeoutMs);
1135
1178
  let selectedTargetNodeRoutingId = targetNodeRoutingId;
1136
1179
  if (
1137
1180
  selectedTargetNodeRoutingId !== this.descriptor.nodeRoutingId
@@ -1263,7 +1306,7 @@ export class RawServiceMeshRuntime {
1263
1306
  this.operations.complete(
1264
1307
  pending.id,
1265
1308
  reply.terminalResult === 0
1266
- ? { ...result, payload: decodeApplicationPayload(replyParts[1]!) }
1309
+ ? { ...result, payload: decodeApplicationPayloadView(replyParts[1]!) }
1267
1310
  : result
1268
1311
  );
1269
1312
  } catch (error) {
@@ -1277,6 +1320,18 @@ export class RawServiceMeshRuntime {
1277
1320
  return pending;
1278
1321
  }
1279
1322
 
1323
+ /**
1324
+ * The generic node/channel backend may supply its single owned M6A frame
1325
+ * directly. All other callers keep the typed application-payload boundary.
1326
+ */
1327
+ private applicationFrame(payload: ServiceApplicationPayloadInput): Buffer {
1328
+ return Buffer.isBuffer(payload) ? payload : encodeApplicationPayload(payload);
1329
+ }
1330
+
1331
+ private applicationPayload(payload: ServiceApplicationPayloadInput): ServiceApplicationPayload {
1332
+ return Buffer.isBuffer(payload) ? decodeApplicationPayloadView(payload) : payload;
1333
+ }
1334
+
1280
1335
  private admitPeer(
1281
1336
  descriptor: ServiceNodeDescriptor,
1282
1337
  connection: PhysicalConnectionCandidate,
@@ -55,14 +55,6 @@ interface DomainState {
55
55
  bytes: number;
56
56
  }
57
57
 
58
- export interface ServiceMailboxLimits {
59
- readonly applicationMessages: number;
60
- readonly applicationBytes: number;
61
- readonly infrastructureMessages: number;
62
- readonly infrastructureBytes: number;
63
- }
64
-
65
-
66
58
  /** Level-triggered queues with one active application claim per owner. */
67
59
  export class ServiceMailbox {
68
60
  private readonly application: DomainState;
@@ -78,10 +70,7 @@ export class ServiceMailbox {
78
70
  private nextRelocationSerial = 1n;
79
71
  private closed = false;
80
72
 
81
- constructor(
82
- _legacyLimits?: ServiceMailboxLimits,
83
- private readonly onReady?: (domain: ServiceMailboxDomain) => void
84
- ) {
73
+ constructor(private readonly onReady?: (domain: ServiceMailboxDomain) => void) {
85
74
  // Host ApplicationJobQueue permits own ordinary-record admission. The
86
75
  // Framework-owned payload lifetime does not retain Core HWM credit. A
87
76
  // second mailbox cap would reject already-admitted work before the shared
@@ -125,8 +114,8 @@ export class ServiceMailbox {
125
114
  if (!queue.claimed && !target.indexed.has(record.owner)) {
126
115
  target.indexed.add(record.owner);
127
116
  target.ready.push(record.owner);
117
+ this.onReady?.(record.domain);
128
118
  }
129
- this.onReady?.(record.domain);
130
119
  return true;
131
120
  }
132
121
 
@@ -186,6 +175,7 @@ export class ServiceMailbox {
186
175
  } else if (!target.indexed.has(claim.owner)) {
187
176
  target.indexed.add(claim.owner);
188
177
  target.ready.push(claim.owner);
178
+ this.onReady?.(claim.domain);
189
179
  }
190
180
  return true;
191
181
  }
@@ -269,7 +269,8 @@ export interface ReadyBatch {
269
269
  }
270
270
 
271
271
  export interface ReceiveBatch {
272
- reset(): void;
272
+ /** Reuses the batch, limiting this receive to the admitted record count. */
273
+ reset(messageCapacity?: number): void;
273
274
  close(): void;
274
275
  }
275
276
 
@@ -619,8 +619,8 @@ export class ServiceTerminalOperationRegistry<T> {
619
619
  this.operations = operations;
620
620
  }
621
621
 
622
- reserve(timeoutMs: number, timeoutOwner: 'registry' | 'sender' = 'registry'): PendingOperation<T> {
623
- return this.operations.reserve(timeoutMs, timeoutOwner);
622
+ register(timeoutMs: number, timeoutOwner: 'registry' | 'sender' = 'registry'): PendingOperation<T> {
623
+ return this.operations.register(timeoutMs, timeoutOwner);
624
624
  }
625
625
 
626
626
  reply(id: bigint, value: T): boolean {
@@ -1240,7 +1240,7 @@ export class ServiceStatefulRuntime {
1240
1240
  payload: ServiceApplicationPayload,
1241
1241
  timeoutMs: number
1242
1242
  ): ServiceStatefulPendingOperation {
1243
- const pending = this.operations.reserve(timeoutMs);
1243
+ const pending = this.operations.register(timeoutMs);
1244
1244
  const target = this.acceptSpotAuthority(requested);
1245
1245
  if (target === undefined) {
1246
1246
  this.operations.reply(pending.id, {
@@ -1294,7 +1294,7 @@ export class ServiceStatefulRuntime {
1294
1294
  sourceActor?: ServiceActorRef,
1295
1295
  boundSession?: { readonly sessionRid: string; readonly bindingGeneration: bigint }
1296
1296
  ): ServiceStatefulPendingOperation {
1297
- const pending = this.operations.reserve(timeoutMs);
1297
+ const pending = this.operations.register(timeoutMs);
1298
1298
  const route = this.tryActorFence(target);
1299
1299
  if (route === undefined) {
1300
1300
  this.operations.reply(pending.id, {
@@ -1393,7 +1393,7 @@ export class ServiceStatefulRuntime {
1393
1393
  sourceSpotId?: string,
1394
1394
  metadataFrame?: Uint8Array
1395
1395
  ): ServiceStatefulPendingOperation {
1396
- const pending = this.operations.reserve(timeoutMs);
1396
+ const pending = this.operations.register(timeoutMs);
1397
1397
  this.submitRequest(
1398
1398
  pending,
1399
1399
  route.targetNodeRid,
@@ -1442,7 +1442,7 @@ export class ServiceStatefulRuntime {
1442
1442
  metadataFrame?: Uint8Array
1443
1443
  ): ServiceStatefulPendingOperation {
1444
1444
  const timeoutMs = remainingDeadlineMs(deadlineUnixMs);
1445
- const pending = this.operations.reserve(timeoutMs);
1445
+ const pending = this.operations.register(timeoutMs);
1446
1446
  this.submitRequest(
1447
1447
  pending,
1448
1448
  target.targetNodeRid,
@@ -1471,7 +1471,7 @@ export class ServiceStatefulRuntime {
1471
1471
  actorId: string,
1472
1472
  timeoutMs: number
1473
1473
  ): ServiceStatefulPendingOperation {
1474
- const pending = this.operations.reserve(timeoutMs);
1474
+ const pending = this.operations.register(timeoutMs);
1475
1475
  this.submitRequest(
1476
1476
  pending,
1477
1477
  targetNodeRid,
@@ -1483,7 +1483,7 @@ export class ServiceStatefulRuntime {
1483
1483
  }
1484
1484
 
1485
1485
  destroyActor(actor: ServiceActorRef, timeoutMs: number): ServiceStatefulPendingOperation {
1486
- const pending = this.operations.reserve(timeoutMs);
1486
+ const pending = this.operations.register(timeoutMs);
1487
1487
  const local = actor.nodeRid === this.nodeRid;
1488
1488
  if (local) {
1489
1489
  queueMicrotask(() => {
@@ -1510,7 +1510,7 @@ export class ServiceStatefulRuntime {
1510
1510
  timeoutMs: number
1511
1511
  ): ServiceStatefulPendingOperation {
1512
1512
  const deadlineMs = performance.now() + timeoutMs;
1513
- const pending = this.operations.reserve(timeoutMs, targetNodeRid === this.nodeRid ? 'registry' : 'sender');
1513
+ const pending = this.operations.register(timeoutMs, targetNodeRid === this.nodeRid ? 'registry' : 'sender');
1514
1514
  const target = this.trySpotFence(targetNodeRid, {
1515
1515
  ...targetSpot,
1516
1516
  generation: targetSpotGeneration
@@ -1537,7 +1537,7 @@ export class ServiceStatefulRuntime {
1537
1537
  timeoutMs: number
1538
1538
  ): ServiceStatefulPendingOperation {
1539
1539
  const deadlineMs = performance.now() + timeoutMs;
1540
- const pending = this.operations.reserve(timeoutMs, targetNodeRid === this.nodeRid ? 'registry' : 'sender');
1540
+ const pending = this.operations.register(timeoutMs, targetNodeRid === this.nodeRid ? 'registry' : 'sender');
1541
1541
  const target = this.trySpotFence(targetNodeRid, {
1542
1542
  ...targetSpot,
1543
1543
  generation: targetSpotGeneration
@@ -1561,7 +1561,7 @@ export class ServiceStatefulRuntime {
1561
1561
  timeoutMs: number
1562
1562
  ): ServiceStatefulPendingOperation {
1563
1563
  const deadlineMs = performance.now() + timeoutMs;
1564
- const pending = this.operations.reserve(timeoutMs, targetNodeRid === this.nodeRid ? 'registry' : 'sender');
1564
+ const pending = this.operations.register(timeoutMs, targetNodeRid === this.nodeRid ? 'registry' : 'sender');
1565
1565
  this.submitActorJoin(
1566
1566
  pending,
1567
1567
  actor,
@@ -1582,7 +1582,7 @@ export class ServiceStatefulRuntime {
1582
1582
  timeoutMs: number
1583
1583
  ): ServiceStatefulPendingOperation {
1584
1584
  const deadlineMs = performance.now() + timeoutMs;
1585
- const pending = this.operations.reserve(timeoutMs, targetNodeRid === this.nodeRid ? 'registry' : 'sender');
1585
+ const pending = this.operations.register(timeoutMs, targetNodeRid === this.nodeRid ? 'registry' : 'sender');
1586
1586
  this.submitActorJoin(
1587
1587
  pending,
1588
1588
  actor,
@@ -1600,7 +1600,7 @@ export class ServiceStatefulRuntime {
1600
1600
  expectedMembershipEpoch: bigint,
1601
1601
  timeoutMs: number
1602
1602
  ): ServiceStatefulPendingOperation {
1603
- const pending = this.operations.reserve(timeoutMs);
1603
+ const pending = this.operations.register(timeoutMs);
1604
1604
  queueMicrotask(() => {
1605
1605
  try {
1606
1606
  const transition = this.registry.leaveActor(actor, expectedMembershipEpoch);
@@ -1635,7 +1635,7 @@ export class ServiceStatefulRuntime {
1635
1635
  actorAuthority?: StreamSessionActorAuthorityFence
1636
1636
  ): ServiceStatefulPendingOperation {
1637
1637
  const deadlineMs = performance.now() + timeoutMs;
1638
- const pending = this.operations.reserve(timeoutMs, 'sender');
1638
+ const pending = this.operations.register(timeoutMs, 'sender');
1639
1639
  const generation = this.nextSessionSequence++;
1640
1640
  const localBinding: ServiceSessionBinding = {
1641
1641
  actor,
@@ -1777,7 +1777,7 @@ export class ServiceStatefulRuntime {
1777
1777
  expectedBindingGeneration: bigint,
1778
1778
  timeoutMs: number
1779
1779
  ): ServiceStatefulPendingOperation {
1780
- const pending = this.operations.reserve(timeoutMs);
1780
+ const pending = this.operations.register(timeoutMs);
1781
1781
  const delivery = this.sessionDeliveries.get(actorKey(actor));
1782
1782
  if (
1783
1783
  delivery === undefined
@@ -2644,7 +2644,7 @@ export class ServiceStatefulRuntime {
2644
2644
  return;
2645
2645
  }
2646
2646
 
2647
- const pending = this.operations.reserve(remainingMs);
2647
+ const pending = this.operations.register(remainingMs);
2648
2648
  this.submitRequest(
2649
2649
  pending,
2650
2650
  route.targetNodeRid,
@@ -69,7 +69,7 @@ const INTERNAL_TO_PUBLIC: Readonly<Record<ZLinkFrameworkInternalErrorKind, ZLink
69
69
  [ZLinkFrameworkInternalErrorKind.RequestRejected]: ZLinkFrameworkErrorKind.Rejected,
70
70
  [ZLinkFrameworkInternalErrorKind.RequestProtocolError]: ZLinkFrameworkErrorKind.ProtocolError,
71
71
  [ZLinkFrameworkInternalErrorKind.RequestFailed]: ZLinkFrameworkErrorKind.InternalFailure,
72
- [ZLinkFrameworkInternalErrorKind.WorkerQueueFull]: ZLinkFrameworkErrorKind.CapacityExceeded,
72
+ [ZLinkFrameworkInternalErrorKind.WorkerQueueFull]: ZLinkFrameworkErrorKind.Unavailable,
73
73
  [ZLinkFrameworkInternalErrorKind.WorkerTimedOut]: ZLinkFrameworkErrorKind.DeadlineExceeded,
74
74
  [ZLinkFrameworkInternalErrorKind.WorkerFailed]: ZLinkFrameworkErrorKind.InternalFailure,
75
75
  [ZLinkFrameworkInternalErrorKind.ActorLocationStale]: ZLinkFrameworkErrorKind.Unavailable,
@@ -82,7 +82,7 @@ const INTERNAL_TO_PUBLIC: Readonly<Record<ZLinkFrameworkInternalErrorKind, ZLink
82
82
  [ZLinkFrameworkInternalErrorKind.ActorGenerationStale]: ZLinkFrameworkErrorKind.InvalidOperation,
83
83
  [ZLinkFrameworkInternalErrorKind.ActorMoving]: ZLinkFrameworkErrorKind.Unavailable,
84
84
  [ZLinkFrameworkInternalErrorKind.DeadlineExceeded]: ZLinkFrameworkErrorKind.DeadlineExceeded,
85
- [ZLinkFrameworkInternalErrorKind.PlacementCapacityExhausted]: ZLinkFrameworkErrorKind.CapacityExceeded,
85
+ [ZLinkFrameworkInternalErrorKind.PlacementCapacityExhausted]: ZLinkFrameworkErrorKind.Unavailable,
86
86
  [ZLinkFrameworkInternalErrorKind.RoutingIdConflict]: ZLinkFrameworkErrorKind.AlreadyExists,
87
87
  [ZLinkFrameworkInternalErrorKind.SpotGenerationStale]: ZLinkFrameworkErrorKind.InvalidOperation,
88
88
  [ZLinkFrameworkInternalErrorKind.SpotMoving]: ZLinkFrameworkErrorKind.Unavailable,
@@ -145,8 +145,7 @@ const INTERNAL_KIND_BY_WIRE_FAILURE_CODE: ReadonlyMap<number, ZLinkFrameworkInte
145
145
  ...Object.entries(ZLINK_FRAMEWORK_INTERNAL_ERROR_KIND_VALUES)
146
146
  .map(([kind, code]) => [code + 1, kind as ZLinkFrameworkInternalErrorKind] as const),
147
147
  // Spec 32-framework-error-model:99-103 — a workerQueueFull(18) received in a
148
- // remote reply is the target's queue state (Unavailable), not the source-owned
149
- // CapacityExceeded that the wire-value-minus-one derivation would give.
148
+ // remote reply is the target's queue state and maps to Unavailable.
150
149
  [18, ZLinkFrameworkInternalErrorKind.RouteNotConnected]
151
150
  ]);
152
151
 
@@ -188,8 +187,8 @@ export function internalFrameworkErrorKind(
188
187
  * terminal is classified identically across languages instead of collapsing to
189
188
  * Unavailable. Spec 32-framework-error-model:81-92 (request-terminal
190
189
  * classification); a deadline yields DeadlineExceeded, a lost route Unavailable.
191
- * Backpressure on the REQUEST path is CapacityExceeded (distinct from the
192
- * one-way submit path, which is DeadlineExceeded).
190
+ * Backpressure waits in Core until the operation deadline, so request and
191
+ * one-way terminals both surface DeadlineExceeded when that wait expires.
193
192
  */
194
193
  export function requestResultToPublicErrorKind(result: number): ZLinkFrameworkErrorKind {
195
194
  switch (result) {
@@ -204,8 +203,7 @@ export function requestResultToPublicErrorKind(result: number): ZLinkFrameworkEr
204
203
  case RequestResult.Rejected:
205
204
  return ZLinkFrameworkErrorKind.Rejected;
206
205
  case RequestResult.Backpressured:
207
- // Source-owned send/completion reservation exhausted (spec 32:93-98).
208
- return ZLinkFrameworkErrorKind.CapacityExceeded;
206
+ return ZLinkFrameworkErrorKind.DeadlineExceeded;
209
207
  case RequestResult.Conflict:
210
208
  case RequestResult.Busy:
211
209
  case RequestResult.NotConnected:
@@ -229,8 +227,8 @@ export function requestResultToPublicErrorKind(result: number): ZLinkFrameworkEr
229
227
  * coarse terminal; when absent (0) or unrecognised, the coarse terminal
230
228
  * classifies. Remote another-node queue/operation-table saturation
231
229
  * (Conflict/Busy) is Unavailable — a resource this runtime does not own — while
232
- * only the placement admission terminal Backpressured(113) is CapacityExceeded,
233
- * a target-owned admission decision rather than a queue. Shared by the lifecycle
230
+ * placement admission terminal Backpressured(113) is Unavailable because no
231
+ * node can host the Spot. Shared by the lifecycle
234
232
  * completion paths (Actor join, User Spot create/close) so they classify
235
233
  * identically to the request path instead of collapsing to a single kind.
236
234
  */
@@ -170,7 +170,7 @@ export class ZLinkActorPlacementCoordinator {
170
170
  // ownership-aware translator instead of collapsing every non-OK
171
171
  // remote create to InternalFailure (spec 32-framework-error-model:
172
172
  // 83-118, 99-108). Backpressured(113) still surfaces as target
173
- // placement capacity -> CapacityExceeded through the shared path.
173
+ // placement capacity -> Unavailable through the shared path.
174
174
  throw wireReplyFailureException(
175
175
  remote.terminalResult,
176
176
  remote.failureCode,
@@ -380,7 +380,8 @@ export class ZLinkFrameworkRuntimeHost implements
380
380
  this.meshRouters.classifyManualNodeTarget(meshName, targetNodeRid),
381
381
  (meshName, sourceNodeRid, parts) =>
382
382
  this.submitLocalMeshRoute(meshName, sourceNodeRid, parts),
383
- this.metrics
383
+ this.metrics,
384
+ () => this.flowCreationEnabled()
384
385
  );
385
386
  this.spotAddressTransport = new ZLinkHostSpotAddressTransport({
386
387
  resolver: () => this.createLocationSpotRouteResolver(),
@@ -5035,10 +5035,6 @@ export function relocationFailedFailureCode(
5035
5035
  return ServiceWireFrameworkErrorCode.requestRejected;
5036
5036
  case ZLinkFrameworkErrorKind.ProtocolError:
5037
5037
  return ServiceWireFrameworkErrorCode.requestProtocolError;
5038
- // No dedicated "capacity exceeded" wire code exists; a full queue is
5039
- // the closest capacity-shaped signal.
5040
- case ZLinkFrameworkErrorKind.CapacityExceeded:
5041
- return ServiceWireFrameworkErrorCode.workerQueueFull;
5042
5038
  // No dedicated "deadline exceeded" wire code exists; a worker timeout
5043
5039
  // is the closest timeout-shaped signal.
5044
5040
  case ZLinkFrameworkErrorKind.DeadlineExceeded:
@@ -763,7 +763,7 @@ function missingInstanceRequestFailure(
763
763
  ? ZLinkFrameworkInternalErrorKind.RuntimeShutdown
764
764
  : result === RequestResult.Backpressured
765
765
  // Spec 32-framework-error-model:104-108 — the bounded admission
766
- // terminal is target placement capacity: CapacityExceeded.
766
+ // terminal is target placement capacity: Unavailable.
767
767
  ? ZLinkFrameworkInternalErrorKind.PlacementCapacityExhausted
768
768
  : result === RequestResult.NotConnected
769
769
  ? ZLinkFrameworkInternalErrorKind.RouteNotConnected
@@ -1110,7 +1110,7 @@ function remoteUserSpotFailure(
1110
1110
  // Remote operation-table saturation Busy(108)+None is Unavailable,
1111
1111
  // InvalidState(111)+None is InvalidOperation, every fine code follows the
1112
1112
  // shared table, and only placement admission Backpressured(113) stays
1113
- // CapacityExceeded.
1113
+ // Unavailable.
1114
1114
  return wireReplyFailureException(
1115
1115
  terminalResult,
1116
1116
  failureCode,