@shipfox/api-runners 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 (78) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +20 -0
  3. package/dist/db/index.d.ts +1 -1
  4. package/dist/db/index.d.ts.map +1 -1
  5. package/dist/db/index.js +1 -1
  6. package/dist/db/index.js.map +1 -1
  7. package/dist/db/job-executions.d.ts +8 -21
  8. package/dist/db/job-executions.d.ts.map +1 -1
  9. package/dist/db/job-executions.js +63 -60
  10. package/dist/db/job-executions.js.map +1 -1
  11. package/dist/db/provisioner-tokens.d.ts.map +1 -1
  12. package/dist/db/provisioner-tokens.js +14 -2
  13. package/dist/db/provisioner-tokens.js.map +1 -1
  14. package/dist/db/reservations.d.ts +10 -0
  15. package/dist/db/reservations.d.ts.map +1 -1
  16. package/dist/db/reservations.js +139 -17
  17. package/dist/db/reservations.js.map +1 -1
  18. package/dist/db/runner-assignments.d.ts.map +1 -1
  19. package/dist/db/runner-assignments.js +8 -5
  20. package/dist/db/runner-assignments.js.map +1 -1
  21. package/dist/db/runner-instances.d.ts +0 -1
  22. package/dist/db/runner-instances.d.ts.map +1 -1
  23. package/dist/db/runner-instances.js +85 -100
  24. package/dist/db/runner-instances.js.map +1 -1
  25. package/dist/db/runner-states.d.ts +3 -1
  26. package/dist/db/runner-states.d.ts.map +1 -1
  27. package/dist/db/runner-states.js +6 -4
  28. package/dist/db/runner-states.js.map +1 -1
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +5 -4
  32. package/dist/index.js.map +1 -1
  33. package/dist/presentation/index.d.ts +2 -1
  34. package/dist/presentation/index.d.ts.map +1 -1
  35. package/dist/presentation/index.js +2 -1
  36. package/dist/presentation/index.js.map +1 -1
  37. package/dist/presentation/inter-module.d.ts +0 -1
  38. package/dist/presentation/inter-module.d.ts.map +1 -1
  39. package/dist/presentation/inter-module.js +2 -25
  40. package/dist/presentation/inter-module.js.map +1 -1
  41. package/dist/presentation/subscribers/on-workflows-job-execution-queued.d.ts +3 -0
  42. package/dist/presentation/subscribers/on-workflows-job-execution-queued.d.ts.map +1 -0
  43. package/dist/presentation/subscribers/on-workflows-job-execution-queued.js +21 -0
  44. package/dist/presentation/subscribers/on-workflows-job-execution-queued.js.map +1 -0
  45. package/dist/presentation/subscribers/on-workflows-job-execution-terminated.d.ts +3 -0
  46. package/dist/presentation/subscribers/on-workflows-job-execution-terminated.d.ts.map +1 -0
  47. package/dist/presentation/subscribers/{on-workflows-job-execution-timed-out.js → on-workflows-job-execution-terminated.js} +5 -4
  48. package/dist/presentation/subscribers/on-workflows-job-execution-terminated.js.map +1 -0
  49. package/dist/tsconfig.test.tsbuildinfo +1 -1
  50. package/package.json +3 -3
  51. package/src/db/index.ts +0 -2
  52. package/src/db/job-executions.test.ts +106 -217
  53. package/src/db/job-executions.ts +71 -70
  54. package/src/db/provisioner-tokens.test.ts +19 -0
  55. package/src/db/provisioner-tokens.ts +13 -2
  56. package/src/db/reservations.test.ts +138 -0
  57. package/src/db/reservations.ts +314 -35
  58. package/src/db/runner-assignments.test.ts +20 -0
  59. package/src/db/runner-assignments.ts +6 -7
  60. package/src/db/runner-instances.test.ts +350 -15
  61. package/src/db/runner-instances.ts +136 -198
  62. package/src/db/runner-states.test.ts +16 -0
  63. package/src/db/runner-states.ts +9 -4
  64. package/src/index.test.ts +1 -0
  65. package/src/index.ts +8 -7
  66. package/src/presentation/index.ts +2 -1
  67. package/src/presentation/inter-module.ts +2 -40
  68. package/src/presentation/subscribers/on-workflows-job-execution-queued.test.ts +57 -0
  69. package/src/presentation/subscribers/on-workflows-job-execution-queued.ts +26 -0
  70. package/src/presentation/subscribers/on-workflows-job-execution-terminated.test.ts +142 -0
  71. package/src/presentation/subscribers/{on-workflows-job-execution-timed-out.ts → on-workflows-job-execution-terminated.ts} +5 -4
  72. package/test/factories/pending-job.ts +3 -0
  73. package/tsconfig.build.tsbuildinfo +1 -1
  74. package/dist/presentation/subscribers/on-workflows-job-execution-timed-out.d.ts +0 -3
  75. package/dist/presentation/subscribers/on-workflows-job-execution-timed-out.d.ts.map +0 -1
  76. package/dist/presentation/subscribers/on-workflows-job-execution-timed-out.js.map +0 -1
  77. package/src/presentation/inter-module.test.ts +0 -15
  78. package/src/presentation/subscribers/on-workflows-job-execution-timed-out.test.ts +0 -163
@@ -1,6 +1,6 @@
1
1
  $ shipfox-swc && shipfox-temporal-bundle dist/temporal/workflows/index.js
2
- Successfully compiled: 104 files with swc (558.99ms)
3
- 2026-08-10T11:40:54.402Z [INFO] asset workflow-bundle-0c3da2487e29e352d64e.js 3.17 MiB [emitted] [immutable] (name: main)
2
+ Successfully compiled: 105 files with swc (500.52ms)
3
+ 2026-08-14T18:57:08.640Z [INFO] asset workflow-bundle-0c3da2487e29e352d64e.js 3.17 MiB [emitted] [immutable] (name: main)
4
4
  orphan modules 33.5 KiB [orphan] 21 modules
5
5
  runtime modules 1.13 KiB 5 modules
6
6
  modules by path ../../../node_modules/.pnpm/ 970 KiB 196 modules
@@ -14,6 +14,6 @@ optional modules 30 bytes [optional]
14
14
  __temporal_custom_payload_converter (ignored) 15 bytes [optional] [built] [code generated]
15
15
  __temporal_custom_failure_converter (ignored) 15 bytes [optional] [built] [code generated]
16
16
  ../../shared/node/temporal/dist/workflow-error-interceptor.js 1.21 KiB [built] [code generated]
