@shipfox/api-workflows 12.6.0 → 13.0.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 (117) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/CHANGELOG.md +23 -0
  3. package/dist/core/entities/workflow-run.d.ts +3 -0
  4. package/dist/core/entities/workflow-run.d.ts.map +1 -1
  5. package/dist/core/entities/workflow-run.js.map +1 -1
  6. package/dist/db/index.d.ts +1 -1
  7. package/dist/db/index.d.ts.map +1 -1
  8. package/dist/db/index.js +1 -1
  9. package/dist/db/index.js.map +1 -1
  10. package/dist/db/job-listeners.d.ts.map +1 -1
  11. package/dist/db/job-listeners.js +17 -1
  12. package/dist/db/job-listeners.js.map +1 -1
  13. package/dist/db/workflow-runs/job-executions.d.ts +2 -3
  14. package/dist/db/workflow-runs/job-executions.d.ts.map +1 -1
  15. package/dist/db/workflow-runs/job-executions.js +48 -20
  16. package/dist/db/workflow-runs/job-executions.js.map +1 -1
  17. package/dist/db/workflow-runs/outbox.d.ts +15 -0
  18. package/dist/db/workflow-runs/outbox.d.ts.map +1 -1
  19. package/dist/db/workflow-runs/outbox.js +35 -1
  20. package/dist/db/workflow-runs/outbox.js.map +1 -1
  21. package/dist/db/workflow-runs/queries.d.ts +5 -3
  22. package/dist/db/workflow-runs/queries.d.ts.map +1 -1
  23. package/dist/db/workflow-runs/queries.js +20 -6
  24. package/dist/db/workflow-runs/queries.js.map +1 -1
  25. package/dist/db/workflow-runs/run-graph.d.ts.map +1 -1
  26. package/dist/db/workflow-runs/run-graph.js +12 -0
  27. package/dist/db/workflow-runs/run-graph.js.map +1 -1
  28. package/dist/db/workflow-runs/run-status.d.ts.map +1 -1
  29. package/dist/db/workflow-runs/run-status.js +13 -1
  30. package/dist/db/workflow-runs/run-status.js.map +1 -1
  31. package/dist/db/workflow-runs.d.ts +2 -2
  32. package/dist/db/workflow-runs.d.ts.map +1 -1
  33. package/dist/db/workflow-runs.js +2 -2
  34. package/dist/db/workflow-runs.js.map +1 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +2 -4
  37. package/dist/index.js.map +1 -1
  38. package/dist/presentation/dto/workflow-run.d.ts.map +1 -1
  39. package/dist/presentation/dto/workflow-run.js +4 -2
  40. package/dist/presentation/dto/workflow-run.js.map +1 -1
  41. package/dist/presentation/index.d.ts +1 -1
  42. package/dist/presentation/index.d.ts.map +1 -1
  43. package/dist/presentation/index.js +1 -1
  44. package/dist/presentation/index.js.map +1 -1
  45. package/dist/presentation/routes/get-run.d.ts.map +1 -1
  46. package/dist/presentation/routes/get-run.js +2 -1
  47. package/dist/presentation/routes/get-run.js.map +1 -1
  48. package/dist/presentation/subscribers/index.d.ts +0 -1
  49. package/dist/presentation/subscribers/index.d.ts.map +1 -1
  50. package/dist/presentation/subscribers/index.js +0 -1
  51. package/dist/presentation/subscribers/index.js.map +1 -1
  52. package/dist/temporal/activities/index.d.ts +2 -18
  53. package/dist/temporal/activities/index.d.ts.map +1 -1
  54. package/dist/temporal/activities/index.js +3 -5
  55. package/dist/temporal/activities/index.js.map +1 -1
  56. package/dist/temporal/activities/orchestration-activities.d.ts +5 -14
  57. package/dist/temporal/activities/orchestration-activities.d.ts.map +1 -1
  58. package/dist/temporal/activities/orchestration-activities.js +13 -34
  59. package/dist/temporal/activities/orchestration-activities.js.map +1 -1
  60. package/dist/temporal/workflows/index.bundle.js +20 -68
  61. package/dist/temporal/workflows/job-execution-orchestration.d.ts +0 -3
  62. package/dist/temporal/workflows/job-execution-orchestration.d.ts.map +1 -1
  63. package/dist/temporal/workflows/job-execution-orchestration.js +17 -41
  64. package/dist/temporal/workflows/job-execution-orchestration.js.map +1 -1
  65. package/dist/temporal/workflows/job-listener-orchestration.d.ts +0 -3
  66. package/dist/temporal/workflows/job-listener-orchestration.d.ts.map +1 -1
  67. package/dist/temporal/workflows/job-listener-orchestration.js +1 -9
  68. package/dist/temporal/workflows/job-listener-orchestration.js.map +1 -1
  69. package/dist/temporal/workflows/run-orchestration.d.ts.map +1 -1
  70. package/dist/temporal/workflows/run-orchestration.js +2 -18
  71. package/dist/temporal/workflows/run-orchestration.js.map +1 -1
  72. package/dist/temporal/workflows/test-env.d.ts +4 -6
  73. package/dist/temporal/workflows/test-env.d.ts.map +1 -1
  74. package/dist/temporal/workflows/test-env.js +13 -24
  75. package/dist/temporal/workflows/test-env.js.map +1 -1
  76. package/dist/tsconfig.test.tsbuildinfo +1 -1
  77. package/package.json +3 -3
  78. package/src/core/entities/workflow-run.ts +3 -0
  79. package/src/db/index.ts +1 -1
  80. package/src/db/job-listeners.test.ts +21 -0
  81. package/src/db/job-listeners.ts +18 -0
  82. package/src/db/workflow-runs/job-executions.test.ts +110 -12
  83. package/src/db/workflow-runs/job-executions.ts +54 -29
  84. package/src/db/workflow-runs/outbox.ts +63 -0
  85. package/src/db/workflow-runs/queries.test.ts +27 -0
  86. package/src/db/workflow-runs/queries.ts +31 -5
  87. package/src/db/workflow-runs/run-graph.ts +17 -0
  88. package/src/db/workflow-runs/run-status.test.ts +13 -0
  89. package/src/db/workflow-runs/run-status.ts +15 -1
  90. package/src/db/workflow-runs.ts +3 -1
  91. package/src/index.ts +1 -5
  92. package/src/presentation/dto/workflow-run.ts +7 -1
  93. package/src/presentation/index.ts +0 -1
  94. package/src/presentation/routes/get-run.test.ts +31 -0
  95. package/src/presentation/routes/get-run.ts +1 -0
  96. package/src/presentation/routes/list-runs.test.ts +5 -1
  97. package/src/presentation/subscribers/index.ts +0 -1
  98. package/src/temporal/activities/index.ts +2 -8
  99. package/src/temporal/activities/orchestration-activities.test.ts +17 -1
  100. package/src/temporal/activities/orchestration-activities.ts +13 -46
  101. package/src/temporal/workflows/job-execution-orchestration.test.ts +6 -25
  102. package/src/temporal/workflows/job-execution-orchestration.ts +17 -43
  103. package/src/temporal/workflows/job-listener-orchestration-continuation.test.ts +0 -3
  104. package/src/temporal/workflows/job-listener-orchestration.test.ts +9 -12
  105. package/src/temporal/workflows/job-listener-orchestration.ts +0 -8
  106. package/src/temporal/workflows/run-orchestration.test.ts +22 -33
  107. package/src/temporal/workflows/run-orchestration.ts +4 -28
  108. package/src/temporal/workflows/test-env.ts +14 -34
  109. package/test/fixtures/runners-inter-module.ts +0 -3
  110. package/test/helpers/workflow-runs.ts +25 -0
  111. package/tsconfig.build.tsbuildinfo +1 -1
  112. package/dist/presentation/subscribers/on-runner-job-queued.d.ts +0 -3
  113. package/dist/presentation/subscribers/on-runner-job-queued.d.ts.map +0 -1
  114. package/dist/presentation/subscribers/on-runner-job-queued.js +0 -20
  115. package/dist/presentation/subscribers/on-runner-job-queued.js.map +0 -1
  116. package/src/presentation/subscribers/on-runner-job-queued.test.ts +0 -57
  117. package/src/presentation/subscribers/on-runner-job-queued.ts +0 -23
