deepline 0.1.199 → 0.1.200

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 (72) hide show
  1. package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +5 -1
  2. package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +51 -17
  3. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/README.md +1 -1
  4. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +16 -0
  5. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/receipts.ts +40 -9
  6. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +108 -39
  7. package/dist/bundling-sources/sdk/src/http.ts +5 -2
  8. package/dist/bundling-sources/sdk/src/play.ts +1 -1
  9. package/dist/bundling-sources/sdk/src/plays/harness-stub.ts +7 -0
  10. package/dist/bundling-sources/sdk/src/release.ts +2 -2
  11. package/dist/bundling-sources/sdk/src/worker-play-entry.ts +11 -55
  12. package/dist/bundling-sources/shared_libs/play-runtime/adaptive-tool-dispatcher.ts +355 -0
  13. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +172 -73
  14. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +1794 -377
  15. package/dist/bundling-sources/shared_libs/play-runtime/ctx-contract.ts +3 -0
  16. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +67 -4
  17. package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +161 -39
  18. package/dist/bundling-sources/shared_libs/play-runtime/governor/adaptive-admission.ts +218 -0
  19. package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +1 -1
  20. package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +115 -25
  21. package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +17 -17
  22. package/dist/bundling-sources/shared_libs/play-runtime/lease-policy.ts +22 -0
  23. package/dist/bundling-sources/shared_libs/play-runtime/ledger-safe-payload.ts +2 -1
  24. package/dist/bundling-sources/shared_libs/play-runtime/live-events.ts +0 -1
  25. package/dist/bundling-sources/shared_libs/play-runtime/map-memory-limits.ts +265 -0
  26. package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +135 -0
  27. package/dist/bundling-sources/shared_libs/play-runtime/play-input.ts +17 -0
  28. package/dist/bundling-sources/shared_libs/play-runtime/play-latency-trace.ts +0 -3
  29. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +10 -1
  30. package/dist/bundling-sources/shared_libs/play-runtime/providers.ts +9 -15
  31. package/dist/bundling-sources/shared_libs/play-runtime/receipt-completion-sink.ts +262 -0
  32. package/dist/bundling-sources/shared_libs/play-runtime/receipt-heartbeat-supervisor.ts +64 -0
  33. package/dist/bundling-sources/shared_libs/play-runtime/receipt-sql.ts +76 -8
  34. package/dist/bundling-sources/shared_libs/play-runtime/receipt-state-sink.ts +59 -0
  35. package/dist/bundling-sources/shared_libs/play-runtime/receipt-status.ts +3 -2
  36. package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +269 -0
  37. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +2 -3
  38. package/dist/bundling-sources/shared_libs/play-runtime/run-terminal-source.ts +0 -1
  39. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +433 -0
  40. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +227 -0
  41. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +882 -0
  42. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/local-process.ts +573 -0
  43. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/bundle.ts +264 -0
  44. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/index.ts +33 -0
  45. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runner-events.ts +35 -0
  46. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/types.ts +53 -0
  47. package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +0 -1
  48. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +459 -103
  49. package/dist/bundling-sources/shared_libs/play-runtime/runtime-constants.ts +12 -0
  50. package/dist/bundling-sources/shared_libs/play-runtime/runtime-infra-mode.ts +23 -0
  51. package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-attempt-state-machine.ts +23 -2
  52. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +16 -24
  53. package/dist/bundling-sources/shared_libs/play-runtime/serial-task-queue.ts +26 -0
  54. package/dist/bundling-sources/shared_libs/play-runtime/submit-limits.ts +1 -1
  55. package/dist/bundling-sources/shared_libs/play-runtime/vercel-protection.ts +81 -0
  56. package/dist/bundling-sources/shared_libs/play-runtime/work-receipt-state-machine.ts +54 -9
  57. package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +17 -3
  58. package/dist/bundling-sources/shared_libs/play-runtime/worker-api-types.ts +0 -20
  59. package/dist/bundling-sources/shared_libs/plays/dataset.ts +105 -4
  60. package/dist/bundling-sources/shared_libs/plays/row-identity.ts +4 -11
  61. package/dist/cli/index.js +167 -54
  62. package/dist/cli/index.mjs +167 -54
  63. package/dist/index.d.mts +1 -1
  64. package/dist/index.d.ts +1 -1
  65. package/dist/index.js +8 -8
  66. package/dist/index.mjs +8 -8
  67. package/dist/plays/bundle-play-file.mjs +15 -20
  68. package/package.json +1 -1
  69. package/dist/bundling-sources/shared_libs/play-runtime/hatchet-cold-execution-diagnosis.ts +0 -321
  70. package/dist/bundling-sources/shared_libs/play-runtime/hatchet-cold-execution-target.ts +0 -158
  71. package/dist/bundling-sources/shared_libs/temporal/constants.ts +0 -39
  72. package/dist/bundling-sources/shared_libs/temporal/preview-config.ts +0 -150