17
- webpack 5.107.2 compiled successfully in 6787 ms
18
- 2026-08-10T11:40:54.408Z [INFO] Workflow bundle created { size: '3.17MB' }
17
+ webpack 5.107.2 compiled successfully in 6538 ms
18
+ 2026-08-14T18:57:08.663Z [INFO] Workflow bundle created { size: '3.17MB' }
19
19
  /home/runner/work/shipfox/shipfox/libs/api/runners/dist/temporal/workflows/index.js: 3326598 bytes
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @shipfox/api-runners
2
2
 
3
+ ## 13.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - af6b31e: Expose ordered queued and terminal workflow job-execution facts, and release terminal runner reservations once no uncancelled lease remains.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [af6b31e]
12
+ - @shipfox/api-runners-dto@13.0.0
13
+ - @shipfox/api-workflows-dto@13.0.0
14
+
15
+ ## 12.7.0
16
+
17
+ ### Patch Changes
18
+
19
+ - 7a5e247: Release runner reservation units when a provisioner reports a terminal runner.
20
+ - Updated dependencies [4df5e37]
21
+ - @shipfox/api-workflows-dto@12.7.0
22
+
3
23
  ## 12.6.0
4
24
 
5
25
  ### Patch Changes
@@ -4,7 +4,7 @@ export { closeDb, db, schema } from './db.js';
4
4
  export type { CreateEphemeralRegistrationTokenParams, DeleteExpiredEphemeralRegistrationTokensParams, } from './ephemeral-registration-tokens.js';
5
5
  export { createEphemeralRegistrationToken, createRunnerSessionConsumingEphemeralToken, deleteExpiredEphemeralRegistrationTokens, resolveEphemeralRegistrationTokenByHash, } from './ephemeral-registration-tokens.js';
6
6
  export type { ClaimedJobExecution, EnqueueJobExecutionParams, RunnerInstanceBoundJobExecution, } from './job-executions.js';
7
- export { cancelRunnerJobs, claimPendingJobExecution, enqueueJobExecution, expireStuckJobExecutions, getWorkspaceJobCounts, isJobLeaseActive, listActiveRunningJobExecutions, listRunningJobExecutionsByRunnerInstanceTx, recordHeartbeat, releaseJobExecution, } from './job-executions.js';
7
+ export { claimPendingJobExecution, enqueueJobExecution, expireStuckJobExecutions, getWorkspaceJobCounts, isJobLeaseActive, listActiveRunningJobExecutions, listRunningJobExecutionsByRunnerInstanceTx, recordHeartbeat, } from './job-executions.js';
8
8
  export type { CreateManualRegistrationTokenParams } from './manual-registration-tokens.js';
9
9
  export { createManualRegistrationToken, listUsableManualRegistrationTokensByWorkspaceId, resolveManualRegistrationTokenByHash, revokeManualRegistrationToken, } from './manual-registration-tokens.js';
10
10
  export { hasActiveWorkspaceProvisionerCapability, listActiveWorkspaceProvisionerCapabilitySnapshots, listStaleWorkspaceProvisionerCapabilitySnapshots, publishWorkspaceProvisionerCapabilitySnapshot, } from './provisioner-capability-snapshots.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,mCAAmC,EACnC,8BAA8B,EAC9B,sCAAsC,EACtC,sCAAsC,GACvC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAC,gCAAgC,EAAC,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAC,OAAO,EAAE,EAAE,EAAE,MAAM,EAAC,MAAM,SAAS,CAAC;AAC5C,YAAY,EACV,sCAAsC,EACtC,8CAA8C,GAC/C,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,gCAAgC,EAChC,0CAA0C,EAC1C,wCAAwC,EACxC,uCAAuC,GACxC,MAAM,oCAAoC,CAAC;AAC5C,YAAY,EACV,mBAAmB,EACnB,yBAAyB,EACzB,+BAA+B,GAChC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,EAChB,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,qBAAqB,EACrB,gBAAgB,EAChB,8BAA8B,EAC9B,0CAA0C,EAC1C,eAAe,EACf,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAC,mCAAmC,EAAC,MAAM,iCAAiC,CAAC;AACzF,OAAO,EACL,6BAA6B,EAC7B,+CAA+C,EAC/C,oCAAoC,EACpC,6BAA6B,GAC9B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,uCAAuC,EACvC,iDAAiD,EACjD,gDAAgD,EAChD,6CAA6C,GAC9C,MAAM,uCAAuC,CAAC;AAC/C,YAAY,EAAC,4BAA4B,EAAC,MAAM,yBAAyB,CAAC;AAC1E,OAAO,EACL,2CAA2C,EAC3C,sBAAsB,EACtB,2BAA2B,EAC3B,iCAAiC,EACjC,wCAAwC,EACxC,6BAA6B,EAC7B,2CAA2C,EAC3C,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,UAAU,EACV,0BAA0B,EAC1B,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAC,qBAAqB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,YAAY,EACV,iCAAiC,EACjC,8BAA8B,EAC9B,gCAAgC,EAChC,8BAA8B,EAC9B,2BAA2B,EAC3B,yBAAyB,EACzB,6BAA6B,EAC7B,mCAAmC,GACpC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,8BAA8B,EAC9B,iCAAiC,EACjC,eAAe,EACf,0CAA0C,EAC1C,yBAAyB,EACzB,oCAAoC,EACpC,+BAA+B,EAC/B,wBAAwB,EACxB,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,yBAAyB,EACzB,iCAAiC,GAClC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAC,mBAAmB,EAAE,2BAA2B,EAAC,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAC,qBAAqB,EAAE,qBAAqB,EAAC,MAAM,qCAAqC,CAAC;AAEjG,eAAO,MAAM,cAAc,QAAoE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,mCAAmC,EACnC,8BAA8B,EAC9B,sCAAsC,EACtC,sCAAsC,GACvC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAC,gCAAgC,EAAC,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAC,OAAO,EAAE,EAAE,EAAE,MAAM,EAAC,MAAM,SAAS,CAAC;AAC5C,YAAY,EACV,sCAAsC,EACtC,8CAA8C,GAC/C,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,gCAAgC,EAChC,0CAA0C,EAC1C,wCAAwC,EACxC,uCAAuC,GACxC,MAAM,oCAAoC,CAAC;AAC5C,YAAY,EACV,mBAAmB,EACnB,yBAAyB,EACzB,+BAA+B,GAChC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,qBAAqB,EACrB,gBAAgB,EAChB,8BAA8B,EAC9B,0CAA0C,EAC1C,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAC,mCAAmC,EAAC,MAAM,iCAAiC,CAAC;AACzF,OAAO,EACL,6BAA6B,EAC7B,+CAA+C,EAC/C,oCAAoC,EACpC,6BAA6B,GAC9B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,uCAAuC,EACvC,iDAAiD,EACjD,gDAAgD,EAChD,6CAA6C,GAC9C,MAAM,uCAAuC,CAAC;AAC/C,YAAY,EAAC,4BAA4B,EAAC,MAAM,yBAAyB,CAAC;AAC1E,OAAO,EACL,2CAA2C,EAC3C,sBAAsB,EACtB,2BAA2B,EAC3B,iCAAiC,EACjC,wCAAwC,EACxC,6BAA6B,EAC7B,2CAA2C,EAC3C,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,UAAU,EACV,0BAA0B,EAC1B,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAC,qBAAqB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,YAAY,EACV,iCAAiC,EACjC,8BAA8B,EAC9B,gCAAgC,EAChC,8BAA8B,EAC9B,2BAA2B,EAC3B,yBAAyB,EACzB,6BAA6B,EAC7B,mCAAmC,GACpC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,8BAA8B,EAC9B,iCAAiC,EACjC,eAAe,EACf,0CAA0C,EAC1C,yBAAyB,EACzB,oCAAoC,EACpC,+BAA+B,EAC/B,wBAAwB,EACxB,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,yBAAyB,EACzB,iCAAiC,GAClC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAC,mBAAmB,EAAE,2BAA2B,EAAC,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAC,qBAAqB,EAAE,qBAAqB,EAAC,MAAM,qCAAqC,CAAC;AAEjG,eAAO,MAAM,cAAc,QAAoE,CAAC"}
