deepline 0.3.284 → 0.3.285

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 (34) hide show
  1. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  2. package/dist/bundling-sources/shared_libs/play-runtime/activity-observation.ts +26 -2
  3. package/dist/bundling-sources/shared_libs/play-runtime/batch-runtime.ts +7 -0
  4. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +817 -2236
  5. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +19 -6
  6. package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +127 -789
  7. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +180 -14
  8. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +1 -0
  9. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +3 -22
  10. package/dist/bundling-sources/shared_libs/play-runtime/runtime-incident-drills.ts +26 -0
  11. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +50 -8
  12. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +122 -18
  13. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +218 -41
  14. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-capacity.ts +20 -2
  15. package/dist/bundling-sources/shared_libs/play-runtime/test-runtime-seams.ts +13 -2
  16. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/receipt-cohort.ts +181 -326
  17. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/runtime-step-receipts-adapter.ts +38 -151
  18. package/dist/bundling-sources/shared_libs/play-runtime/worker-api-types.ts +8 -0
  19. package/dist/cli/index.js +28 -3
  20. package/dist/cli/index.mjs +28 -3
  21. package/dist/{client-B0AjIkgb.d.mts → client-Cl-zj2oS.d.mts} +4 -0
  22. package/dist/{client-BCXQ_Ftc.d.ts → client-T2jVnZrq.d.ts} +4 -0
  23. package/dist/index.d.mts +2 -2
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.js +25 -1
  26. package/dist/index.mjs +25 -1
  27. package/dist/install-integrity.json +2 -2
  28. package/dist/plays/run-request.d.mts +1 -1
  29. package/dist/plays/run-request.d.ts +1 -1
  30. package/dist/release.d.mts +1 -1
  31. package/dist/release.d.ts +1 -1
  32. package/dist/release.js +1 -1
  33. package/dist/release.mjs +1 -1
  34. package/package.json +1 -1
@@ -44,6 +44,13 @@ export type PlayRunLedgerStepStatus =
44
44
 
45
45
  export type PlayRunLedgerEventSource = RunEventSource;
46
46
 
47
+ /** Authority of the row sample, independent of gateway process lifetime. */
48
+ export type PlayMapRowActivitySource =
49
+ | 'resolver'
50
+ | 'map_start'
51
+ | 'map_event'
52
+ | 'log';
53
+
47
54
  /**
48
55
  * Live progress for a single step/node.
49
56
  *
@@ -76,6 +83,13 @@ export type PlayRunLedgerStepProgress = {
76
83
  startedRows?: number;
77
84
  activeRows?: number;
78
85
  waitingRows?: number;
86
+ /** Internal provenance for merging stateless progress batches. */
87
+ rowActivitySource?: PlayMapRowActivitySource;
88
+ /** Existing logical map identity, absent from legacy progress writers. */
89
+ mapInvocationId?: string;
90
+ /** Existing runner event times for pool boundaries and row observations. */
91
+ rowActivityStartedAt?: number;
92
+ rowActivityObservedAt?: number;
79
93
  completedRows?: number;
80
94
  supersededRows?: number;
81
95
  message?: string;