@@ -66,10 +66,10 @@ import {
66
66
  import { RECEIPT_STATUS_CODE, receiptStatusFromCode } from './receipt-status';
67
67
  import {
68
68
  workReceiptClaimableStatusCodes,
69
+ workReceiptActiveOwnerPredicateSql,
69
70
  workReceiptClaimConflictPredicateSql,
70
71
  workReceiptHeartbeatPredicateSql,
71
72
  workReceiptReleaseOwnerPredicateSql,
72
- workReceiptRunningOwnerPredicateSql,
73
73
  } from './receipt-sql';
74
74
  import {
75
75
  activeRuntimeSheetAttemptFenceSql,
@@ -105,6 +105,7 @@ import {
105
105
  PLAY_RUNTIME_TEST_FAULT_HEADER,
106
106
  type RuntimeTestFaultName,
107
107
  } from './test-runtime-seams';
108
+ import { vercelProtectionBypassHeader } from './vercel-protection';
108
109
 
109
110
  type RuntimeApiContext = {
110
111
  baseUrl?: string | null;
@@ -302,10 +303,11 @@ const RUNTIME_DB_SESSION_RENEWAL_WINDOW_MS = 60_000;
302
303
  const RUNTIME_API_RETRY_DELAYS_MS = [
303
304
  250, 500, 1_000, 2_000, 4_000, 8_000, 8_000,
304
305
  ] as const;
305
- const RUNTIME_API_MAX_ATTEMPTS = RUNTIME_API_RETRY_DELAYS_MS.length + 1;
306
+ const RUNTIME_API_RESOLVE_PLAY_RETRY_DELAYS_MS = [
307
+ 250, 500, 1_000, 2_000, 4_000, 8_000, 8_000, 15_000, 15_000, 30_000,
308
+ ] as const;
306
309
  const RUNTIME_API_DEFAULT_RETRY_AFTER_MS = 2_000;
307
310
  const RUNTIME_API_REQUEST_TIMEOUT_MS = 30_000;
308
- const vercelProtectionCookieCache = new Map<string, Promise<string | null>>();
309
311
  const RUNTIME_POSTGRES_PREWARM_MAX_ATTEMPTS = 4;
310
312
  const RUNTIME_POSTGRES_PREWARM_RETRY_DELAYS_MS = [250, 750, 1_500] as const;
311
313
  const RUNTIME_POSTGRES_CONNECT_MAX_ATTEMPTS = 4;
@@ -315,11 +317,12 @@ const RUNTIME_WORK_RECEIPT_QUERY_RETRY_DELAYS_MS = [250, 750] as const;
315
317
  // Runtime DB sessions are minted against the pooled tenant endpoint. A healthy
316
318
  // connect is sub-second; spending minutes on one sandbox dial only hides a
317
319
  // broken route and stalls the whole play. Keep retries bounded and loud.
318
- const RUNTIME_POSTGRES_CONNECT_TIMEOUT_MS = 5_000;
320
+ const RUNTIME_POSTGRES_CONNECT_TIMEOUT_MS = 10_000;
319
321
  // Daytona executes one play per runner process. This is the bounded data-plane
320
322
  // budget for that one run's sheet reads/writes, receipts, and terminal/export
321
323
  // reads over the PgBouncer URL; it is not a scheduler/control-plane fan-out.
322
324
  const RUNTIME_POSTGRES_POOL_MAX_CONNECTIONS = 8;
325
+ const RECEIPT_STATUS_QUEUED = RECEIPT_STATUS_CODE.queued;
323
326
  const RECEIPT_STATUS_PENDING = RECEIPT_STATUS_CODE.pending;
324
327
  const RECEIPT_STATUS_RUNNING = RECEIPT_STATUS_CODE.running;
325
328
  const RECEIPT_STATUS_COMPLETED = RECEIPT_STATUS_CODE.completed;
@@ -456,20 +459,12 @@ function resolveRuntimeApiUrl(context: RuntimeApiContext): string {
456
459
  if (!baseUrl) {
457
460
  throw new Error('Runner runtime API requires a baseUrl.');
458
461
  }
459
- const url = new URL(
460
- `${baseUrl.replace(/\/$/, '')}${PLAY_RUNTIME_API_COMPAT_PATH}`,
461
- );
462
- const bypassToken = context.vercelProtectionBypassToken?.trim();
463
- if (bypassToken) {
464
- url.searchParams.set('x-vercel-set-bypass-cookie', 'true');
465
- url.searchParams.set('x-vercel-protection-bypass', bypassToken);
466
- }
467
- return url.toString();
462
+ return `${baseUrl.replace(/\/$/, '')}${PLAY_RUNTIME_API_COMPAT_PATH}`;
468
463
  }
469
464
 
470
465
  function resolveRuntimeApiHeaders(
471
466
  context: RuntimeApiContext,
472
- vercelProtectionCookie?: string | null,
467
+ vercelHeaders: Record<string, string>,
473
468
  ): Record<string, string> {
474
469
  const token = context.executorToken?.trim();
475
470
  if (!token) {
@@ -479,61 +474,16 @@ function resolveRuntimeApiHeaders(
479
474
  'content-type': 'application/json',
480
475
  authorization: `Bearer ${token}`,
481
476
  [PLAY_RUNTIME_CONTRACT_HEADER]: String(PLAY_RUNTIME_CONTRACT),
482
- ...(context.vercelProtectionBypassToken
483
- ? { 'x-vercel-protection-bypass': context.vercelProtectionBypassToken }
484
- : {}),
477
+ ...vercelHeaders,
485
478
  ...(context.runtimeTestFaultHeader?.trim()
486
479
  ? {
487
480
  [PLAY_RUNTIME_TEST_FAULT_HEADER]:
488
481
  context.runtimeTestFaultHeader.trim(),
489
482
  }
490
483
  : {}),
491
- ...(vercelProtectionCookie ? { cookie: vercelProtectionCookie } : {}),
492
484
  };
493
485
  }
494
486
 
495
- function setCookieHeaders(headers: Headers): string[] {
496
- const getter = (headers as Headers & { getSetCookie?: () => string[] })
497
- .getSetCookie;
498
- if (typeof getter === 'function') {
499
- return getter.call(headers);
500
- }
501
- const single = headers.get('set-cookie');
502
- return single ? [single] : [];
503
- }
504
-
505
- function cookieHeaderFromSetCookie(headers: Headers): string | null {
506
- const cookies = setCookieHeaders(headers)
507
- .flatMap((header) => header.split(/,(?=\s*[^;,=]+=[^;,]+)/g))
508
- .map((header) => header.split(';', 1)[0]?.trim() ?? '')
509
- .filter(Boolean);
510
- return cookies.length > 0 ? cookies.join('; ') : null;
511
- }
512
-
513
- function resolveVercelProtectionCookie(
514
- context: RuntimeApiContext,
515
- ): Promise<string | null> {
516
- const bypassToken = context.vercelProtectionBypassToken?.trim();
517
- const baseUrl = context.baseUrl?.trim().replace(/\/$/, '');
518
- if (!bypassToken || !baseUrl) return Promise.resolve(null);
519
-
520
- const cacheKey = `${baseUrl}\n${bypassToken}`;
521
- const cached = vercelProtectionCookieCache.get(cacheKey);
522
- if (cached) return cached;
523
-
524
- const promise = (async () => {
525
- const url = new URL(`${baseUrl}/api/v2/health`);
526
- url.searchParams.set('x-vercel-set-bypass-cookie', 'true');
527
- url.searchParams.set('x-vercel-protection-bypass', bypassToken);
528
- const response = await fetch(url.toString(), {
529
- headers: { 'x-vercel-protection-bypass': bypassToken },
530
- }).catch(() => null);
531
- return response ? cookieHeaderFromSetCookie(response.headers) : null;
532
- })();
533
- vercelProtectionCookieCache.set(cacheKey, promise);
534
- return promise;
535
- }
536
-
537
487
  function normalizeRuntimeUserEmail(
538
488
  value: string | null | undefined,
539
489
  ): string | null {
@@ -546,8 +496,11 @@ async function postRuntimeApi<TResponse>(
546
496
  body: RuntimeApiActionRequest,
547
497
  ): Promise<TResponse> {
548
498
  const url = resolveRuntimeApiUrl(context);
549
- const vercelProtectionCookie = await resolveVercelProtectionCookie(context);
550
- for (let attempt = 1; attempt <= RUNTIME_API_MAX_ATTEMPTS; attempt += 1) {
499
+ const vercelHeaders = vercelProtectionBypassHeader(
500
+ context.vercelProtectionBypassToken,
501
+ );
502
+ const maxAttempts = runtimeApiMaxAttempts(body.action);
503
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
551
504
  let response: Response;
552
505
  const abortController = new AbortController();
553
506
  const timeout = setTimeout(
@@ -557,13 +510,13 @@ async function postRuntimeApi<TResponse>(
557
510
  try {
558
511
  response = await fetch(url, {
559
512
  method: 'POST',
560
- headers: resolveRuntimeApiHeaders(context, vercelProtectionCookie),
513
+ headers: resolveRuntimeApiHeaders(context, vercelHeaders),
561
514
  body: JSON.stringify(body),
562
515
  signal: abortController.signal,
563
516
  });
564
517
  } catch (error) {
565
- if (attempt < RUNTIME_API_MAX_ATTEMPTS) {
566
- await sleepRuntimeApiRetry(attempt);
518
+ if (attempt < maxAttempts) {
519
+ await sleepRuntimeApiRetry(body.action, attempt);
567
520
  continue;
568
521
  }
569
522
  const message = error instanceof Error ? error.message : String(error);
@@ -605,8 +558,8 @@ async function postRuntimeApi<TResponse>(
605
558
  isTransientRuntimeApiErrorMessage(
606
559
  `${typeof parsed?.error === 'string' ? parsed.error : ''}\n${details ?? ''}`,
607
560
  ));
608
- if (shouldRetryRuntimeResponse && attempt < RUNTIME_API_MAX_ATTEMPTS) {
609
- await sleepRuntimeApiRetry(attempt, retryAfterMs);
561
+ if (shouldRetryRuntimeResponse && attempt < maxAttempts) {
562
+ await sleepRuntimeApiRetry(body.action, attempt, retryAfterMs);
610
563
  continue;
611
564
  }
612
565
 
@@ -625,11 +578,12 @@ async function postRuntimeApi<TResponse>(
625
578
  }
626
579
 
627
580
  function runtimeApiRetryDelayMs(
581
+ action: RuntimeApiActionRequest['action'],
628
582
  attempt: number,
629
583
  retryAfterMs?: number,
630
584
  ): number {
631
585
  const configuredDelay =
632
- RUNTIME_API_RETRY_DELAYS_MS[attempt - 1] ??
586
+ runtimeApiRetryDelaysForAction(action)[attempt - 1] ??
633
587
  RUNTIME_API_DEFAULT_RETRY_AFTER_MS;
634
588
  return retryAfterMs === undefined
635
589
  ? configuredDelay
@@ -637,14 +591,29 @@ function runtimeApiRetryDelayMs(
637
591
  }
638
592
 
639
593
  async function sleepRuntimeApiRetry(
594
+ action: RuntimeApiActionRequest['action'],
640
595
  attempt: number,
641
596
  retryAfterMs?: number,
642
597
  ): Promise<void> {
643
598
  await new Promise((resolve) =>
644
- setTimeout(resolve, runtimeApiRetryDelayMs(attempt, retryAfterMs)),
599
+ setTimeout(resolve, runtimeApiRetryDelayMs(action, attempt, retryAfterMs)),
645
600
  );
646
601
  }
647
602
 
603
+ function runtimeApiRetryDelaysForAction(
604
+ action: RuntimeApiActionRequest['action'],
605
+ ): readonly number[] {
606
+ return action === 'resolve_play'
607
+ ? RUNTIME_API_RESOLVE_PLAY_RETRY_DELAYS_MS
608
+ : RUNTIME_API_RETRY_DELAYS_MS;
609
+ }
610
+
611
+ function runtimeApiMaxAttempts(
612
+ action: RuntimeApiActionRequest['action'],
613
+ ): number {
614
+ return runtimeApiRetryDelaysForAction(action).length + 1;
615
+ }
616
+
648
617
  function isTransientRuntimeApiErrorMessage(message: string): boolean {
649
618
  return /timeout exceeded when trying to connect|timed out|fetch failed|ECONNRESET|ECONNREFUSED|UND_ERR_CONNECT_TIMEOUT|requested endpoint could not be found, or you don't have access/i.test(
650
619
  message,
@@ -1303,9 +1272,13 @@ async function prewarmRuntimePostgresSession(
1303
1272
  attempt += 1
1304
1273
  ) {
1305
1274
  try {
1306
- await withRuntimePostgres(session, async (client) => {
1307
- await client.query('SELECT 1');
1308
- });
1275
+ await withRuntimePostgres(
1276
+ session,
1277
+ async (client) => {
1278
+ await client.query('SELECT 1');
1279
+ },
1280
+ { maxConnectAttempts: 1 },
1281
+ );
1309
1282
  return;
1310
1283
  } catch (error) {
1311
1284
  if (
@@ -1454,17 +1427,17 @@ async function resetRuntimePostgresPool(postgresUrl: string): Promise<void> {
1454
1427
  async function withRuntimePostgres<T>(
1455
1428
  session: RuntimePostgresSession,
1456
1429
  fn: (client: RuntimePoolClient) => Promise<T>,
1457
- options: { cachePool?: boolean } = {},
1430
+ options: { cachePool?: boolean; maxConnectAttempts?: number } = {},
1458
1431
  ): Promise<T> {
1459
1432
  let client: RuntimePoolClient | null = null;
1460
1433
  let requestLocalPool: RuntimePool | null = null;
1461
1434
  const cachePool =
1462
1435
  (options.cachePool ?? true) && canReuseRuntimePostgresPoolsAcrossRequests();
1463
- for (
1464
- let attempt = 1;
1465
- attempt <= RUNTIME_POSTGRES_CONNECT_MAX_ATTEMPTS;
1466
- attempt += 1
1467
- ) {
1436
+ const maxConnectAttempts =
1437
+ options.maxConnectAttempts === undefined
1438
+ ? RUNTIME_POSTGRES_CONNECT_MAX_ATTEMPTS
1439
+ : Math.max(1, Math.floor(options.maxConnectAttempts));
1440
+ for (let attempt = 1; attempt <= maxConnectAttempts; attempt += 1) {
1468
1441
  try {
1469
1442
  const pool = getPostgresPool(session.postgresUrl, cachePool);
1470
1443
  if (!cachePool) {
@@ -1484,7 +1457,7 @@ async function withRuntimePostgres<T>(
1484
1457
  requestLocalPool = null;
1485
1458
  }
1486
1459
  if (
1487
- attempt >= RUNTIME_POSTGRES_CONNECT_MAX_ATTEMPTS ||
1460
+ attempt >= maxConnectAttempts ||
1488
1461
  !isTransientRuntimePostgresConnectionError(error)
1489
1462
  ) {
1490
1463
  throw error;
@@ -2658,6 +2631,8 @@ async function prepareRuntimeSheetDatasetRows(
2658
2631
  '$9::timestamptz',
2659
2632
  '$10::integer',
2660
2633
  );
2634
+ const targetOlderForeignAttemptOwnerSql =
2635
+ 'coalesce(target._attempt_owner_run_id, target._run_id) IS DISTINCT FROM $8::text AND COALESCE(target._attempt_seq, 0) < $10::integer';
2661
2636
  const existingAttemptFenceSql = activeRuntimeSheetAttemptFenceSql(
2662
2637
  'existing',
2663
2638
  '$7::text',
@@ -2665,6 +2640,8 @@ async function prepareRuntimeSheetDatasetRows(
2665
2640
  '$9::timestamptz',
2666
2641
  '$10::integer',
2667
2642
  );
2643
+ const existingForeignAttemptOwnerSql =
2644
+ 'coalesce(existing._attempt_owner_run_id, existing._run_id) IS DISTINCT FROM $8::text';
2668
2645
  const targetNewerTerminalRowSql = newerTerminalRuntimeSheetRowSql(
2669
2646
  'target',
2670
2647
  '$9::timestamptz',
@@ -2776,10 +2753,14 @@ async function prepareRuntimeSheetDatasetRows(
2776
2753
  WHERE target._key = input_rows._key
2777
2754
  AND target._status IN ('pending', 'running', 'failed')
2778
2755
  AND NOT (${targetNewerTerminalRowSql})
2779
- AND (
2780
- target._status = 'failed'
2781
- OR ${targetAttemptFenceSql}
2782
- )
2756
+ AND (
2757
+ ${targetAttemptFenceSql}
2758
+ OR ${targetOlderForeignAttemptOwnerSql}
2759
+ OR (
2760
+ $11::boolean
2761
+ AND target._status = 'failed'
2762
+ )
2763
+ )
2783
2764
  AND (
2784
2765
  target._run_id IS DISTINCT FROM $4::text
2785
2766
  OR target._input_index IS DISTINCT FROM input_rows._input_index
@@ -2808,10 +2789,17 @@ async function prepareRuntimeSheetDatasetRows(
2808
2789
  WHERE (
2809
2790
  existing._status IN ('pending', 'running', 'failed')
2810
2791
  AND NOT (${existingNewerTerminalRowSql})
2811
- AND (
2812
- existing._status = 'failed'
2813
- OR ${existingAttemptFenceSql}
2814
- )
2792
+ AND (
2793
+ ${existingAttemptFenceSql}
2794
+ OR (
2795
+ ${existingForeignAttemptOwnerSql}
2796
+ AND COALESCE(existing._attempt_seq, 0) < $10::integer
2797
+ )
2798
+ OR (
2799
+ $11::boolean
2800
+ AND existing._status = 'failed'
2801
+ )
2802
+ )
2815
2803
  )
2816
2804
  OR (
2817
2805
  existing._status = 'enriched'
@@ -3110,6 +3098,8 @@ async function markRuntimeRowsPendingForRecompute(
3110
3098
  '$7::timestamptz',
3111
3099
  '$8::integer',
3112
3100
  );
3101
+ const targetOlderForeignAttemptOwnerSql =
3102
+ 'coalesce(target._attempt_owner_run_id, target._run_id) IS DISTINCT FROM $6::text AND COALESCE(target._attempt_seq, 0) < $8::integer';
3113
3103
  const targetWritableEnrichedAttemptSql =
3114
3104
  writableEnrichedRuntimeSheetAttemptSql(
3115
3105
  'target',
@@ -3138,7 +3128,18 @@ async function markRuntimeRowsPendingForRecompute(
3138
3128
  FROM target_rows
3139
3129
  WHERE target._key = target_rows._key
3140
3130
  AND (
3141
- ${targetAttemptFenceSql}
3131
+ (
3132
+ target._status IN ('pending', 'running', 'failed')
3133
+ AND (
3134
+ ${targetAttemptFenceSql}
3135
+ OR ${targetOlderForeignAttemptOwnerSql}
3136
+ OR (
3137
+ $10::boolean
3138
+ AND target._status = 'failed'
3139
+ )
3140
+ )
3141
+ )
3142
+ OR ${targetAttemptFenceSql}
3142
3143
  OR (
3143
3144
  $10::boolean
3144
3145
  AND target._status = 'enriched'
@@ -3450,6 +3451,10 @@ export async function claimRuntimeWorkReceipt(
3450
3451
  input.leaseTtlMs,
3451
3452
  PLAY_RUNTIME_WORK_RECEIPT_LEASE_TTL_MS,
3452
3453
  );
3454
+ const claimStatus =
3455
+ input.leaseAware === true
3456
+ ? RECEIPT_STATUS_QUEUED
3457
+ : RECEIPT_STATUS_RUNNING;
3453
3458
  const claimableStatuses = workReceiptClaimableStatusCodes({
3454
3459
  forceRefresh: input.forceRefresh,
3455
3460
  forceFailedRefresh: input.forceFailedRefresh,
@@ -3457,7 +3462,13 @@ export async function claimRuntimeWorkReceipt(
3457
3462
  });
3458
3463
  const { rows } = await client.query<Record<string, unknown>>(
3459
3464
  `
3460
- WITH claimed AS (
3465
+ WITH existing AS MATERIALIZED (
3466
+ SELECT status
3467
+ FROM ${workReceiptTable(session)}
3468
+ WHERE k = decode($1, 'hex')
3469
+ FOR UPDATE
3470
+ ),
3471
+ claimed AS (
3461
3472
  INSERT INTO ${workReceiptTable(session)} (
3462
3473
  k,
3463
3474
  status,
@@ -3500,6 +3511,7 @@ export async function claimRuntimeWorkReceipt(
3500
3511
  claimableStatusesSql: '$5',
3501
3512
  claimantRunIdSql: '$3',
3502
3513
  claimantRunAttemptSql: '$9',
3514
+ reclaimRunningSql: '$11',
3503
3515
  forceRefreshSql: '$8',
3504
3516
  forceFailedRefreshSql: '$10',
3505
3517
  })}
@@ -3525,25 +3537,39 @@ export async function claimRuntimeWorkReceipt(
3525
3537
  lease_owner_run_id,
3526
3538
  lease_owner_attempt,
3527
3539
  lease_expires_at,
3528
- updated_at
3540
+ updated_at,
3541
+ EXISTS (
3542
+ SELECT 1
3543
+ FROM existing
3544
+ WHERE status = $12::smallint
3545
+ ) AS refreshed_failed_receipt
3529
3546
  FROM claimed
3530
3547
  `,
3531
3548
  [
3532
3549
  workReceiptKeyHex(input.key),
3533
- RECEIPT_STATUS_RUNNING,
3550
+ claimStatus,
3534
3551
  input.runId,
3535
- RECEIPT_STATUS_RUNNING,
3552
+ claimStatus,
3536
3553
  claimableStatuses,
3537
3554
  leaseId,
3538
3555
  leaseTtlMs,
3539
3556
  input.forceRefresh === true,
3540
3557
  runAttempt,
3541
3558
  input.forceFailedRefresh === true,
3559
+ input.reclaimRunning === true,
3560
+ RECEIPT_STATUS_CODE.failed,
3542
3561
  ],
3543
3562
  );
3544
- const claimed = rows[0] ? mapRuntimeWorkReceiptRow(rows[0]) : null;
3563
+ const claimedRow = rows[0] ?? null;
3564
+ const claimed = claimedRow ? mapRuntimeWorkReceiptRow(claimedRow) : null;
3545
3565
  if (claimed) {
3546
- return { disposition: 'claimed', receipt: claimed };
3566
+ return {
3567
+ disposition: 'claimed',
3568
+ receipt: claimed,
3569
+ wasFailed:
3570
+ claimedRow.refreshed_failed_receipt === true ||
3571
+ claimedRow.refreshed_failed_receipt === 'true',
3572
+ };
3547
3573
  }
3548
3574
 
3549
3575
  const latest = await readRuntimeWorkReceipt(client, session, input.key);
@@ -3577,7 +3603,11 @@ function runtimeWorkReceiptClaimDispositionForBlockedReceipt(input: {
3577
3603
  if (receipt.status === 'failed') {
3578
3604
  return { disposition: 'failed', receipt };
3579
3605
  }
3580
- if (receipt.status === 'pending' || receipt.status === 'running') {
3606
+ if (
3607
+ receipt.status === 'queued' ||
3608
+ receipt.status === 'pending' ||
3609
+ receipt.status === 'running'
3610
+ ) {
3581
3611
  const ownerRunId = receipt.leaseOwnerRunId ?? receipt.runId ?? null;
3582
3612
  const leaseExpiresAtMs =
3583
3613
  typeof receipt.leaseExpiresAt === 'string'
@@ -3628,6 +3658,10 @@ export async function claimRuntimeWorkReceipts(
3628
3658
  const leaseIds = keys.map(() =>
3629
3659
  input.leaseAware === true ? newRuntimeWorkReceiptLeaseId() : null,
3630
3660
  );
3661
+ const claimStatus =
3662
+ input.leaseAware === true
3663
+ ? RECEIPT_STATUS_QUEUED
3664
+ : RECEIPT_STATUS_RUNNING;
3631
3665
  const runAttempt = normalizeRuntimeRunAttempt(input.runAttempt);
3632
3666
  const leaseTtlMs = normalizeRuntimeLeaseTtlMs(
3633
3667
  input.leaseTtlMs,
@@ -3697,6 +3731,7 @@ export async function claimRuntimeWorkReceipts(
3697
3731
  claimableStatusesSql: '$6',
3698
3732
  claimantRunIdSql: '$4::text',
3699
3733
  claimantRunAttemptSql: '$9',
3734
+ reclaimRunningSql: '$11',
3700
3735
  forceRefreshSql: '$8',
3701
3736
  forceFailedRefreshSql: '$10',
3702
3737
  })}
@@ -3783,14 +3818,15 @@ export async function claimRuntimeWorkReceipts(
3783
3818
  [
3784
3819
  keyHexes,
3785
3820
  leaseIds,
3786
- RECEIPT_STATUS_RUNNING,
3821
+ claimStatus,
3787
3822
  input.runId,
3788
- RECEIPT_STATUS_RUNNING,
3823
+ claimStatus,
3789
3824
  reclaimStatuses,
3790
3825
  leaseTtlMs,
3791
3826
  input.forceRefresh === true,
3792
3827
  runAttempt,
3793
3828
  input.forceFailedRefresh === true,
3829
+ input.reclaimRunning === true,
3794
3830
  ],
3795
3831
  );
3796
3832
  return rows.map((row) => {
@@ -3811,6 +3847,317 @@ export async function claimRuntimeWorkReceipts(
3811
3847
  );
3812
3848
  }
3813
3849
 
3850
+ export async function markRuntimeWorkReceiptRunning(
3851
+ context: RuntimeApiContext,
3852
+ input: {
3853
+ playName: string;
3854
+ runId: string;
3855
+ key: string;
3856
+ runAttempt?: number | null;
3857
+ leaseId?: string | null;
3858
+ leaseTtlMs?: number | null;
3859
+ },
3860
+ ): Promise<WorkReceipt | null> {
3861
+ const session = await getRuntimeWorkReceiptSession(context, {
3862
+ playName: input.playName,
3863
+ key: input.key,
3864
+ });
3865
+ return await withRuntimeWorkReceiptClient(
3866
+ context,
3867
+ session,
3868
+ async (client) => {
3869
+ const leaseId = input.leaseId?.trim() || null;
3870
+ const runAttempt = normalizeRuntimeRunAttempt(input.runAttempt);
3871
+ const leaseTtlMs = normalizeRuntimeLeaseTtlMs(
3872
+ input.leaseTtlMs,
3873
+ PLAY_RUNTIME_WORK_RECEIPT_LEASE_TTL_MS,
3874
+ );
3875
+ const { rows } = await client.query<Record<string, unknown>>(
3876
+ `
3877
+ WITH running AS (
3878
+ UPDATE ${workReceiptTable(session)}
3879
+ SET status = $2::smallint,
3880
+ lease_expires_at = CASE
3881
+ WHEN lease_id IS NULL THEN NULL
3882
+ ELSE now() + ($6::double precision * interval '1 millisecond')
3883
+ END,
3884
+ updated_at = now()
3885
+ WHERE k = decode($1, 'hex')
3886
+ AND ${workReceiptActiveOwnerPredicateSql({
3887
+ receiptTable: workReceiptTable(session),
3888
+ ownerRunIdSql: '$3',
3889
+ ownerRunAttemptSql: '$5',
3890
+ leaseIdSql: '$4::text',
3891
+ })}
3892
+ RETURNING convert_from(k, 'UTF8') AS k,
3893
+ status,
3894
+ output,
3895
+ error,
3896
+ failure_kind,
3897
+ run_id,
3898
+ lease_id,
3899
+ lease_owner_run_id,
3900
+ lease_owner_attempt,
3901
+ lease_expires_at,
3902
+ updated_at
3903
+ )
3904
+ SELECT k, status, output, error, failure_kind, run_id, lease_id, lease_owner_run_id, lease_owner_attempt, lease_expires_at, updated_at
3905
+ FROM running
3906
+ `,
3907
+ [
3908
+ workReceiptKeyHex(input.key),
3909
+ RECEIPT_STATUS_RUNNING,
3910
+ input.runId,
3911
+ leaseId,
3912
+ runAttempt,
3913
+ leaseTtlMs,
3914
+ ],
3915
+ );
3916
+ return rows[0] ? mapRuntimeWorkReceiptRow(rows[0]) : null;
3917
+ },
3918
+ );
3919
+ }
3920
+
3921
+ export async function markRuntimeWorkReceiptsRunning(
3922
+ context: RuntimeApiContext,
3923
+ input: {
3924
+ playName: string;
3925
+ receipts: Array<{
3926
+ runId: string;
3927
+ key: string;
3928
+ leaseId?: string | null;
3929
+ runAttempt?: number | null;
3930
+ }>;
3931
+ leaseTtlMs?: number | null;
3932
+ },
3933
+ ): Promise<Array<WorkReceipt | null>> {
3934
+ const receipts = input.receipts.filter((receipt) => receipt.key.trim());
3935
+ if (receipts.length === 0) return [];
3936
+ const leaseTtlMs = normalizeRuntimeLeaseTtlMs(
3937
+ input.leaseTtlMs,
3938
+ PLAY_RUNTIME_WORK_RECEIPT_LEASE_TTL_MS,
3939
+ );
3940
+ const session = await getRuntimeWorkReceiptSessionForKeys(context, {
3941
+ playName: input.playName,
3942
+ keys: receipts.map((receipt) => receipt.key),
3943
+ });
3944
+ return await withRuntimeWorkReceiptClient(
3945
+ context,
3946
+ session,
3947
+ async (client) => {
3948
+ const { rows } = await client.query<Record<string, unknown>>(
3949
+ `
3950
+ WITH inputs AS (
3951
+ SELECT key_values.key_hex,
3952
+ run_values.run_id,
3953
+ lease_values.lease_id,
3954
+ attempt_values.run_attempt,
3955
+ key_values.ord
3956
+ FROM unnest($1::text[]) WITH ORDINALITY AS key_values(key_hex, ord)
3957
+ JOIN unnest($2::text[]) WITH ORDINALITY AS run_values(run_id, ord)
3958
+ ON run_values.ord = key_values.ord
3959
+ JOIN unnest($3::text[]) WITH ORDINALITY AS lease_values(lease_id, ord)
3960
+ ON lease_values.ord = key_values.ord
3961
+ JOIN unnest($4::integer[]) WITH ORDINALITY AS attempt_values(run_attempt, ord)
3962
+ ON attempt_values.ord = key_values.ord
3963
+ ),
3964
+ running AS (
3965
+ UPDATE ${workReceiptTable(session)} AS target
3966
+ SET status = $5::smallint,
3967
+ lease_expires_at = CASE
3968
+ WHEN target.lease_id IS NULL THEN NULL
3969
+ ELSE now() + ($6::double precision * interval '1 millisecond')
3970
+ END,
3971
+ updated_at = now()
3972
+ FROM inputs
3973
+ WHERE target.k = decode(inputs.key_hex, 'hex')
3974
+ AND ${workReceiptActiveOwnerPredicateSql({
3975
+ receiptTable: 'target',
3976
+ ownerRunIdSql: 'inputs.run_id',
3977
+ ownerRunAttemptSql: 'inputs.run_attempt',
3978
+ leaseIdSql: 'inputs.lease_id',
3979
+ })}
3980
+ RETURNING target.k,
3981
+ target.status,
3982
+ target.output,
3983
+ target.error,
3984
+ target.failure_kind,
3985
+ target.run_id,
3986
+ target.lease_id,
3987
+ target.lease_owner_run_id,
3988
+ target.lease_owner_attempt,
3989
+ target.lease_expires_at,
3990
+ target.updated_at,
3991
+ inputs.ord
3992
+ ),
3993
+ returned AS (
3994
+ SELECT running.k,
3995
+ running.status,
3996
+ running.output,
3997
+ running.error,
3998
+ running.failure_kind,
3999
+ running.run_id,
4000
+ running.lease_id,
4001
+ running.lease_owner_run_id,
4002
+ running.lease_owner_attempt,
4003
+ running.lease_expires_at,
4004
+ running.updated_at,
4005
+ inputs.ord
4006
+ FROM inputs
4007
+ LEFT JOIN running ON running.ord = inputs.ord
4008
+ )
4009
+ SELECT convert_from(returned.k, 'UTF8') AS k,
4010
+ returned.status,
4011
+ returned.output,
4012
+ returned.error,
4013
+ returned.failure_kind,
4014
+ returned.run_id,
4015
+ returned.lease_id,
4016
+ returned.lease_owner_run_id,
4017
+ returned.lease_owner_attempt,
4018
+ returned.lease_expires_at,
4019
+ returned.updated_at
4020
+ FROM returned
4021
+ ORDER BY returned.ord
4022
+ `,
4023
+ [
4024
+ receipts.map((receipt) => workReceiptKeyHex(receipt.key)),
4025
+ receipts.map((receipt) => receipt.runId),
4026
+ receipts.map((receipt) => receipt.leaseId?.trim() || null),
4027
+ receipts.map((receipt) =>
4028
+ normalizeRuntimeRunAttempt(receipt.runAttempt),
4029
+ ),
4030
+ RECEIPT_STATUS_RUNNING,
4031
+ leaseTtlMs,
4032
+ ],
4033
+ );
4034
+ return rows.map((row) =>
4035
+ row.k == null ? null : mapRuntimeWorkReceiptRow(row),
4036
+ );
4037
+ },
4038
+ );
4039
+ }
4040
+
4041
+ export async function markRuntimeWorkReceiptsQueued(
4042
+ context: RuntimeApiContext,
4043
+ input: {
4044
+ playName: string;
4045
+ receipts: Array<{
4046
+ runId: string;
4047
+ key: string;
4048
+ leaseId?: string | null;
4049
+ runAttempt?: number | null;
4050
+ }>;
4051
+ leaseTtlMs?: number | null;
4052
+ },
4053
+ ): Promise<Array<WorkReceipt | null>> {
4054
+ const receipts = input.receipts.filter((receipt) => receipt.key.trim());
4055
+ if (receipts.length === 0) return [];
4056
+ const leaseTtlMs = normalizeRuntimeLeaseTtlMs(
4057
+ input.leaseTtlMs,
4058
+ PLAY_RUNTIME_WORK_RECEIPT_LEASE_TTL_MS,
4059
+ );
4060
+ const session = await getRuntimeWorkReceiptSessionForKeys(context, {
4061
+ playName: input.playName,
4062
+ keys: receipts.map((receipt) => receipt.key),
4063
+ });
4064
+ return await withRuntimeWorkReceiptClient(
4065
+ context,
4066
+ session,
4067
+ async (client) => {
4068
+ const { rows } = await client.query<Record<string, unknown>>(
4069
+ `
4070
+ WITH inputs AS (
4071
+ SELECT key_values.key_hex,
4072
+ run_values.run_id,
4073
+ lease_values.lease_id,
4074
+ attempt_values.run_attempt,
4075
+ key_values.ord
4076
+ FROM unnest($1::text[]) WITH ORDINALITY AS key_values(key_hex, ord)
4077
+ JOIN unnest($2::text[]) WITH ORDINALITY AS run_values(run_id, ord)
4078
+ ON run_values.ord = key_values.ord
4079
+ JOIN unnest($3::text[]) WITH ORDINALITY AS lease_values(lease_id, ord)
4080
+ ON lease_values.ord = key_values.ord
4081
+ JOIN unnest($4::integer[]) WITH ORDINALITY AS attempt_values(run_attempt, ord)
4082
+ ON attempt_values.ord = key_values.ord
4083
+ ),
4084
+ queued AS (
4085
+ UPDATE ${workReceiptTable(session)} AS target
4086
+ SET status = $5::smallint,
4087
+ lease_expires_at = CASE
4088
+ WHEN target.lease_id IS NULL THEN NULL
4089
+ ELSE now() + ($6::double precision * interval '1 millisecond')
4090
+ END,
4091
+ updated_at = now()
4092
+ FROM inputs
4093
+ WHERE target.k = decode(inputs.key_hex, 'hex')
4094
+ AND ${workReceiptActiveOwnerPredicateSql({
4095
+ receiptTable: 'target',
4096
+ ownerRunIdSql: 'inputs.run_id',
4097
+ ownerRunAttemptSql: 'inputs.run_attempt',
4098
+ leaseIdSql: 'inputs.lease_id',
4099
+ })}
4100
+ RETURNING target.k,
4101
+ target.status,
4102
+ target.output,
4103
+ target.error,
4104
+ target.failure_kind,
4105
+ target.run_id,
4106
+ target.lease_id,
4107
+ target.lease_owner_run_id,
4108
+ target.lease_owner_attempt,
4109
+ target.lease_expires_at,
4110
+ target.updated_at,
4111
+ inputs.ord
4112
+ ),
4113
+ returned AS (
4114
+ SELECT queued.k,
4115
+ queued.status,
4116
+ queued.output,
4117
+ queued.error,
4118
+ queued.failure_kind,
4119
+ queued.run_id,
4120
+ queued.lease_id,
4121
+ queued.lease_owner_run_id,
4122
+ queued.lease_owner_attempt,
4123
+ queued.lease_expires_at,
4124
+ queued.updated_at,
4125
+ inputs.ord
4126
+ FROM inputs
4127
+ LEFT JOIN queued ON queued.ord = inputs.ord
4128
+ )
4129
+ SELECT convert_from(returned.k, 'UTF8') AS k,
4130
+ returned.status,
4131
+ returned.output,
4132
+ returned.error,
4133
+ returned.failure_kind,
4134
+ returned.run_id,
4135
+ returned.lease_id,
4136
+ returned.lease_owner_run_id,
4137
+ returned.lease_owner_attempt,
4138
+ returned.lease_expires_at,
4139
+ returned.updated_at
4140
+ FROM returned
4141
+ ORDER BY returned.ord
4142
+ `,
4143
+ [
4144
+ receipts.map((receipt) => workReceiptKeyHex(receipt.key)),
4145
+ receipts.map((receipt) => receipt.runId),
4146
+ receipts.map((receipt) => receipt.leaseId?.trim() || null),
4147
+ receipts.map((receipt) =>
4148
+ normalizeRuntimeRunAttempt(receipt.runAttempt),
4149
+ ),
4150
+ RECEIPT_STATUS_QUEUED,
4151
+ leaseTtlMs,
4152
+ ],
4153
+ );
4154
+ return rows.map((row) =>
4155
+ row.k == null ? null : mapRuntimeWorkReceiptRow(row),
4156
+ );
4157
+ },
4158
+ );
4159
+ }
4160
+
3814
4161
  export async function completeRuntimeWorkReceipt(
3815
4162
  context: RuntimeApiContext,
3816
4163
  input: {
@@ -3876,7 +4223,7 @@ export async function completeRuntimeWorkReceipt(
3876
4223
  lease_expires_at = NULL,
3877
4224
  updated_at = now()
3878
4225
  WHERE k = decode($1, 'hex')
3879
- AND ${workReceiptRunningOwnerPredicateSql({
4226
+ AND ${workReceiptActiveOwnerPredicateSql({
3880
4227
  receiptTable: workReceiptTable(session),
3881
4228
  ownerRunIdSql: '$4',
3882
4229
  ownerRunAttemptSql: '$6',
@@ -4012,7 +4359,7 @@ export async function completeRuntimeWorkReceipts(
4012
4359
  updated_at = now()
4013
4360
  FROM inputs
4014
4361
  WHERE target.k = decode(inputs.key_hex, 'hex')
4015
- AND ${workReceiptRunningOwnerPredicateSql({
4362
+ AND ${workReceiptActiveOwnerPredicateSql({
4016
4363
  receiptTable: 'target',
4017
4364
  ownerRunIdSql: 'inputs.run_id',
4018
4365
  ownerRunAttemptSql: 'inputs.run_attempt',
@@ -4120,7 +4467,7 @@ export async function failRuntimeWorkReceipt(
4120
4467
  lease_expires_at = NULL,
4121
4468
  updated_at = now()
4122
4469
  WHERE k = decode($1, 'hex')
4123
- AND ${workReceiptRunningOwnerPredicateSql({
4470
+ AND ${workReceiptActiveOwnerPredicateSql({
4124
4471
  receiptTable: workReceiptTable(session),
4125
4472
  ownerRunIdSql: '$4',
4126
4473
  ownerRunAttemptSql: '$7',
@@ -4190,7 +4537,7 @@ export async function releaseRuntimeWorkReceipt(
4190
4537
  lease_expires_at = NULL,
4191
4538
  updated_at = now()
4192
4539
  WHERE k = decode($1, 'hex')
4193
- AND ${workReceiptRunningOwnerPredicateSql({
4540
+ AND ${workReceiptActiveOwnerPredicateSql({
4194
4541
  receiptTable: workReceiptTable(session),
4195
4542
  ownerRunIdSql: '$3',
4196
4543
  ownerRunAttemptSql: '$5',
@@ -4283,7 +4630,7 @@ export async function failRuntimeWorkReceipts(
4283
4630
  updated_at = now()
4284
4631
  FROM inputs
4285
4632
  WHERE target.k = decode(inputs.key_hex, 'hex')
4286
- AND ${workReceiptRunningOwnerPredicateSql({
4633
+ AND ${workReceiptActiveOwnerPredicateSql({
4287
4634
  receiptTable: 'target',
4288
4635
  ownerRunIdSql: 'inputs.run_id',
4289
4636
  ownerRunAttemptSql: 'inputs.run_attempt',
@@ -4493,7 +4840,7 @@ export async function skipRuntimeWorkReceipt(
4493
4840
  lease_expires_at = NULL,
4494
4841
  updated_at = now()
4495
4842
  WHERE k = decode($1, 'hex')
4496
- AND ${workReceiptRunningOwnerPredicateSql({
4843
+ AND ${workReceiptActiveOwnerPredicateSql({
4497
4844
  receiptTable: workReceiptTable(session),
4498
4845
  ownerRunIdSql: '$4',
4499
4846
  ownerRunAttemptSql: '$6',
@@ -5122,6 +5469,13 @@ async function completeRuntimeMapRowChunks(
5122
5469
  '$11::timestamptz',
5123
5470
  '$12',
5124
5471
  );
5472
+ const writableNonTerminalAttemptSql = `(
5473
+ target._status <> 'enriched'
5474
+ AND (
5475
+ ${activeRuntimeSheetAttemptFenceSql('target', '$9::text', '$10::text', '$11::timestamptz', '$12::integer')}
5476
+ OR coalesce(target._attempt_owner_run_id, target._run_id) IS DISTINCT FROM $10::text
5477
+ )
5478
+ )`;
5125
5479
  const sameOwnerTerminalEpochSql = sameOwnerTerminalAttemptEpochSql(
5126
5480
  'target',
5127
5481
  '$10::text',
@@ -5174,6 +5528,7 @@ async function completeRuntimeMapRowChunks(
5174
5528
  AND NOT (${sameOwnerTerminalEpochSql})
5175
5529
  AND (
5176
5530
  ($9::text IS NULL AND target._run_id = $5::text)
5531
+ OR ($9::text IS NOT NULL AND ${writableNonTerminalAttemptSql})
5177
5532
  OR ($9::text IS NOT NULL AND ${activeRuntimeSheetAttemptFenceSql('target', '$9::text', '$10::text', '$11::timestamptz', '$12::integer')})
5178
5533
  OR ($9::text IS NOT NULL AND ${writableEnrichedAttemptSql})
5179
5534
  )
@@ -5208,6 +5563,7 @@ async function completeRuntimeMapRowChunks(
5208
5563
  (${newerTerminalRuntimeSheetRowSql('target', '$11::timestamptz', '$12::integer', '$9::text', '$10::text')})
5209
5564
  OR NOT (
5210
5565
  ($9::text IS NULL AND target._run_id = $5::text)
5566
+ OR ($9::text IS NOT NULL AND ${writableNonTerminalAttemptSql})
5211
5567
  OR ($9::text IS NOT NULL AND ${activeRuntimeSheetAttemptFenceSql('target', '$9::text', '$10::text', '$11::timestamptz', '$12::integer')})
5212
5568
  OR ($9::text IS NOT NULL AND ${writableEnrichedAttemptSql})
5213
5569
  )