package/dist/db/index.js CHANGED
@@ -3,7 +3,7 @@ import { fileURLToPath } from 'node:url';
3
3
  export { listAdministratorRunnerInstances } from './admin-runner-instances.js';
4
4
  export { closeDb, db, schema } from './db.js';
5
5
  export { createEphemeralRegistrationToken, createRunnerSessionConsumingEphemeralToken, deleteExpiredEphemeralRegistrationTokens, resolveEphemeralRegistrationTokenByHash } from './ephemeral-registration-tokens.js';
6
- export { cancelRunnerJobs, claimPendingJobExecution, enqueueJobExecution, expireStuckJobExecutions, getWorkspaceJobCounts, isJobLeaseActive, listActiveRunningJobExecutions, listRunningJobExecutionsByRunnerInstanceTx, recordHeartbeat, releaseJobExecution } from './job-executions.js';
6
+ export { claimPendingJobExecution, enqueueJobExecution, expireStuckJobExecutions, getWorkspaceJobCounts, isJobLeaseActive, listActiveRunningJobExecutions, listRunningJobExecutionsByRunnerInstanceTx, recordHeartbeat } from './job-executions.js';
7
7
  export { createManualRegistrationToken, listUsableManualRegistrationTokensByWorkspaceId, resolveManualRegistrationTokenByHash, revokeManualRegistrationToken } from './manual-registration-tokens.js';
8
8
  export { hasActiveWorkspaceProvisionerCapability, listActiveWorkspaceProvisionerCapabilitySnapshots, listStaleWorkspaceProvisionerCapabilitySnapshots, publishWorkspaceProvisionerCapabilitySnapshot } from './provisioner-capability-snapshots.js';