@@ -1073,8 +1087,112 @@ function mergeMonotonicStepProgress(input: {
1073
1087
  current?: PlayRunLedgerStepProgress | null;
1074
1088
  next: PlayRunLedgerStepProgress;
1075
1089
  }): PlayRunLedgerStepProgress {
1076
- const current = input.current ?? {};
1077
- const next = input.next;
1090
+ const previous = input.current ?? {};
1091
+ const hasMapInvocationIdentities =
1092
+ previous.mapInvocationId !== undefined &&
1093
+ input.next.mapInvocationId !== undefined;
1094
+ const differentMapInvocation =
1095
+ hasMapInvocationIdentities &&
1096
+ previous.mapInvocationId !== input.next.mapInvocationId;
1097
+ const sameMapInvocation =
1098
+ previous.mapInvocationId !== undefined &&
1099
+ previous.mapInvocationId === input.next.mapInvocationId;
1100
+ const previousPoolStartedAt =
1101
+ previous.rowActivityStartedAt ?? previous.rowActivityObservedAt;
1102
+ const observesEarlierSourceTime =
1103
+ typeof previous.rowActivityObservedAt === 'number' &&
1104
+ typeof input.next.rowActivityObservedAt === 'number' &&
1105
+ input.next.rowActivityObservedAt < previous.rowActivityObservedAt;
1106
+ const replaysOlderMapBoundary =
1107
+ hasMapInvocationIdentities &&
1108
+ input.next.rowActivitySource === 'map_start' &&
1109
+ (observesEarlierSourceTime ||
1110
+ (sameMapInvocation &&
1111
+ typeof previousPoolStartedAt === 'number' &&
1112
+ typeof input.next.rowActivityStartedAt === 'number' &&
1113
+ input.next.rowActivityStartedAt <= previousPoolStartedAt));
1114
+ const replaysOlderResolverSample =
1115
+ hasMapInvocationIdentities &&
1116
+ input.next.rowActivitySource === 'resolver' &&
1117
+ observesEarlierSourceTime;
1118
+ const replaysOlderCountObservation =
1119
+ hasMapInvocationIdentities &&
1120
+ input.next.rowActivitySource === 'map_event' &&
1121
+ input.next.completedAt === undefined &&
1122
+ observesEarlierSourceTime;
1123
+ const replaysOlderMapObservation =
1124
+ replaysOlderMapBoundary ||
1125
+ replaysOlderResolverSample ||
1126
+ replaysOlderCountObservation;
1127
+ const changesMapInvocation =
1128
+ differentMapInvocation &&
1129
+ !replaysOlderMapObservation &&
1130
+ input.next.rowActivitySource !== undefined &&
1131
+ input.next.rowActivitySource !== 'log';
1132
+ const startsExecutionPool =
1133
+ sameMapInvocation &&
1134
+ !replaysOlderMapObservation &&
1135
+ (input.next.rowActivitySource === 'map_start' ||
1136
+ ((input.next.rowActivitySource === 'resolver' ||
1137
+ input.next.rowActivitySource === 'map_event') &&
1138
+ typeof previousPoolStartedAt === 'number' &&
1139
+ typeof input.next.rowActivityStartedAt === 'number' &&
1140
+ input.next.rowActivityStartedAt > previousPoolStartedAt));
1141
+ const replacesInferredActivity =
1142
+ sameMapInvocation &&
1143
+ !replaysOlderMapObservation &&
1144
+ input.next.rowActivitySource === 'resolver' &&
1145
+ previous.rowActivitySource !== undefined &&
1146
+ previous.rowActivitySource !== 'resolver';
1147
+ // Admission belongs to the current execution pool. The logical map identity
1148
+ // survives replay/pages, so only a witnessed pool boundary or newer typed
1149
+ // invocation resets activity. Accepted row outcomes remain monotonic.
1150
+ const current =
1151
+ changesMapInvocation || startsExecutionPool || replacesInferredActivity
1152
+ ? {
1153
+ ...previous,
1154
+ startedRows: undefined,
1155
+ activeRows: undefined,
1156
+ waitingRows: undefined,
1157
+ rowActivitySource: undefined,
1158
+ ...(changesMapInvocation
1159
+ ? {
1160
+ rowActivityStartedAt: undefined,
1161
+ rowActivityObservedAt: undefined,
1162
+ }
1163
+ : {}),
1164
+ }
1165
+ : previous;
1166
+ // A gateway reporter is recreated for every batch. Only the durable fold
1167
+ // can keep a row-resolver sample authoritative when a later batch contains
1168
+ // tool-call or page-setup diagnostics without a repeated activity sample.
1169
+ // Lifecycle facts explicitly reset this authority; legacy absence retains
1170
+ // the historical merge behavior instead of guessing from numeric fields.
1171
+ const next =
1172
+ (current.rowActivitySource === 'resolver' &&
1173
+ input.next.rowActivitySource === 'log') ||
1174
+ (differentMapInvocation &&
1175
+ input.next.rowActivitySource === 'log' &&
1176
+ (current.rowActivitySource === 'map_start' ||
1177
+ current.rowActivitySource === 'map_event')) ||
1178
+ replaysOlderMapObservation
1179
+ ? {
1180
+ ...input.next,
1181
+ completed: current.completed,
1182
+ total: current.total,
1183
+ failed: current.failed,
1184
+ startedRows: current.startedRows,
1185
+ activeRows: current.activeRows,
1186
+ waitingRows: current.waitingRows,
1187
+ completedRows: current.completedRows,
1188
+ supersededRows: current.supersededRows,
1189
+ message: current.message,
1190
+ rowActivitySource: current.rowActivitySource,
1191
+ mapInvocationId: current.mapInvocationId,
1192
+ rowActivityStartedAt: current.rowActivityStartedAt,
1193
+ rowActivityObservedAt: current.rowActivityObservedAt,
1194
+ }
1195
+ : input.next;
1078
1196
  const supersededRows = maxProgressNumber(
1079
1197
  current.supersededRows,
1080
1198
  next.supersededRows === undefined
@@ -1084,6 +1202,7 @@ function mergeMonotonicStepProgress(input: {
1084
1202
  return {
1085
1203
  ...current,
1086
1204
  ...next,
1205
+ rowActivitySource: next.rowActivitySource,
1087
1206
  ...(maxProgressNumber(current.completed, next.completed) !== undefined
1088
1207
  ? { completed: maxProgressNumber(current.completed, next.completed) }
1089
1208
  : {}),
@@ -1118,13 +1237,14 @@ function mergeMonotonicStepProgress(input: {
1118
1237
  }
1119
1238
 
1120
1239
  /**
1121
- * Zero the in-flight row accounting on a settled step. `activeRows` (calls in
1122
- * flight) and `waitingRows` (rows queued behind rate/concurrency limits) are
1123
- * live-only counters: once a step is terminal nothing is in flight or waiting,
1124
- * so leaving the last observed values renders a contradiction like
1240
+ * Clear stale in-flight row accounting on a settled step. Once a step is
1241
+ * terminal nothing is in flight or waiting, so leaving the last observed
1242
+ * values renders a contradiction like
1125
1243
  * "status: completed · 1 in flight · waiting 29". This preserves the settled
1126
- * counts (including supersededRows) and only clears the two live accumulators.
1127
- * Idempotent: dropping already-absent fields is a no-op.
1244
+ * counts (including supersededRows). A terminal map event or reconciliation
1245
+ * explicitly witnesses zero active and waiting rows; retain those zeros for
1246
+ * readers that follow the same counters through completion. Older writers
1247
+ * and nonterminal activity samples retain their existing omission behavior.
1128
1248
  */
1129
1249
  function clearInFlightProgressOnTerminal(
1130
1250
  progress: PlayRunLedgerStepProgress | null | undefined,
@@ -1134,16 +1254,23 @@ function clearInFlightProgressOnTerminal(
1134
1254
  typeof progress.message === 'string'
1135
1255
  ? settledProgressMessage(progress.message, progress)
1136
1256
  : progress.message;
1257
+ const witnessedTerminalZeroActivity =
1258
+ progress.rowActivitySource === 'map_event' &&
1259
+ progress.activeRows === 0 &&
1260
+ progress.waitingRows === 0;
1137
1261
  if (
1138
- progress.activeRows === undefined &&
1139
- progress.waitingRows === undefined &&
1262
+ (witnessedTerminalZeroActivity ||
1263
+ (progress.activeRows === undefined &&
1264
+ progress.waitingRows === undefined)) &&
1140
1265
  message === progress.message
1141
1266
  ) {
1142
1267
  return progress;
1143
1268
  }
1144
1269
  const cleared = { ...progress };
1145
- delete cleared.activeRows;
1146
- delete cleared.waitingRows;
1270
+ if (!witnessedTerminalZeroActivity) {
1271
+ delete cleared.activeRows;
1272
+ delete cleared.waitingRows;
1273
+ }
1147
1274
  if (message === undefined) delete cleared.message;
1148
1275
  else cleared.message = message;
1149
1276
  return cleared;
@@ -1222,6 +1349,29 @@ function normalizeStepProgress(
1222
1349
  ...(optionalFiniteNumber(value.waitingRows) !== undefined
1223
1350
  ? { waitingRows: optionalFiniteNumber(value.waitingRows) }
1224
1351
  : {}),
1352
+ ...(value.rowActivitySource === 'resolver' ||
1353
+ value.rowActivitySource === 'map_start' ||
1354
+ value.rowActivitySource === 'map_event' ||
1355
+ value.rowActivitySource === 'log'
1356
+ ? { rowActivitySource: value.rowActivitySource }
1357
+ : {}),
1358
+ ...(optionalString(value.mapInvocationId)
1359
+ ? { mapInvocationId: optionalString(value.mapInvocationId) }
1360
+ : {}),
1361
+ ...(optionalFiniteNumber(value.rowActivityStartedAt) !== undefined
1362
+ ? {
1363
+ rowActivityStartedAt: optionalFiniteNumber(
1364
+ value.rowActivityStartedAt,
1365
+ ),
1366
+ }
1367
+ : {}),
1368
+ ...(optionalFiniteNumber(value.rowActivityObservedAt) !== undefined
1369
+ ? {
1370
+ rowActivityObservedAt: optionalFiniteNumber(
1371
+ value.rowActivityObservedAt,
1372
+ ),
1373
+ }
1374
+ : {}),
1225
1375
  ...(optionalFiniteNumber(value.completedRows) !== undefined
1226
1376
  ? { completedRows: optionalFiniteNumber(value.completedRows) }
1227
1377
  : {}),
@@ -1936,8 +2086,8 @@ export function reducePlayRunLedgerEvent(
1936
2086
  (status === 'completed' || status === 'failed'
1937
2087
  ? (completedAt ?? event.progress.updatedAt ?? occurredAt)
1938
2088
  : null);
1939
- // Once the step is settled, drop the live in-flight/waiting accumulators
1940
- // so a terminal node never reports active or waiting rows.
2089
+ // Once the step is settled, clear stale live counters while retaining
2090
+ // explicit terminal zero activity from the owning map event.
1941
2091
  const progress = isTerminalStepStatus(status)
1942
2092
  ? clearInFlightProgressOnTerminal(mergedProgress)
1943
2093
  : mergedProgress;
@@ -2139,6 +2289,10 @@ function progressSignature(
2139
2289
  startedRows: progress?.startedRows ?? null,
2140
2290
  activeRows: progress?.activeRows ?? null,
2141
2291
  waitingRows: progress?.waitingRows ?? null,
2292
+ rowActivitySource: progress?.rowActivitySource ?? null,
2293
+ mapInvocationId: progress?.mapInvocationId ?? null,
2294
+ rowActivityStartedAt: progress?.rowActivityStartedAt ?? null,
2295
+ rowActivityObservedAt: progress?.rowActivityObservedAt ?? null,
2142
2296
  completedRows: progress?.completedRows ?? null,
2143
2297
  supersededRows: progress?.supersededRows ?? null,
2144
2298
  message: progress?.message ?? null,
@@ -2438,6 +2592,18 @@ export function buildPlayRunLedgerEventsFromStatusPatch(input: {
2438
2592
  ...(progress.waitingRows !== undefined
2439
2593
  ? { waitingRows: progress.waitingRows }
2440
2594
  : {}),
2595
+ ...(progress.rowActivitySource !== undefined
2596
+ ? { rowActivitySource: progress.rowActivitySource }
2597
+ : {}),
2598
+ ...(progress.mapInvocationId !== undefined
2599
+ ? { mapInvocationId: progress.mapInvocationId }
2600
+ : {}),
2601
+ ...(progress.rowActivityStartedAt !== undefined
2602
+ ? { rowActivityStartedAt: progress.rowActivityStartedAt }
2603
+ : {}),
2604
+ ...(progress.rowActivityObservedAt !== undefined
2605
+ ? { rowActivityObservedAt: progress.rowActivityObservedAt }
2606
+ : {}),
2441
2607
  ...(progress.completedRows !== undefined
2442
2608
  ? { completedRows: progress.completedRows }
2443
2609
  : {}),
@@ -2577,6 +2577,7 @@ async function run(
2577
2577
  columnTypes: mapContext.columnTypes,
2578
2578
  }),
2579
2579
  rows: items,
2580
+ pendingRerunAdmission: true,
2580
2581
  inputOffset: mapContext.inputOffset,
2581
2582
  mode: mapContext.mode,
2582
2583
  runId,
@@ -1839,29 +1839,10 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
1839
1839
  }),
1840
1840
  );
1841
1841
  } catch (error) {
1842
- // Resource persistence belongs to the scheduler control plane. Preserve
1843
- // its typed capacity/fence errors so Absurd can defer or fence the
1844
- // attempt; converting them into a runner failure would terminally fail a
1845
- // play that never began executing customer code. The sandbox exists
1846
- // before that durable callback can succeed, so synchronously delete it
1847
- // before returning the control-plane error. The old fire-and-forget
1848
- // cleanup path was skipped by this rethrow and leaked the acquisition.
1842
+ // Publication can fail after it commits, or after a successor adopts
1843
+ // this sandbox. Preserve the scheduler error and leave deletion to its
1844
+ // fenced cleanup owner; this backend no longer has deletion authority.
1849
1845
  if (runtimeResourceReportErrors.has(error)) {
1850
- const sandbox = sandboxCleanup.currentSandbox();
1851
- if (sandbox) {
1852
- try {
1853
- await preparedExecution.daytona.delete(sandbox, 30);
1854
- console.info(
1855
- '[play-runner.daytona.resource_report_failure_cleanup_done]',
1856
- { sandboxId: sandbox.id },
1857
- );
1858
- } catch (cleanupError) {
1859
- throw new AggregateError(
1860
- [error, cleanupError],
1861
- `Failed to persist or delete acquired Daytona sandbox ${sandbox.id}.`,
1862
- );
1863
- }
1864
- }
1865
1846
  throw error;
1866
1847
  }
1867
1848
  if (isRuntimeSandboxCapacityLimitError(error)) {
@@ -13,6 +13,7 @@ export const RUNTIME_TEST_FAULT_IDS = [
13
13
  'receipt_fail_write_fail',
14
14
  'worker_receipt_complete_write_fail',
15
15
  'invocation_response_delivery_abort',
16
+ 'execution_upstream_response_loss',
16
17
  'receipt_gateway_hold_ms',
17
18
  'receipt_claim_query_hold_once_ms',
18
19
  'receipt_claim_response_timeout',
@@ -226,6 +227,31 @@ export const RUNTIME_INCIDENT_DRILL_CATALOG: readonly RuntimeIncidentDrill[] = [
226
227
  target: 'invocation-delivery-replay-e2e',
227
228
  },
228
229
  },
230
+ {
231
+ id: 'execution_upstream_response_loss',
232
+ description:
233
+ 'Lose the first response of each keyed execution after the upstream app durably stores it.',
234
+ topologies: ['preview_fly'],
235
+ control: {
236
+ kind: 'runtime_test_fault',
237
+ defaultValue: 1,
238
+ valueMeaning: 'occurrence',
239
+ },
240
+ expected: {
241
+ outcome:
242
+ 'The same invocation recovers the stored response without repeating provider work or customer charges, and every row retains its usable result.',
243
+ safetyFacts: [
244
+ 'same_logical_run',
245
+ 'bounded_provider_execution',
246
+ 'all_rows_settle_once',
247
+ 'no_false_success',
248
+ ],
249
+ },
250
+ verification: {
251
+ kind: 'preview_black_box',
252
+ target: '101-agent-heavy-gateway-load-e2e',
253
+ },
254
+ },
229
255
  {
230
256
  id: 'receipt_gateway_hold_ms',
231
257
  description: 'Hold one checked-out receipt-gateway scheduler client.',
@@ -175,6 +175,7 @@ import {
175
175
  readPostgresSchedulerRunnerTerminal,
176
176
  readPostgresSchedulerRunnerLiveness,
177
177
  readPostgresSchedulerRuntimeResourcesForRun,
178
+ isPostgresSchedulerRunnerProgressAttemptCurrent,
178
179
  requestComputeBillingDispatchToken,
179
180
  blockPostgresSchedulerCronSchedule,
180
181
  PostgresSchedulerCronScheduleConfigurationError,
@@ -2640,7 +2641,26 @@ async function executeAbsurdAttemptAdapter(
2640
2641
  runtimeApi,
2641
2642
  });
2642
2643
  } catch (error) {
2643
- if (error instanceof SuspendTask || error instanceof DeferTask) throw error;
2644
+ if (error instanceof SuspendTask || error instanceof DeferTask) {
2645
+ throw error;
2646
+ }
2647
+ if (error instanceof RuntimeResourceFenceLostError) {
2648
+ // A rejected resource promotion can retain the current attempt fence.
2649
+ // Its final failure must settle visibly; a successor's fence denies
2650
+ // this terminal write before any resource settlement or cleanup.
2651
+ await salvageAbsurdFinalAttemptTerminal({
2652
+ ctx,
2653
+ deps,
2654
+ options,
2655
+ runId,
2656
+ launch: executionLaunch,
2657
+ generation,
2658
+ leaseToken,
2659
+ error,
2660
+ detachedRecovery: isDetachedRecovery,
2661
+ });
2662
+ throw error;
2663
+ }
2644
2664
  let detached: Awaited<
2645
2665
  ReturnType<typeof resumePostgresSchedulerDetachedRunnerAttempt>
2646
2666
  >;
@@ -3397,6 +3417,20 @@ async function executeAssignedAbsurdAttempt(input: {
3397
3417
  )
3398
3418
  )
3399
3419
  return;
3420
+ const stillOwnsAttempt = await deps.runWithClient((client) =>
3421
+ isPostgresSchedulerRunnerProgressAttemptCurrent(
3422
+ client,
3423
+ { runId, attempt: generation },
3424
+ options,
3425
+ ),
3426
+ );
3427
+ if (!stillOwnsAttempt) {
3428
+ console.info('[absurd-runtime.worker] stale_attempt_cleanup_skipped', {
3429
+ runId,
3430
+ attempt: generation,
3431
+ });
3432
+ return;
3433
+ }
3400
3434
  const cleanupTerminalReason =
3401
3435
  error instanceof CancelledTask ? 'lease_expired' : 'runner_failed';
3402
3436
  await deps
@@ -3407,6 +3441,7 @@ async function executeAssignedAbsurdAttempt(input: {
3407
3441
  runId,
3408
3442
  billingEndedAt: Date.now(),
3409
3443
  terminalReason: cleanupTerminalReason,
3444
+ throughAttempt: generation,
3410
3445
  },
3411
3446
  options,
3412
3447
  ),
@@ -3422,10 +3457,8 @@ async function executeAssignedAbsurdAttempt(input: {
3422
3457
  : String(settlementError),
3423
3458
  });
3424
3459
  });
3425
- // Sheet/receipt lease release is attempt-scoped (this generation's
3426
- // fences), but sandbox GC must be GENERATION-AGNOSTIC: a reclaimed prior
3427
- // attempt's sandbox is recorded under the OLD generation and would leak
3428
- // if the cleanup read only this attempt's resources.
3460
+ // Include resources from retired attempts through this generation, while
3461
+ // excluding a successor if one is assigned during cleanup.
3429
3462
  let attemptResourceReadFailed = false;
3430
3463
  const attemptResources = await deps
3431
3464
  .runWithClient((client) =>
@@ -3451,7 +3484,7 @@ async function executeAssignedAbsurdAttempt(input: {
3451
3484
  .runWithClient((client) =>
3452
3485
  readPostgresSchedulerRuntimeResourcesForRun(
3453
3486
  client,
3454
- { runId, attempt: null },
3487
+ { runId, attempt: null, throughAttempt: generation },
3455
3488
  options,
3456
3489
  ),
3457
3490
  )
@@ -3462,7 +3495,7 @@ async function executeAssignedAbsurdAttempt(input: {
3462
3495
  await deps.runWithClient((client) =>
3463
3496
  enqueuePostgresSchedulerSandboxCleanupForRun(
3464
3497
  client,
3465
- { runId },
3498
+ { runId, throughAttempt: generation },
3466
3499
  options,
3467
3500
  ),
3468
3501
  );
@@ -3490,6 +3523,7 @@ async function executeAssignedAbsurdAttempt(input: {
3490
3523
  readSandboxCrashDiagnostic: deps.readSandboxCrashDiagnostic,
3491
3524
  leaseOwner: `attempt-error:${runId}:${generation}`,
3492
3525
  runId,
3526
+ throughAttempt: generation,
3493
3527
  environment,
3494
3528
  schedulerOptions: options,
3495
3529
  timeoutMs: deps.sandboxGcTimeoutMs,
@@ -3905,7 +3939,11 @@ async function executeAssignedAbsurdAttempt(input: {
3905
3939
  reservationInput,
3906
3940
  options,
3907
3941
  );
3908
- const reserve = () => deps.runWithClient(reserveOperation);
3942
+ const reserve = () =>
3943
+ deps.runWithClient(reserveOperation, {
3944
+ runId,
3945
+ phase: 'sandbox_capacity_admission',
3946
+ });
3909
3947
  const deferForSchedulerFailure = (error: unknown): never => {
3910
3948
  console.warn(
3911
3949
  '[absurd-runtime.worker] sandbox_capacity_admission_deferred',
@@ -6974,6 +7012,7 @@ async function claimPostgresSchedulerSandboxCleanupBatch(input: {
6974
7012
  runWithClient: AbsurdRuntimeWorkerDeps['runWithClient'];
6975
7013
  leaseOwner: string;
6976
7014
  runId?: string;
7015
+ throughAttempt?: number;
6977
7016
  environment: 'preview' | 'production';
6978
7017
  schedulerOptions: Pick<PostgresSchedulerOptions, 'schema'>;
6979
7018
  limit: number;
@@ -6985,6 +7024,7 @@ async function claimPostgresSchedulerSandboxCleanupBatch(input: {
6985
7024
  {
6986
7025
  leaseOwner: input.leaseOwner,
6987
7026
  runId: input.runId,
7027
+ throughAttempt: input.throughAttempt,
6988
7028
  environment: input.environment,
6989
7029
  limit: input.limit,
6990
7030
  providers: input.providers,
@@ -7300,6 +7340,7 @@ export async function drainPostgresSchedulerSandboxCleanupOnce(input: {
7300
7340
  deleteRuntimeSandboxWithOutcome?: AbsurdRuntimeWorkerDeps['deleteRuntimeSandboxWithOutcome'];
7301
7341
  leaseOwner: string;
7302
7342
  runId?: string;
7343
+ throughAttempt?: number;
7303
7344
  environment: 'preview' | 'production';
7304
7345
  schedulerOptions?: Pick<PostgresSchedulerOptions, 'schema'>;
7305
7346
  limit?: number;
@@ -7322,6 +7363,7 @@ export async function drainPostgresSchedulerSandboxCleanupOnce(input: {
7322
7363
  runWithClient: input.runWithClient,
7323
7364
  leaseOwner: input.leaseOwner,
7324
7365
  runId: input.runId,
7366
+ throughAttempt: input.throughAttempt,
7325
7367
  environment: input.environment,
7326
7368
  schedulerOptions: options,
7327
7369
  limit,