@@ -197,7 +197,7 @@ describe('jobListenerOrchestration', () => {
197
197
 
198
198
  expect(listenerFiringOutcomeCalls()).toHaveLength(0);
199
199
  expect(resolveJobListenerCalls().map((c) => c.params.reason)).toEqual(['until']);
200
- expect(callsNamed('enqueueJobExecutionForRunner')).toHaveLength(0);
200
+ expect(callsNamed('queueJobExecutionActivity')).toHaveLength(0);
201
201
  });
202
202
 
203
203
  test('resolves with timeout when the listening deadline elapses', async () => {
@@ -233,9 +233,10 @@ describe('jobListenerOrchestration', () => {
233
233
 
234
234
  await runListener({maxExecutions: 1});
235
235
 
236
- expect(callsNamed('enqueueJobExecutionForRunner')).toHaveLength(1);
237
- expect(callsNamed('enqueueJobExecutionForRunner')[0]?.params).toMatchObject({
238
- requiredLabels: ['gpu', 'linux'],
236
+ expect(callsNamed('queueJobExecutionActivity')).toHaveLength(1);
237
+ expect(callsNamed('queueJobExecutionActivity')[0]?.params).toMatchObject({
238
+ jobId,
239
+ jobExecutionId: 'exec-1',
239
240
  });
240
241
  expect(listenerFiringOutcomeCalls().map((c) => c.params.outcome)).toEqual(['succeeded']);
241
242
  });
@@ -250,7 +251,7 @@ describe('jobListenerOrchestration', () => {
250
251
  await runListener({maxExecutions: 1});
251
252
 
252
253
  // The child actually ran (enqueued), then returned failed.
253
- expect(callsNamed('enqueueJobExecutionForRunner')).toHaveLength(1);
254
+ expect(callsNamed('queueJobExecutionActivity')).toHaveLength(1);
254
255
  expect(listenerFiringOutcomeCalls().map((c) => c.params.outcome)).toEqual(['failed']);
255
256
  expect(resolveJobListenerCalls().map((c) => c.params.reason)).toEqual(['max_executions']);
256
257
  });
@@ -265,7 +266,7 @@ describe('jobListenerOrchestration', () => {
265
266
  await runListener({maxExecutions: 2});
266
267
 
267
268
  // Sequence 1 failed at materialization (no child enqueued); sequence 2 ran and succeeded.
268
- expect(callsNamed('enqueueJobExecutionForRunner')).toHaveLength(1);
269
+ expect(callsNamed('queueJobExecutionActivity')).toHaveLength(1);
269
270
  expect(listenerFiringOutcomeCalls().map((c) => c.params.outcome)).toEqual([
270
271
  'failed',
271
272
  'succeeded',
@@ -369,15 +370,12 @@ describe('jobListenerOrchestration', () => {
369
370
  });
370
371
 
371
372
  const handle = await startListener({onResolve: 'cancel'});
372
- await waitForActivity('enqueueJobExecutionForRunner');
373
+ await waitForActivity('queueJobExecutionActivity');
373
374
  await handle.signal(LISTENER_RESOLVE_SIGNAL);
374
375
  await handle.result();
375
376
 
376
377
  expect(settleListenerCalls().map((c) => c.params.status)).toEqual(['cancelled']);
377
378
  expect(listenerFiringOutcomeCalls().map((c) => c.params.outcome)).toEqual(['cancelled']);
378
- expect(callsNamed('cancelRunnerJobsActivity')).toEqual([
379
- {name: 'cancelRunnerJobsActivity', params: {jobIds: [jobId]}},
380
- ]);
381
379
  expect(resolveJobListenerCalls().map((c) => c.params.reason)).toEqual(['until']);
382
380
  });
383
381
 
@@ -390,7 +388,7 @@ describe('jobListenerOrchestration', () => {
390
388
  });
391
389
 
392
390
  const handle = await startListener({onResolve: 'finish'});
393
- await waitForActivity('enqueueJobExecutionForRunner');
391
+ await waitForActivity('queueJobExecutionActivity');
394
392
  // Resolution arrives mid-firing; finish mode must wait for the child, not cancel it.
395
393
  await handle.signal(LISTENER_RESOLVE_SIGNAL);
396
394
  const child = testEnv.client.workflow.getHandle(`job:${jobId}`);
@@ -398,7 +396,6 @@ describe('jobListenerOrchestration', () => {
398
396
  await handle.result();
399
397
 
400
398
  expect(settleListenerCalls()).toHaveLength(0);
401
- expect(callsNamed('cancelRunnerJobsActivity')).toHaveLength(0);
402
399
  expect(listenerFiringOutcomeCalls().map((c) => c.params.outcome)).toEqual(['succeeded']);
403
400
  expect(resolveJobListenerCalls().map((c) => c.params.reason)).toEqual(['until']);
404
401
  });
@@ -25,7 +25,6 @@ const {
25
25
  resolveJobListenerActivity,
26
26
  settleListenerJobExecutionActivity,
27
27
  recordListenerFiringOutcomeActivity,
28
- cancelRunnerJobsActivity,
29
28
  } = proxyActivities<ReturnType<typeof createOrchestrationActivities>>({
30
29
  startToCloseTimeout: '30s',
31
30
  });
@@ -34,9 +33,6 @@ export const listenerEventsAvailableSignal = defineSignal<[]>(LISTENER_EVENTS_AV
34
33
  export const listenerResolveSignal = defineSignal<[]>(LISTENER_RESOLVE_SIGNAL);
35
34
 
36
35
  export interface JobListenerOrchestrationInput {
37
- workspaceId: string;
38
- workflowRunId: string;
39
- projectId: string;
40
36
  runAttemptId: string;
41
37
  jobId: string;
42
38
  jobVersion: number;
@@ -413,9 +409,6 @@ async function runListenerExecution(params: {
413
409
  workflowIdReusePolicy: 'ALLOW_DUPLICATE',
414
410
  args: [
415
411
  {
416
- workspaceId: params.input.workspaceId,
417
- workflowRunId: params.input.workflowRunId,
418
- projectId: params.input.projectId,
419
412
  jobId: params.input.jobId,
420
413
  jobExecutionId: params.jobExecutionId,
421
414
  runAttemptId: params.input.runAttemptId,
@@ -447,7 +440,6 @@ async function runListenerExecution(params: {
447
440
  status: 'cancelled',
448
441
  });
449
442
  await recordListenerFiringOutcomeActivity({outcome: 'cancelled'});
450
- await cancelRunnerJobsActivity({jobIds: [params.input.jobId]});
451
443
  return;
452
444
  }
453
445
  }
@@ -59,17 +59,13 @@ describe('runOrchestration', () => {
59
59
 
60
60
  const runAttemptStatuses = setRunAttemptStatusCalls().map((c) => c.params.status);
61
61
  expect(runAttemptStatuses).toEqual(['running', 'succeeded']);
62
- const enqueueCalls = callsNamed('enqueueJobExecutionForRunner');
62
+ const enqueueCalls = callsNamed('queueJobExecutionActivity');
63
63
  expect(enqueueCalls).toHaveLength(3);
64
- // The lease tuple is sourced from the loaded dag (workspace/project/run together).
65
- for (const call of enqueueCalls) {
66
- expect(call.params).toMatchObject({
67
- workspaceId: 'workspace-1',
68
- projectId: 'project-1',
69
- runAttemptId: 'r1-attempt-1',
70
- requiredLabels: ['ubuntu22'],
71
- });
72
- }
64
+ expect(enqueueCalls.map((call) => call.params)).toEqual([
65
+ {jobId: 'j1', jobExecutionId: 'j1'},
66
+ {jobId: 'j2', jobExecutionId: 'j2'},
67
+ {jobId: 'j3', jobExecutionId: 'j3'},
68
+ ]);
73
69
  });
74
70
 
75
71
  test('already-succeeded upstream jobs are not re-enqueued', async () => {
@@ -83,7 +79,7 @@ describe('runOrchestration', () => {
83
79
 
84
80
  const runAttemptStatuses = setRunAttemptStatusCalls().map((c) => c.params.status);
85
81
  expect(runAttemptStatuses).toEqual(['running', 'succeeded']);
86
- const enqueueCalls = callsNamed('enqueueJobExecutionForRunner');
82
+ const enqueueCalls = callsNamed('queueJobExecutionActivity');
87
83
  expect(enqueueCalls).toHaveLength(1);
88
84
  expect(enqueueCalls[0]?.params).toMatchObject({jobId: 'j2'});
89
85
  });
@@ -99,7 +95,7 @@ describe('runOrchestration', () => {
99
95
 
100
96
  const runAttemptStatuses = setRunAttemptStatusCalls().map((c) => c.params.status);
101
97
  expect(runAttemptStatuses).toEqual(['running', 'failed']);
102
- const enqueueCalls = callsNamed('enqueueJobExecutionForRunner');
98
+ const enqueueCalls = callsNamed('queueJobExecutionActivity');
103
99
  expect(enqueueCalls).toHaveLength(1);
104
100
  expect(enqueueCalls[0]?.params).toMatchObject({jobId: 'j2'});
105
101
  });
@@ -116,7 +112,7 @@ describe('runOrchestration', () => {
116
112
 
117
113
  const runAttemptStatuses = setRunAttemptStatusCalls().map((c) => c.params.status);
118
114
  expect(runAttemptStatuses).toEqual(['running', 'succeeded']);
119
- expect(callsNamed('enqueueJobExecutionForRunner')).toHaveLength(3);
115
+ expect(callsNamed('queueJobExecutionActivity')).toHaveLength(3);
120
116
  });
121
117
 
122
118
  test('single job fails, dependents are skipped', async () => {
@@ -132,7 +128,7 @@ describe('runOrchestration', () => {
132
128
  const runAttemptStatuses = setRunAttemptStatusCalls().map((c) => c.params.status);
133
129
  expect(runAttemptStatuses).toEqual(['running', 'failed']);
134
130
 
135
- expect(callsNamed('enqueueJobExecutionForRunner')).toHaveLength(1);
131
+ expect(callsNamed('queueJobExecutionActivity')).toHaveLength(1);
136
132
  const jobStatuses = setJobStatusCalls().map((c) => ({
137
133
  id: c.params.jobId,
138
134
  status: c.params.status,
@@ -162,7 +158,7 @@ describe('runOrchestration', () => {
162
158
 
163
159
  const runAttemptStatuses = setRunAttemptStatusCalls().map((c) => c.params.status);
164
160
  expect(runAttemptStatuses).toEqual(['running', 'failed']);
165
- expect(callsNamed('enqueueJobExecutionForRunner')).toHaveLength(1);
161
+ expect(callsNamed('queueJobExecutionActivity')).toHaveLength(1);
166
162
 
167
163
  const jobStatuses = setJobStatusCalls().map((c) => ({
168
164
  id: c.params.jobId,
@@ -199,7 +195,7 @@ describe('runOrchestration', () => {
199
195
  },
200
196
  },
201
197
  ]);
202
- expect(callsNamed('enqueueJobExecutionForRunner').map((call) => call.params)).toEqual([
198
+ expect(callsNamed('queueJobExecutionActivity').map((call) => call.params)).toEqual([
203
199
  expect.objectContaining({jobId: 'j1'}),
204
200
  expect.objectContaining({jobId: 'j2'}),
205
201
  ]);
@@ -221,7 +217,7 @@ describe('runOrchestration', () => {
221
217
 
222
218
  await executeRun();
223
219
 
224
- expect(callsNamed('enqueueJobExecutionForRunner').map((call) => call.params)).toEqual([
220
+ expect(callsNamed('queueJobExecutionActivity').map((call) => call.params)).toEqual([
225
221
  expect.objectContaining({jobId: 'j1'}),
226
222
  ]);
227
223
  expect(setRunAttemptStatusCalls().map((c) => c.params.status)).toEqual([
@@ -249,7 +245,7 @@ describe('runOrchestration', () => {
249
245
  },
250
246
  },
251
247
  ]);
252
- const enqueuedJobs = callsNamed('enqueueJobExecutionForRunner').map((call) => call.params);
248
+ const enqueuedJobs = callsNamed('queueJobExecutionActivity').map((call) => call.params);
253
249
  expect(enqueuedJobs).toHaveLength(3);
254
250
  expect(enqueuedJobs).toEqual(
255
251
  expect.arrayContaining([
@@ -273,7 +269,7 @@ describe('runOrchestration', () => {
273
269
 
274
270
  await executeRun();
275
271
 
276
- expect(callsNamed('enqueueJobExecutionForRunner')).toHaveLength(0);
272
+ expect(callsNamed('queueJobExecutionActivity')).toHaveLength(0);
277
273
  expect(setRunAttemptStatusCalls().map((c) => c.params.status)).toEqual([
278
274
  'running',
279
275
  'succeeded',
@@ -310,7 +306,7 @@ describe('runOrchestration', () => {
310
306
  expect(finalRunAttemptStatus?.params.version).toBeGreaterThan(0);
311
307
  });
312
308
 
313
- test('cancel signal stops scheduling and fans out runner cancellation', async () => {
309
+ test('cancel signal stops scheduling without calling runners directly', async () => {
314
310
  const jobs = [dagJob('j1', 'build'), dagJob('j2', 'deploy', ['build'])];
315
311
  setCfg({dag: makeDag(jobs, 'r-cancel'), jobResults: new Map(), skipSignal: true});
316
312
 
@@ -319,16 +315,13 @@ describe('runOrchestration', () => {
319
315
  workflowId: `run:${workflowRunId}`,
320
316
  args: [{workflowRunId, workspaceId}],
321
317
  });
322
- await waitForActivity('enqueueJobExecutionForRunner');
318
+ await waitForActivity('queueJobExecutionActivity');
323
319
 
324
320
  await handle.signal('run-cancel');
325
321
  await handle.result();
326
322
 
327
323
  expect(setRunAttemptStatusCalls().map((c) => c.params.status)).toEqual(['running']);
328
- expect(callsNamed('enqueueJobExecutionForRunner')).toHaveLength(1);
329
- expect(callsNamed('cancelRunnerJobsActivity')).toEqual([
330
- {name: 'cancelRunnerJobsActivity', params: {jobIds: ['j1', 'j2']}},
331
- ]);
324
+ expect(callsNamed('queueJobExecutionActivity')).toHaveLength(1);
332
325
  });
333
326
 
334
327
  test('starts listening jobs and cancels them with the run', async () => {
@@ -347,10 +340,7 @@ describe('runOrchestration', () => {
347
340
  await handle.result();
348
341
 
349
342
  expect(setRunAttemptStatusCalls().map((c) => c.params.status)).toEqual(['running']);
350
- expect(callsNamed('enqueueJobExecutionForRunner')).toHaveLength(0);
351
- expect(callsNamed('cancelRunnerJobsActivity')).toEqual([
352
- {name: 'cancelRunnerJobsActivity', params: {jobIds: ['j1']}},
353
- ]);
343
+ expect(callsNamed('queueJobExecutionActivity')).toHaveLength(0);
354
344
  });
355
345
 
356
346
  test('aborts early when the initial running write reports an already-terminal run', async () => {
@@ -364,8 +354,7 @@ describe('runOrchestration', () => {
364
354
  await executeRun();
365
355
 
366
356
  expect(setRunAttemptStatusCalls().map((c) => c.params.status)).toEqual(['running']);
367
- expect(callsNamed('enqueueJobExecutionForRunner')).toHaveLength(0);
368
- expect(callsNamed('cancelRunnerJobsActivity')).toHaveLength(0);
357
+ expect(callsNamed('queueJobExecutionActivity')).toHaveLength(0);
369
358
  });
370
359
 
371
360
  test('diamond DAG with partial failure', async () => {
@@ -383,7 +372,7 @@ describe('runOrchestration', () => {
383
372
  expect(runAttemptStatuses).toEqual(['running', 'failed']);
384
373
 
385
374
  // A, B, C enqueued: D skipped because B failed
386
- expect(callsNamed('enqueueJobExecutionForRunner')).toHaveLength(3);
375
+ expect(callsNamed('queueJobExecutionActivity')).toHaveLength(3);
387
376
  const jobStatuses = setJobStatusCalls().map((c) => ({
388
377
  id: c.params.jobId,
389
378
  status: c.params.status,
@@ -401,7 +390,7 @@ describe('runOrchestration', () => {
401
390
  setCfg({
402
391
  dag: makeDag(jobs, 'r7'),
403
392
  jobResults: new Map(),
404
- enqueueError: 'Runner service unavailable',
393
+ queueError: 'Runner service unavailable',
405
394
  });
406
395
 
407
396
  await expect(executeRun()).rejects.toThrow();
@@ -9,7 +9,6 @@ import {
9
9
  } from '@temporalio/workflow';
10
10
  import {
11
11
  createRuntimeRunProgress,
12
- nonCompletedRuntimeJobIds,
13
12
  type RuntimeRunProgress,
14
13
  recordRuntimeJobResult,
15
14
  recordSkippedRuntimeJob,
@@ -26,15 +25,10 @@ import {deadlineReached, remainingMs} from './deadline.js';
26
25
  import {jobExecutionOrchestration} from './job-execution-orchestration.js';
27
26
  import {jobListenerOrchestration} from './job-listener-orchestration.js';
28
27
 
29
- const {
30
- loadRunAttemptDag,
31
- evaluateJobActivationsActivity,
32
- setRunAttemptStatus,
33
- setJobStatus,
34
- cancelRunnerJobsActivity,
35
- } = proxyActivities<ReturnType<typeof createOrchestrationActivities>>({
36
- startToCloseTimeout: '30s',
37
- });
28
+ const {loadRunAttemptDag, evaluateJobActivationsActivity, setRunAttemptStatus, setJobStatus} =
29
+ proxyActivities<ReturnType<typeof createOrchestrationActivities>>({
30
+ startToCloseTimeout: '30s',
31
+ });
38
32
 
39
33
  const {failRunAsTimedOutActivity} = proxyActivities<
40
34
  ReturnType<typeof createOrchestrationActivities>
@@ -74,12 +68,10 @@ export async function runOrchestration(input: RunOrchestrationInput): Promise<vo
74
68
 
75
69
  while (true) {
76
70
  if (cancelRequested) {
77
- await cancelNonCompletedRunnerJobs(dag, progress);
78
71
  return;
79
72
  }
80
73
  if (deadlineReached(runDeadline)) {
81
74
  await failRunAsTimedOutActivity({runAttemptId: input.runAttemptId});
82
- await cancelNonCompletedRunnerJobs(dag, progress);
83
75
  return;
84
76
  }
85
77
 
@@ -144,12 +136,10 @@ export async function runOrchestration(input: RunOrchestrationInput): Promise<vo
144
136
 
145
137
  const settled = await waitForNextSettlement(inFlight, () => cancelRequested, runDeadline);
146
138
  if (settled.kind === 'cancelled') {
147
- await cancelNonCompletedRunnerJobs(dag, progress);
148
139
  return;
149
140
  }
150
141
  if (settled.kind === 'timed-out') {
151
142
  await failRunAsTimedOutActivity({runAttemptId: input.runAttemptId});
152
- await cancelNonCompletedRunnerJobs(dag, progress);
153
143
  return;
154
144
  }
155
145
  inFlight.delete(settled.job.key);
@@ -187,9 +177,6 @@ function launchJob(
187
177
  workflowId: `job-listener:${job.id}`,
188
178
  args: [
189
179
  {
190
- workspaceId: run.workspaceId,
191
- workflowRunId: run.workflowRunId,
192
- projectId: run.projectId,
193
180
  jobId: job.id,
194
181
  runAttemptId: run.runAttemptId,
195
182
  jobVersion: runtimeJobVersion(job, progress),
@@ -235,9 +222,6 @@ function launchJob(
235
222
  workflowId: `job:${job.id}`,
236
223
  args: [
237
224
  {
238
- workspaceId: run.workspaceId,
239
- workflowRunId: run.workflowRunId,
240
- projectId: run.projectId,
241
225
  jobId: job.id,
242
226
  jobExecutionId: job.jobExecutionId,
243
227
  runAttemptId: run.runAttemptId,
@@ -273,11 +257,3 @@ async function waitForNextSettlement(
273
257
  );
274
258
  return await Promise.race([childSettled, cancel]);
275
259
  }
276
-
277
- async function cancelNonCompletedRunnerJobs(
278
- dag: RunDag,
279
- progress: RuntimeRunProgress,
280
- ): Promise<void> {
281
- const jobIds = nonCompletedRuntimeJobIds(dag.jobs, progress);
282
- await cancelRunnerJobsActivity({jobIds});
283
- }
@@ -25,11 +25,11 @@ export interface TestConfig {
25
25
  dag: RunDag;
26
26
  /** Map of jobId → status the mock runner should signal back */
27
27
  jobResults: Map<string, RuntimeCompletionStatus>;
28
- /** If set, enqueueJobExecutionForRunner will throw with this message instead of signaling */
29
- enqueueError?: string;
30
- /** If true, enqueueJobExecutionForRunner sends two job-finished signals (for dedup testing) */
28
+ /** If set, queueJobExecutionActivity will throw with this message instead of signaling */
29
+ queueError?: string;
30
+ /** If true, queueJobExecutionActivity sends two job-finished signals (for dedup testing) */
31
31
  duplicateSignal?: boolean;
32
- /** If true, enqueueJobExecutionForRunner does nothing without a signal for timeout testing */
32
+ /** If true, queueJobExecutionActivity does nothing without a signal for timeout testing */
33
33
  skipSignal?: boolean;
34
34
  /** If true, emit the claim but hold back the terminal outcome (for deadline testing) */
35
35
  skipOutcomeSignal?: boolean;
@@ -43,8 +43,6 @@ export interface TestConfig {
43
43
  leaseExpiredStatus?: RuntimeCompletionStatus;
44
44
  /** If set, resolveJobStatusFromJobExecutionsActivity throws with this message */
45
45
  resolveJobStatusError?: string;
46
- /** If set, releaseLeaseActivity throws (non-retryable) to prove the workflow still returns */
47
- releaseLeaseError?: string;
48
46
  /** Scripted job activation decisions keyed by job id; defaults to start */
49
47
  activationDecisions?: Map<string, JobActivationDecision>;
50
48
  /** If set, failJobExecutionAsTimedOutActivity throws (for timeout error-path testing) */
@@ -289,30 +287,19 @@ function createMockActivities() {
289
287
  calls.push({name: 'bulkSetStepStatuses', params});
290
288
  },
291
289
 
292
- cancelRunnerJobsActivity: (params: {jobIds: string[]}) => {
293
- calls.push({name: 'cancelRunnerJobsActivity', params});
294
- },
295
-
296
290
  // Scheduling is step-less. The mock runner reports the job outcome by signalling
297
291
  // the per-step terminal-completion signal (job-finished) and/or the lease-expiry
298
292
  // signal, reproducing the outbox → subscriber → signal rail.
299
- enqueueJobExecutionForRunner: async (params: {
300
- workspaceId: string;
301
- workflowRunId: string;
302
- runAttemptId: string;
303
- jobId: string;
304
- jobExecutionId: string;
305
- projectId: string;
306
- requiredLabels: string[];
307
- }) => {
308
- calls.push({name: 'enqueueJobExecutionForRunner', params});
293
+ queueJobExecutionActivity: async (params: {jobId: string; jobExecutionId: string}) => {
294
+ calls.push({name: 'queueJobExecutionActivity', params});
309
295
 
310
- if (cfg.enqueueError) {
296
+ if (cfg.queueError) {
311
297
  const {ApplicationFailure} = await import('@temporalio/common');
312
- throw ApplicationFailure.nonRetryable(cfg.enqueueError);
298
+ throw ApplicationFailure.nonRetryable(cfg.queueError);
313
299
  }
314
300
 
315
- if (cfg.skipSignal) return;
301
+ const queued = {newVersion: nextVersion(), status: 'pending' as const};
302
+ if (cfg.skipSignal) return queued;
316
303
 
317
304
  const status = cfg.jobResults.get(params.jobId) ?? 'succeeded';
318
305
  const handle = testEnv.client.workflow.getHandle(`job:${params.jobId}`);
@@ -321,17 +308,17 @@ function createMockActivities() {
321
308
  claimedAt: cfg.claimedAt ?? '2026-06-22T10:05:00.000Z',
322
309
  });
323
310
 
324
- if (cfg.skipOutcomeSignal) return;
311
+ if (cfg.skipOutcomeSignal) return queued;
325
312
 
326
313
  if (cfg.signalLeaseExpired) {
327
314
  await handle.signal(JOB_LEASE_EXPIRED_SIGNAL, {jobExecutionId: params.jobExecutionId});
328
- return;
315
+ return queued;
329
316
  }
330
317
 
331
318
  if (cfg.signalBoth) {
332
319
  await handle.signal(JOB_FINISHED_SIGNAL, {status, jobExecutionId: params.jobExecutionId});
333
320
  await handle.signal(JOB_LEASE_EXPIRED_SIGNAL, {jobExecutionId: params.jobExecutionId});
334
- return;
321
+ return queued;
335
322
  }
336
323
 
337
324
  // The terminal outbox is independent from the claim, but the mock delivers it from the
@@ -342,6 +329,7 @@ function createMockActivities() {
342
329
  status,
343
330
  duplicateSignal: cfg.duplicateSignal ?? false,
344
331
  });
332
+ return queued;
345
333
  },
346
334
 
347
335
  resolveLeaseExpiredJobExecutionActivity: (params: {
@@ -369,14 +357,6 @@ function createMockActivities() {
369
357
  };
370
358
  },
371
359
 
372
- releaseLeaseActivity: async (params: {jobExecutionId: string}) => {
373
- calls.push({name: 'releaseLeaseActivity', params});
374
- if (cfg.releaseLeaseError) {
375
- const {ApplicationFailure} = await import('@temporalio/common');
376
- throw ApplicationFailure.nonRetryable(cfg.releaseLeaseError);
377
- }
378
- },
379
-
380
360
  failJobExecutionAsTimedOutActivity: async (params: {
381
361
  jobExecutionId: string;
382
362
  runAttemptId: string;
@@ -35,9 +35,6 @@ export function resetRunnersTestClient(): void {
35
35
  }
36
36
 
37
37
  export const runnersTestClient: RunnersInterModuleClient = {
38
- enqueueJobExecution: async () => ({}),
39
- releaseJobExecution: async () => ({}),
40
- cancelJobs: async () => ({}),
41
38
  getLeaseState: async (params) => ({active: activeLeases.has(leaseKey(params))}),
42
39
  getEffectiveRunnerToolCapabilities: async ({runnerSessionId}) =>
43
40
  toolCapabilities.get(runnerSessionId) ?? {capabilities: {harnesses: {}}, reportFresh: false},
@@ -1,4 +1,5 @@
1
1
  import {
2
+ WORKFLOWS_JOB_EXECUTION_TERMINATED,
2
3
  WORKFLOWS_JOB_TERMINATED,
3
4
  WORKFLOWS_STEP_ATTEMPT_TERMINATED,
4
5
  WORKFLOWS_WORKFLOW_RUN_ATTEMPT_CREATED,
@@ -137,6 +138,30 @@ export async function jobTerminatedEvents(jobId: string) {
137
138
  );
138
139
  }
139
140
 
141
+ export async function jobExecutionTerminatedEvents(jobExecutionId: string) {
142
+ const rows = await db()
143
+ .select({payload: workflowsOutbox.payload})
144
+ .from(workflowsOutbox)
145
+ .where(
146
+ and(
147
+ eq(workflowsOutbox.eventType, WORKFLOWS_JOB_EXECUTION_TERMINATED),
148
+ sql`${workflowsOutbox.payload}->>'jobExecutionId' = ${jobExecutionId}`,
149
+ ),
150
+ );
151
+ return rows.map(
152
+ (row) =>
153
+ row.payload as {
154
+ jobId: string;
155
+ jobExecutionId: string;
156
+ workflowRunId: string;
157
+ workflowRunAttemptId: string;
158
+ status: string;
159
+ statusReason: string | null;
160
+ statusReasonMessage?: string | null;
161
+ },
162
+ );
163
+ }
164
+
140
165
  export async function runTerminatedEvents(workflowRunId: string) {
141
166
  const rows = await db()
142
167
  .select({payload: workflowsOutbox.payload})