9
9
  export { createInstallationProvisionerTokenWithAudit, createProvisionerToken, listActiveProvisionerTokens, listInstallationProvisionerTokens, listUsableProvisionerTokensByWorkspaceId, resolveProvisionerTokenByHash, revokeInstallationProvisionerTokenWithAudit, revokeProvisionerToken, touchProvisionerLastSeen } from './provisioner-tokens.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/db/index.ts"],"sourcesContent":["import {dirname, resolve} from 'node:path';\nimport {fileURLToPath} from 'node:url';\n\nexport type {\n AdministratorRunnerAssignmentFilter,\n AdministratorRunnerInstanceRow,\n ListAdministratorRunnerInstancesParams,\n ListAdministratorRunnerInstancesResult,\n} from './admin-runner-instances.js';\nexport {listAdministratorRunnerInstances} from './admin-runner-instances.js';\nexport {closeDb, db, schema} from './db.js';\nexport type {\n CreateEphemeralRegistrationTokenParams,\n DeleteExpiredEphemeralRegistrationTokensParams,\n} from './ephemeral-registration-tokens.js';\nexport {\n createEphemeralRegistrationToken,\n createRunnerSessionConsumingEphemeralToken,\n deleteExpiredEphemeralRegistrationTokens,\n resolveEphemeralRegistrationTokenByHash,\n} from './ephemeral-registration-tokens.js';\nexport type {\n ClaimedJobExecution,\n EnqueueJobExecutionParams,\n RunnerInstanceBoundJobExecution,\n} from './job-executions.js';\nexport {\n cancelRunnerJobs,\n claimPendingJobExecution,\n enqueueJobExecution,\n expireStuckJobExecutions,\n getWorkspaceJobCounts,\n isJobLeaseActive,\n listActiveRunningJobExecutions,\n listRunningJobExecutionsByRunnerInstanceTx,\n recordHeartbeat,\n releaseJobExecution,\n} from './job-executions.js';\nexport type {CreateManualRegistrationTokenParams} from './manual-registration-tokens.js';\nexport {\n createManualRegistrationToken,\n listUsableManualRegistrationTokensByWorkspaceId,\n resolveManualRegistrationTokenByHash,\n revokeManualRegistrationToken,\n} from './manual-registration-tokens.js';\nexport {\n hasActiveWorkspaceProvisionerCapability,\n listActiveWorkspaceProvisionerCapabilitySnapshots,\n listStaleWorkspaceProvisionerCapabilitySnapshots,\n publishWorkspaceProvisionerCapabilitySnapshot,\n} from './provisioner-capability-snapshots.js';\nexport type {CreateProvisionerTokenParams} from './provisioner-tokens.js';\nexport {\n createInstallationProvisionerTokenWithAudit,\n createProvisionerToken,\n listActiveProvisionerTokens,\n listInstallationProvisionerTokens,\n listUsableProvisionerTokensByWorkspaceId,\n resolveProvisionerTokenByHash,\n revokeInstallationProvisionerTokenWithAudit,\n revokeProvisionerToken,\n touchProvisionerLastSeen,\n} from './provisioner-tokens.js';\nexport {\n consumeRunnersRateLimit,\n pruneExpiredRunnersRateLimits,\n} from './rate-limits.js';\nexport type {\n DemandStat,\n PollDemandAndReserveParams,\n ReservationGrant,\n ReservationTemplate,\n} from './reservations.js';\nexport {\n deleteExpiredReservations,\n deleteReservationsByIds,\n pollDemandAndReserve,\n releaseReservationUnits,\n} from './reservations.js';\nexport {assignRunnerInstances} from './runner-assignments.js';\nexport type {\n ActiveRunnerInstanceTemplateCount,\n ReapStaleRunnerInstancesResult,\n ReconcileRunnerInstancesDbResult,\n ReconcileRunnerInstancesParams,\n ReportRunnerInstancesParams,\n RunnerInstanceReportEvent,\n RunnerInstanceTerminateIntent,\n RunnerInstanceTerminateIntentReason,\n} from './runner-instances.js';\nexport {\n attachRunnerInstanceProviderId,\n countStaleEnrolledRunnerInstances,\n isTerminalState,\n listActiveRunnerInstanceCountsByTemplateTx,\n listActiveRunnerInstances,\n listProvisionerTerminateIntentRowsTx,\n listProvisionerTerminateIntents,\n reapStaleRunnerInstances,\n reconcileRunnerInstances,\n reportRunnerInstances,\n} from './runner-instances.js';\nexport type {\n CreateRunnerSessionParams,\n DeleteExpiredRunnerSessionsParams,\n} from './runner-sessions.js';\nexport {createRunnerSession, deleteExpiredRunnerSessions} from './runner-sessions.js';\nexport {runnersOutbox} from './schema/outbox.js';\nexport {runnerBootstrapTokens, runnerControlSessions} from './schema/runner-control-sessions.js';\n\nexport const migrationsPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../drizzle');\n"],"names":["dirname","resolve","fileURLToPath","listAdministratorRunnerInstances","closeDb","db","schema","createEphemeralRegistrationToken","createRunnerSessionConsumingEphemeralToken","deleteExpiredEphemeralRegistrationTokens","resolveEphemeralRegistrationTokenByHash","cancelRunnerJobs","claimPendingJobExecution","enqueueJobExecution","expireStuckJobExecutions","getWorkspaceJobCounts","isJobLeaseActive","listActiveRunningJobExecutions","listRunningJobExecutionsByRunnerInstanceTx","recordHeartbeat","releaseJobExecution","createManualRegistrationToken","listUsableManualRegistrationTokensByWorkspaceId","resolveManualRegistrationTokenByHash","revokeManualRegistrationToken","hasActiveWorkspaceProvisionerCapability","listActiveWorkspaceProvisionerCapabilitySnapshots","listStaleWorkspaceProvisionerCapabilitySnapshots","publishWorkspaceProvisionerCapabilitySnapshot","createInstallationProvisionerTokenWithAudit","createProvisionerToken","listActiveProvisionerTokens","listInstallationProvisionerTokens","listUsableProvisionerTokensByWorkspaceId","resolveProvisionerTokenByHash","revokeInstallationProvisionerTokenWithAudit","revokeProvisionerToken","touchProvisionerLastSeen","consumeRunnersRateLimit","pruneExpiredRunnersRateLimits","deleteExpiredReservations","deleteReservationsByIds","pollDemandAndReserve","releaseReservationUnits","assignRunnerInstances","attachRunnerInstanceProviderId","countStaleEnrolledRunnerInstances","isTerminalState","listActiveRunnerInstanceCountsByTemplateTx","listActiveRunnerInstances","listProvisionerTerminateIntentRowsTx","listProvisionerTerminateIntents","reapStaleRunnerInstances","reconcileRunnerInstances","reportRunnerInstances","createRunnerSession","deleteExpiredRunnerSessions","runnersOutbox","runnerBootstrapTokens","runnerControlSessions","migrationsPath","url"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,OAAO,QAAO,YAAY;AAC3C,SAAQC,aAAa,QAAO,WAAW;AAQvC,SAAQC,gCAAgC,QAAO,8BAA8B;AAC7E,SAAQC,OAAO,EAAEC,EAAE,EAAEC,MAAM,QAAO,UAAU;AAK5C,SACEC,gCAAgC,EAChCC,0CAA0C,EAC1CC,wCAAwC,EACxCC,uCAAuC,QAClC,qCAAqC;AAM5C,SACEC,gBAAgB,EAChBC,wBAAwB,EACxBC,mBAAmB,EACnBC,wBAAwB,EACxBC,qBAAqB,EACrBC,gBAAgB,EAChBC,8BAA8B,EAC9BC,0CAA0C,EAC1CC,eAAe,EACfC,mBAAmB,QACd,sBAAsB;AAE7B,SACEC,6BAA6B,EAC7BC,+CAA+C,EAC/CC,oCAAoC,EACpCC,6BAA6B,QACxB,kCAAkC;AACzC,SACEC,uCAAuC,EACvCC,iDAAiD,EACjDC,gDAAgD,EAChDC,6CAA6C,QACxC,wCAAwC;AAE/C,SACEC,2CAA2C,EAC3CC,sBAAsB,EACtBC,2BAA2B,EAC3BC,iCAAiC,EACjCC,wCAAwC,EACxCC,6BAA6B,EAC7BC,2CAA2C,EAC3CC,sBAAsB,EACtBC,wBAAwB,QACnB,0BAA0B;AACjC,SACEC,uBAAuB,EACvBC,6BAA6B,QACxB,mBAAmB;AAO1B,SACEC,yBAAyB,EACzBC,uBAAuB,EACvBC,oBAAoB,EACpBC,uBAAuB,QAClB,oBAAoB;AAC3B,SAAQC,qBAAqB,QAAO,0BAA0B;AAW9D,SACEC,8BAA8B,EAC9BC,iCAAiC,EACjCC,eAAe,EACfC,0CAA0C,EAC1CC,yBAAyB,EACzBC,oCAAoC,EACpCC,+BAA+B,EAC/BC,wBAAwB,EACxBC,wBAAwB,EACxBC,qBAAqB,QAChB,wBAAwB;AAK/B,SAAQC,mBAAmB,EAAEC,2BAA2B,QAAO,uBAAuB;AACtF,SAAQC,aAAa,QAAO,qBAAqB;AACjD,SAAQC,qBAAqB,EAAEC,qBAAqB,QAAO,sCAAsC;AAEjG,OAAO,MAAMC,iBAAiB3D,QAAQD,QAAQE,cAAc,YAAY2D,GAAG,IAAI,iBAAiB"}
1
+ {"version":3,"sources":["../../src/db/index.ts"],"sourcesContent":["import {dirname, resolve} from 'node:path';\nimport {fileURLToPath} from 'node:url';\n\nexport type {\n AdministratorRunnerAssignmentFilter,\n AdministratorRunnerInstanceRow,\n ListAdministratorRunnerInstancesParams,\n ListAdministratorRunnerInstancesResult,\n} from './admin-runner-instances.js';\nexport {listAdministratorRunnerInstances} from './admin-runner-instances.js';\nexport {closeDb, db, schema} from './db.js';\nexport type {\n CreateEphemeralRegistrationTokenParams,\n DeleteExpiredEphemeralRegistrationTokensParams,\n} from './ephemeral-registration-tokens.js';\nexport {\n createEphemeralRegistrationToken,\n createRunnerSessionConsumingEphemeralToken,\n deleteExpiredEphemeralRegistrationTokens,\n resolveEphemeralRegistrationTokenByHash,\n} from './ephemeral-registration-tokens.js';\nexport type {\n ClaimedJobExecution,\n EnqueueJobExecutionParams,\n RunnerInstanceBoundJobExecution,\n} from './job-executions.js';\nexport {\n claimPendingJobExecution,\n enqueueJobExecution,\n expireStuckJobExecutions,\n getWorkspaceJobCounts,\n isJobLeaseActive,\n listActiveRunningJobExecutions,\n listRunningJobExecutionsByRunnerInstanceTx,\n recordHeartbeat,\n} from './job-executions.js';\nexport type {CreateManualRegistrationTokenParams} from './manual-registration-tokens.js';\nexport {\n createManualRegistrationToken,\n listUsableManualRegistrationTokensByWorkspaceId,\n resolveManualRegistrationTokenByHash,\n revokeManualRegistrationToken,\n} from './manual-registration-tokens.js';\nexport {\n hasActiveWorkspaceProvisionerCapability,\n listActiveWorkspaceProvisionerCapabilitySnapshots,\n listStaleWorkspaceProvisionerCapabilitySnapshots,\n publishWorkspaceProvisionerCapabilitySnapshot,\n} from './provisioner-capability-snapshots.js';\nexport type {CreateProvisionerTokenParams} from './provisioner-tokens.js';\nexport {\n createInstallationProvisionerTokenWithAudit,\n createProvisionerToken,\n listActiveProvisionerTokens,\n listInstallationProvisionerTokens,\n listUsableProvisionerTokensByWorkspaceId,\n resolveProvisionerTokenByHash,\n revokeInstallationProvisionerTokenWithAudit,\n revokeProvisionerToken,\n touchProvisionerLastSeen,\n} from './provisioner-tokens.js';\nexport {\n consumeRunnersRateLimit,\n pruneExpiredRunnersRateLimits,\n} from './rate-limits.js';\nexport type {\n DemandStat,\n PollDemandAndReserveParams,\n ReservationGrant,\n ReservationTemplate,\n} from './reservations.js';\nexport {\n deleteExpiredReservations,\n deleteReservationsByIds,\n pollDemandAndReserve,\n releaseReservationUnits,\n} from './reservations.js';\nexport {assignRunnerInstances} from './runner-assignments.js';\nexport type {\n ActiveRunnerInstanceTemplateCount,\n ReapStaleRunnerInstancesResult,\n ReconcileRunnerInstancesDbResult,\n ReconcileRunnerInstancesParams,\n ReportRunnerInstancesParams,\n RunnerInstanceReportEvent,\n RunnerInstanceTerminateIntent,\n RunnerInstanceTerminateIntentReason,\n} from './runner-instances.js';\nexport {\n attachRunnerInstanceProviderId,\n countStaleEnrolledRunnerInstances,\n isTerminalState,\n listActiveRunnerInstanceCountsByTemplateTx,\n listActiveRunnerInstances,\n listProvisionerTerminateIntentRowsTx,\n listProvisionerTerminateIntents,\n reapStaleRunnerInstances,\n reconcileRunnerInstances,\n reportRunnerInstances,\n} from './runner-instances.js';\nexport type {\n CreateRunnerSessionParams,\n DeleteExpiredRunnerSessionsParams,\n} from './runner-sessions.js';\nexport {createRunnerSession, deleteExpiredRunnerSessions} from './runner-sessions.js';\nexport {runnersOutbox} from './schema/outbox.js';\nexport {runnerBootstrapTokens, runnerControlSessions} from './schema/runner-control-sessions.js';\n\nexport const migrationsPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../drizzle');\n"],"names":["dirname","resolve","fileURLToPath","listAdministratorRunnerInstances","closeDb","db","schema","createEphemeralRegistrationToken","createRunnerSessionConsumingEphemeralToken","deleteExpiredEphemeralRegistrationTokens","resolveEphemeralRegistrationTokenByHash","claimPendingJobExecution","enqueueJobExecution","expireStuckJobExecutions","getWorkspaceJobCounts","isJobLeaseActive","listActiveRunningJobExecutions","listRunningJobExecutionsByRunnerInstanceTx","recordHeartbeat","createManualRegistrationToken","listUsableManualRegistrationTokensByWorkspaceId","resolveManualRegistrationTokenByHash","revokeManualRegistrationToken","hasActiveWorkspaceProvisionerCapability","listActiveWorkspaceProvisionerCapabilitySnapshots","listStaleWorkspaceProvisionerCapabilitySnapshots","publishWorkspaceProvisionerCapabilitySnapshot","createInstallationProvisionerTokenWithAudit","createProvisionerToken","listActiveProvisionerTokens","listInstallationProvisionerTokens","listUsableProvisionerTokensByWorkspaceId","resolveProvisionerTokenByHash","revokeInstallationProvisionerTokenWithAudit","revokeProvisionerToken","touchProvisionerLastSeen","consumeRunnersRateLimit","pruneExpiredRunnersRateLimits","deleteExpiredReservations","deleteReservationsByIds","pollDemandAndReserve","releaseReservationUnits","assignRunnerInstances","attachRunnerInstanceProviderId","countStaleEnrolledRunnerInstances","isTerminalState","listActiveRunnerInstanceCountsByTemplateTx","listActiveRunnerInstances","listProvisionerTerminateIntentRowsTx","listProvisionerTerminateIntents","reapStaleRunnerInstances","reconcileRunnerInstances","reportRunnerInstances","createRunnerSession","deleteExpiredRunnerSessions","runnersOutbox","runnerBootstrapTokens","runnerControlSessions","migrationsPath","url"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,OAAO,QAAO,YAAY;AAC3C,SAAQC,aAAa,QAAO,WAAW;AAQvC,SAAQC,gCAAgC,QAAO,8BAA8B;AAC7E,SAAQC,OAAO,EAAEC,EAAE,EAAEC,MAAM,QAAO,UAAU;AAK5C,SACEC,gCAAgC,EAChCC,0CAA0C,EAC1CC,wCAAwC,EACxCC,uCAAuC,QAClC,qCAAqC;AAM5C,SACEC,wBAAwB,EACxBC,mBAAmB,EACnBC,wBAAwB,EACxBC,qBAAqB,EACrBC,gBAAgB,EAChBC,8BAA8B,EAC9BC,0CAA0C,EAC1CC,eAAe,QACV,sBAAsB;AAE7B,SACEC,6BAA6B,EAC7BC,+CAA+C,EAC/CC,oCAAoC,EACpCC,6BAA6B,QACxB,kCAAkC;AACzC,SACEC,uCAAuC,EACvCC,iDAAiD,EACjDC,gDAAgD,EAChDC,6CAA6C,QACxC,wCAAwC;AAE/C,SACEC,2CAA2C,EAC3CC,sBAAsB,EACtBC,2BAA2B,EAC3BC,iCAAiC,EACjCC,wCAAwC,EACxCC,6BAA6B,EAC7BC,2CAA2C,EAC3CC,sBAAsB,EACtBC,wBAAwB,QACnB,0BAA0B;AACjC,SACEC,uBAAuB,EACvBC,6BAA6B,QACxB,mBAAmB;AAO1B,SACEC,yBAAyB,EACzBC,uBAAuB,EACvBC,oBAAoB,EACpBC,uBAAuB,QAClB,oBAAoB;AAC3B,SAAQC,qBAAqB,QAAO,0BAA0B;AAW9D,SACEC,8BAA8B,EAC9BC,iCAAiC,EACjCC,eAAe,EACfC,0CAA0C,EAC1CC,yBAAyB,EACzBC,oCAAoC,EACpCC,+BAA+B,EAC/BC,wBAAwB,EACxBC,wBAAwB,EACxBC,qBAAqB,QAChB,wBAAwB;AAK/B,SAAQC,mBAAmB,EAAEC,2BAA2B,QAAO,uBAAuB;AACtF,SAAQC,aAAa,QAAO,qBAAqB;AACjD,SAAQC,qBAAqB,EAAEC,qBAAqB,QAAO,sCAAsC;AAEjG,OAAO,MAAMC,iBAAiBzD,QAAQD,QAAQE,cAAc,YAAYyD,GAAG,IAAI,iBAAiB"}
@@ -8,6 +8,7 @@ export interface EnqueueJobExecutionParams {
8
8
  jobExecutionId: string;
9
9
  projectId: string;
10
10
  requiredLabels: string[];
11
+ queuedAt: Date;
11
12
  }
12
13
  export declare function getWorkspaceJobCounts(params: {
13
14
  workspaceIds: string[];
@@ -55,29 +56,18 @@ export declare function claimPendingJobExecution(params: {
55
56
  maxClaims: number | null;
56
57
  runnerSessionLivenessThrottleSeconds: number;
57
58
  }): Promise<ClaimedJobExecution | null>;
58
- /**
59
- * Releases a job execution's lease when the orchestration workflow finalizes it: deletes the
60
- * running-job-execution row AND any lingering pending row for the same execution, in one tx.
61
- * Idempotent (0-row no-op), no token scope (the workflow is authoritative), and
62
- * emits no event: the workflow already owns the outcome. Sweeping the pending row
63
- * too closes the at-least-once window where an enqueue retry left an orphan that a
64
- * later claim would otherwise pick up for an already-finished job execution.
65
- */
66
- export declare function releaseJobExecution(params: {
67
- jobExecutionId: string;
68
- }): Promise<void>;
69
59
  /**
70
60
  * Reaps stale leases (bounded by `limit`), emitting one
71
61
  * `runners.job.lease_expired` event per reaped job execution.
72
62
  *
73
63
  * The cutoff is re-checked in the DELETE, not just the locking subquery, so a
74
64
  * heartbeat landing mid-call spares the live row. Each reaped execution also sweeps its
75
- * pending row: a failed best-effort `releaseJobExecution` would otherwise leave an orphan
76
- * that a later claim re-runs as an already-finished job execution.
65
+ * pending row so an at-least-once queue replay cannot leave an orphan.
77
66
  *
78
- * Locks pending-then-running to match `claimPendingJobExecution`, `releaseJobExecution`, and
79
- * `reconcileTerminalJobExecution`. The stale candidate scan intentionally does not lock running
80
- * rows first; the running-row DELETE re-checks the stale predicate after the pending-row sweep.
67
+ * Locks pending-then-running to match `claimPendingJobExecution` and
68
+ * `reconcileTerminalJobExecution`. The stale candidate scan intentionally does not lock
69
+ * running rows first; the running-row DELETE re-checks the stale predicate after the
70
+ * pending-row sweep.
81
71
  */
82
72
  export declare function expireStuckJobExecutions(params: {
83
73
  thresholdSeconds: number;
@@ -128,13 +118,10 @@ export declare function recordHeartbeat(params: {
128
118
  }>;
129
119
  /**
130
120
  * Reconciles a terminal job execution with runner state in one transaction: removes its pending
131
- * queue row and requests cancellation on its running lease, if either exists. The operation is
132
- * idempotent and preserves the first cancellation-request timestamp.
121
+ * queue row, requests cancellation on its running lease, and converges any linked reservation.
122
+ * The operation is idempotent and preserves the first cancellation-request timestamp.
133
123
  */
134
124
  export declare function reconcileTerminalJobExecution(params: {
135
125
  jobExecutionId: string;
136
126
  }): Promise<void>;
137
- export declare function cancelRunnerJobs(params: {
138
- jobIds: string[];
139
- }): Promise<void>;
140
127
  //# sourceMappingURL=job-executions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"job-executions.d.ts","sourceRoot":"","sources":["../../src/db/job-executions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"job-executions.d.ts","sourceRoot":"","sources":["../../src/db/job-executions.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { RUNNER_JOB_CLAIMED, RUNNER_JOB_LEASE_EXPIRED, RUNNER_JOB_QUEUED } from '@shipfox/api-runners-dto';
1
+ import { RUNNER_JOB_CLAIMED, RUNNER_JOB_LEASE_EXPIRED } from '@shipfox/api-runners-dto';
2
2
  import { logger } from '@shipfox/node-opentelemetry';
3
3
  import { writeOutboxEvent, writeOutboxEvents } from '@shipfox/node-outbox';
4
4
  import { canonicalizeLabels } from '@shipfox/runner-labels';
@@ -6,6 +6,7 @@ import { and, arrayContained, asc, count, desc, eq, gt, inArray, isNotNull, isNu
6
6
  import { EmptyRequiredLabelsError, RunnerSessionExhaustedError, RunningJobExecutionNotFoundError } from '#core/errors.js';
7
7
  import { jobExecutionEnqueuedCount, jobExecutionLeaseExpiredCount, recordJobExecutionQueueTime, recordProviderRunnerActivationToFirstClaim } from '#metrics/instance.js';
8
8
  import { db } from './db.js';
9
+ import { releaseTerminalRunnerInstanceReservationsByIds } from './reservations.js';
9
10
  import { runnersOutbox } from './schema/outbox.js';
10
11
  import { pendingJobExecutions } from './schema/pending-job-executions.js';
11
12
  import { providerRunners } from './schema/runner-instances.js';
@@ -15,6 +16,34 @@ const runnerJobExecutionLockPrefix = 'runners_job_execution:';
15
16
  async function lockJobExecution(tx, jobExecutionId) {
16
17
  await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${`${runnerJobExecutionLockPrefix}${jobExecutionId}`}))`);
17
18
  }
19
+ async function releaseReservationsForTerminalRunningRows(tx, rows) {
20
+ // Converge after the lease fact changes. The reservation helper locks each provider runner
21
+ // and releases only when the runner is terminal and no uncancelled lease remains.
22
+ const providerRunnerIdsByProvisionerId = new Map();
23
+ for (const row of rows){
24
+ if (row.provisionerId === null || row.providerRunnerId === null) continue;
25
+ const providerRunnerIds = providerRunnerIdsByProvisionerId.get(row.provisionerId) ?? new Set();
26
+ providerRunnerIds.add(row.providerRunnerId);
27
+ providerRunnerIdsByProvisionerId.set(row.provisionerId, providerRunnerIds);
28
+ }
29
+ for (const provisionerId of [
30
+ ...providerRunnerIdsByProvisionerId.keys()
31
+ ].sort()){
32
+ const providerRunnerIds = providerRunnerIdsByProvisionerId.get(provisionerId);
33
+ if (!providerRunnerIds) continue;
34
+ await releaseTerminalRunnerInstanceReservationsByIds(tx, {
35
+ workspaceId: null,
36
+ provisionerId,
37
+ providerRunnerIds: [
38
+ ...providerRunnerIds
39
+ ].sort(),
40
+ requireUnlinkedSession: false,
41
+ // Lock and re-check the runner row locally so lease finalization remains retryable
42
+ // without a workflow/runner scope lock.
43
+ requireTerminalState: false
44
+ });
45
+ }
46
+ }
18
47
  export async function getWorkspaceJobCounts(params) {
19
48
  const [pendingRows, runningRows] = await Promise.all([
20
49
  db().select({
@@ -40,11 +69,9 @@ export async function getWorkspaceJobCounts(params) {
40
69
  running: runningByWorkspace.get(workspaceId) ?? 0
41
70
  }));
42
71
  }
43
- // Idempotent while the job execution is still pending: a duplicate jobExecutionId already in
44
- // `runners_pending_jobs` is a no-op. Temporal retries the enqueue activity at-least-once, so a
45
- // unique-violation throw on a retry-after-lost-result would permanently fail a healthy execution.
46
- // The per-execution advisory lock serializes retries with lease expiry/reconciliation, and the
47
- // durable lease-expired event prevents a retry from re-queueing an execution already reaped.
72
+ // The workflows outbox delivers queue facts at least once. The per-execution advisory lock and
73
+ // pending/running checks make replay a no-op across the claim transition. A durable lease-expired
74
+ // event also prevents a delayed replay from resurrecting an execution already reaped locally.
48
75
  export async function enqueueJobExecution(params) {
49
76
  const requiredLabels = [
50
77
  ...canonicalizeLabels(params.requiredLabels)
@@ -52,6 +79,10 @@ export async function enqueueJobExecution(params) {
52
79
  if (requiredLabels.length === 0) throw new EmptyRequiredLabelsError();
53
80
  const enqueued = await db().transaction(async (tx)=>{
54
81
  await lockJobExecution(tx, params.jobExecutionId);
82
+ const [running] = await tx.select({
83
+ jobExecutionId: runningJobExecutions.jobExecutionId
84
+ }).from(runningJobExecutions).where(eq(runningJobExecutions.jobExecutionId, params.jobExecutionId)).limit(1);
85
+ if (running) return false;
55
86
  const [leaseExpired] = await tx.select({
56
87
  id: runnersOutbox.id
57
88
  }).from(runnersOutbox).where(and(eq(runnersOutbox.eventType, RUNNER_JOB_LEASE_EXPIRED), sql`${runnersOutbox.payload}->>'jobExecutionId' = ${params.jobExecutionId}`)).limit(1);
@@ -63,27 +94,14 @@ export async function enqueueJobExecution(params) {
63
94
  jobId: params.jobId,
64
95
  jobExecutionId: params.jobExecutionId,
65
96
  projectId: params.projectId,
66
- requiredLabels
97
+ requiredLabels,
98
+ createdAt: params.queuedAt
67
99
  }).onConflictDoNothing({
68
100
  target: pendingJobExecutions.jobExecutionId
69
101
  }).returning({
70
102
  createdAt: pendingJobExecutions.createdAt
71
103
  });
72
- // A retry that hits the conflict inserts nothing: the first enqueue already
73
- // emitted the queued event (durably, in the outbox), so re-emitting would
74
- // only add a redundant row the subscriber coalesces away. Skip it.
75
- if (!inserted) return false;
76
- await writeOutboxEvent(tx, runnersOutbox, {
77
- type: RUNNER_JOB_QUEUED,
78
- payload: {
79
- workflowRunId: params.workflowRunId,
80
- workflowRunAttemptId: params.workflowRunAttemptId,
81
- jobId: params.jobId,
82
- jobExecutionId: params.jobExecutionId,
83
- queuedAt: inserted.createdAt.toISOString()
84
- }
85
- });
86
- return true;
104
+ return inserted !== undefined;
87
105
  });
88
106
  if (enqueued) jobExecutionEnqueuedCount.add(1);
89
107
  }
@@ -226,36 +244,18 @@ async function touchRunnerSessionLiveness(params) {
226
244
  updatedAt: sql`now()`
227
245
  }).where(and(eq(runnerSessions.id, params.runnerSessionId), eq(runnerSessions.workspaceId, params.workspaceId), sql`${runnerSessions.updatedAt} < now() - (${params.throttleSeconds} || ' seconds')::interval`));
228
246
  }
229
- /**
230
- * Releases a job execution's lease when the orchestration workflow finalizes it: deletes the
231
- * running-job-execution row AND any lingering pending row for the same execution, in one tx.
232
- * Idempotent (0-row no-op), no token scope (the workflow is authoritative), and
233
- * emits no event: the workflow already owns the outcome. Sweeping the pending row
234
- * too closes the at-least-once window where an enqueue retry left an orphan that a
235
- * later claim would otherwise pick up for an already-finished job execution.
236
- */ export async function releaseJobExecution(params) {
237
- await db().transaction(async (tx)=>{
238
- await lockJobExecution(tx, params.jobExecutionId);
239
- // Delete pending before running to match `claimPendingJobExecution`'s lock-acquisition
240
- // order (it locks the pending row first, then the running row). A concurrent
241
- // claim picking up an orphan pending row for this same job execution would otherwise
242
- // deadlock against the reverse order here.
243
- await tx.delete(pendingJobExecutions).where(eq(pendingJobExecutions.jobExecutionId, params.jobExecutionId));
244
- await tx.delete(runningJobExecutions).where(eq(runningJobExecutions.jobExecutionId, params.jobExecutionId));
245
- });
246
- }
247
247
  /**
248
248
  * Reaps stale leases (bounded by `limit`), emitting one
249
249
  * `runners.job.lease_expired` event per reaped job execution.
250
250
  *
251
251
  * The cutoff is re-checked in the DELETE, not just the locking subquery, so a
252
252
  * heartbeat landing mid-call spares the live row. Each reaped execution also sweeps its
253
- * pending row: a failed best-effort `releaseJobExecution` would otherwise leave an orphan
254
- * that a later claim re-runs as an already-finished job execution.
253
+ * pending row so an at-least-once queue replay cannot leave an orphan.
255
254
  *
256
- * Locks pending-then-running to match `claimPendingJobExecution`, `releaseJobExecution`, and
257
- * `reconcileTerminalJobExecution`. The stale candidate scan intentionally does not lock running
258
- * rows first; the running-row DELETE re-checks the stale predicate after the pending-row sweep.
255
+ * Locks pending-then-running to match `claimPendingJobExecution` and
256
+ * `reconcileTerminalJobExecution`. The stale candidate scan intentionally does not lock
257
+ * running rows first; the running-row DELETE re-checks the stale predicate after the
258
+ * pending-row sweep.
259
259
  */ export async function expireStuckJobExecutions(params) {
260
260
  const reaped = await db().transaction(async (tx)=>{
261
261
  const heartbeatCutoff = sql`now() - (${params.thresholdSeconds} || ' seconds')::interval`;
@@ -281,9 +281,12 @@ async function touchRunnerSessionLiveness(params) {
281
281
  workflowRunId: runningJobExecutions.workflowRunId,
282
282
  workflowRunAttemptId: runningJobExecutions.workflowRunAttemptId,
283
283
  jobId: runningJobExecutions.jobId,
284
- jobExecutionId: runningJobExecutions.jobExecutionId
284
+ jobExecutionId: runningJobExecutions.jobExecutionId,
285
+ provisionerId: runningJobExecutions.provisionerId,
286
+ providerRunnerId: runningJobExecutions.providerRunnerId
285
287
  });
286
288
  if (deleted.length === 0) return [];
289
+ await releaseReservationsForTerminalRunningRows(tx, deleted);
287
290
  await writeOutboxEvents(tx, runnersOutbox, deleted.map((row)=>({
288
291
  type: RUNNER_JOB_LEASE_EXPIRED,
289
292
  payload: {
@@ -293,7 +296,12 @@ async function touchRunnerSessionLiveness(params) {
293
296
  jobExecutionId: row.jobExecutionId
294
297
  }
295
298
  })));
296
- return deleted;
299
+ return deleted.map(({ workflowRunId, workflowRunAttemptId, jobId, jobExecutionId })=>({
300
+ workflowRunId,
301
+ workflowRunAttemptId,
302
+ jobId,
303
+ jobExecutionId
304
+ }));
297
305
  });
298
306
  if (reaped.length > 0) jobExecutionLeaseExpiredCount.add(reaped.length);
299
307
  return reaped;
@@ -431,8 +439,8 @@ export async function recordHeartbeat(params) {
431
439
  }
432
440
  /**
433
441
  * Reconciles a terminal job execution with runner state in one transaction: removes its pending
434
- * queue row and requests cancellation on its running lease, if either exists. The operation is
435
- * idempotent and preserves the first cancellation-request timestamp.
442
+ * queue row, requests cancellation on its running lease, and converges any linked reservation.
443
+ * The operation is idempotent and preserves the first cancellation-request timestamp.
436
444
  */ export async function reconcileTerminalJobExecution(params) {
437
445
  await db().transaction(async (tx)=>{
438
446
  await lockJobExecution(tx, params.jobExecutionId);
@@ -440,18 +448,13 @@ export async function recordHeartbeat(params) {
440
448
  // pending rows with SKIP LOCKED before inserting the running lease, so this ordering makes
441
449
  // a concurrent terminal reconciliation either win before claim or cancel the new lease.
442
450
  await tx.delete(pendingJobExecutions).where(eq(pendingJobExecutions.jobExecutionId, params.jobExecutionId));
443
- await tx.update(runningJobExecutions).set({
451
+ const cancelledRunningRows = await tx.update(runningJobExecutions).set({
444
452
  cancellationRequestedAt: sql`COALESCE(${runningJobExecutions.cancellationRequestedAt}, now())`
445
- }).where(eq(runningJobExecutions.jobExecutionId, params.jobExecutionId));
446
- });
447
- }
448
- export async function cancelRunnerJobs(params) {
449
- if (params.jobIds.length === 0) return;
450
- await db().transaction(async (tx)=>{
451
- await tx.delete(pendingJobExecutions).where(inArray(pendingJobExecutions.jobId, params.jobIds));
452
- await tx.update(runningJobExecutions).set({
453
- cancellationRequestedAt: sql`COALESCE(${runningJobExecutions.cancellationRequestedAt}, now())`
454
- }).where(inArray(runningJobExecutions.jobId, params.jobIds));
453
+ }).where(eq(runningJobExecutions.jobExecutionId, params.jobExecutionId)).returning({
454
+ provisionerId: runningJobExecutions.provisionerId,
455
+ providerRunnerId: runningJobExecutions.providerRunnerId
456
+ });
457
+ await releaseReservationsForTerminalRunningRows(tx, cancelledRunningRows);
455
458
  });
456
459
  }
457
460