deepline 0.3.332 → 0.3.334

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 (122) hide show
  1. package/dist/bundling-sources/sdk/src/play.ts +1 -1
  2. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  3. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +200 -43
  4. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +4 -0
  5. package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +8 -2
  6. package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-invocation-fence.ts +16 -0
  7. package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +30 -9
  8. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +6 -0
  9. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +11 -7
  10. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-deadline.ts +14 -14
  11. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +12 -5
  12. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +6 -1
  13. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-scope.ts +95 -24
  14. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +132 -37
  15. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +13 -5
  16. package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +1 -0
  17. package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +87 -3
  18. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +7 -0
  19. package/dist/cli/{auth-V4IWMXTG.mjs → auth-PAEBUPRS.mjs} +4 -4
  20. package/dist/cli/auth-WR3XFOHN.js +31 -0
  21. package/dist/cli/{billing-GYW4BCPG.mjs → billing-QGQJUM5G.mjs} +4 -4
  22. package/dist/cli/{billing-BW37QW4H.js → billing-V746QMQC.js} +71 -71
  23. package/dist/cli/{chunk-GOH5BRGN.js → chunk-6466HVYR.js} +26 -26
  24. package/dist/cli/{chunk-JHF3GWKX.js → chunk-6MVMTDL7.js} +4 -4
  25. package/dist/cli/{chunk-TTJ7KELW.js → chunk-7XXNMV6G.js} +45 -45
  26. package/dist/cli/{chunk-WQI642QX.mjs → chunk-A7S4PRK2.mjs} +1 -1
  27. package/dist/cli/{chunk-HKQMCTEE.mjs → chunk-EJLUQARV.mjs} +4 -4
  28. package/dist/cli/{chunk-SBPJWA6Y.mjs → chunk-EJVAWHQ7.mjs} +6 -6
  29. package/dist/cli/{chunk-GDBJYM5N.js → chunk-FLMBKUHB.js} +60 -60
  30. package/dist/cli/{chunk-U2B5KDNH.js → chunk-GNHI4WZC.js} +15 -15
  31. package/dist/cli/{chunk-KYQPEIKA.mjs → chunk-GUMAWNWZ.mjs} +5 -5
  32. package/dist/cli/{chunk-CYLZE6RJ.js → chunk-GX2YGFQV.js} +1 -1
  33. package/dist/cli/{chunk-X6MY7BC2.js → chunk-HIPRSI5H.js} +225 -225
  34. package/dist/cli/{chunk-AF6XLGZT.js → chunk-NLHRXDIT.js} +2 -2
  35. package/dist/cli/{chunk-UMKN2BZK.mjs → chunk-O564PHDU.mjs} +1 -1
  36. package/dist/cli/{chunk-MXJVBJYC.mjs → chunk-O64RSXDZ.mjs} +2 -2
  37. package/dist/cli/{chunk-3E7P62NA.mjs → chunk-P3ZX3NSE.mjs} +1 -1
  38. package/dist/cli/{chunk-ERPCUHOQ.js → chunk-R6O3YVIG.js} +8 -8
  39. package/dist/cli/{chunk-PK4EE6T3.js → chunk-SABEM6FL.js} +48 -48
  40. package/dist/cli/{chunk-J5Z5BQGE.mjs → chunk-SBEWKGUA.mjs} +1 -1
  41. package/dist/cli/{chunk-4RLN2DHZ.mjs → chunk-TT2CLWMK.mjs} +1 -1
  42. package/dist/cli/{chunk-B6R2V3TH.mjs → chunk-TVIWEQ4V.mjs} +3 -3
  43. package/dist/cli/{chunk-MSQK2FLJ.mjs → chunk-U724V33M.mjs} +2 -2
  44. package/dist/cli/{chunk-T5TGWH4J.js → chunk-UUB3FTKZ.js} +6 -6
  45. package/dist/cli/{chunk-LAJ56FLL.mjs → chunk-VB7TNIUB.mjs} +1 -1
  46. package/dist/cli/{chunk-ZZWCK6F3.js → chunk-XXZSLV7B.js} +6 -6
  47. package/dist/cli/{chunk-5FHGKEAD.mjs → chunk-Y2VN54ME.mjs} +2 -2
  48. package/dist/cli/{chunk-XON4H3CO.js → chunk-Z6VHC643.js} +9 -9
  49. package/dist/cli/client-6OBIO6GC.js +20 -0
  50. package/dist/cli/{client-TBCDCJ7T.mjs → client-JIZWAGVW.mjs} +3 -3
  51. package/dist/cli/{csv-O3NEPSU3.mjs → csv-7DCY7Z6R.mjs} +3 -3
  52. package/dist/cli/{csv-EFQ7UL3M.js → csv-KAFKXAFU.js} +12 -12
  53. package/dist/cli/{db-2FQ3T4LT.js → db-5LJPO2VA.js} +29 -29
  54. package/dist/cli/{db-RUWCFINP.mjs → db-KJBWUFPL.mjs} +4 -4
  55. package/dist/cli/{describe-4KSVRYPM.mjs → describe-CPF4KDKZ.mjs} +8 -8
  56. package/dist/cli/{describe-YJFDN54P.js → describe-VJEEVOH6.js} +15 -15
  57. package/dist/cli/{executions-MG63LYMV.mjs → executions-LJN5QOK7.mjs} +4 -4
  58. package/dist/cli/{executions-O7IIWVRV.js → executions-LPVCV5SC.js} +6 -6
  59. package/dist/cli/{feedback-WMYRDK2J.mjs → feedback-3HYS6ECC.mjs} +3 -3
  60. package/dist/cli/{feedback-SR2X2H4N.js → feedback-K26WP7HJ.js} +6 -6
  61. package/dist/cli/{git-NIGE3SIJ.mjs → git-U4VBLXSS.mjs} +4 -4
  62. package/dist/cli/git-VMQTXKJQ.js +23 -0
  63. package/dist/cli/index.js +79 -79
  64. package/dist/cli/index.mjs +31 -31
  65. package/dist/cli/{legacy-noops-TXSBNOG6.js → legacy-noops-32YL3VF4.js} +7 -7
  66. package/dist/cli/{legacy-noops-YXYYCDSR.mjs → legacy-noops-ORHX2FMA.mjs} +4 -4
  67. package/dist/cli/{monitors-QYAGRL6V.mjs → monitors-5JJBEJS7.mjs} +4 -4
  68. package/dist/cli/{monitors-ZZUQMDUA.js → monitors-NFE4ICED.js} +85 -85
  69. package/dist/cli/{org-QQSCJJMM.js → org-7CJS35TF.js} +60 -60
  70. package/dist/cli/{org-6NUUN2P7.mjs → org-RJYSDJ3W.mjs} +4 -4
  71. package/dist/cli/play-FUZYMPHF.js +153 -0
  72. package/dist/cli/{play-3G2A53MD.mjs → play-H2QUTJR2.mjs} +7 -7
  73. package/dist/cli/{providers-IGD3ZYTA.mjs → providers-TEVRE4Q6.mjs} +4 -4
  74. package/dist/cli/{providers-AASOAAWT.js → providers-VUKRZWZY.js} +7 -7
  75. package/dist/cli/{quickstart-RRCLOSHQ.mjs → quickstart-2MIHVSZO.mjs} +3 -3
  76. package/dist/cli/{quickstart-NDUT5BVF.js → quickstart-36OGYQ2P.js} +5 -5
  77. package/dist/cli/search-DFSHYVZ5.js +26 -0
  78. package/dist/cli/{search-37MO75IS.mjs → search-IRRQSN6R.mjs} +5 -5
  79. package/dist/cli/{secrets-3OTOG64E.mjs → secrets-3W6OVDVE.mjs} +4 -4
  80. package/dist/cli/{secrets-QXY6DCJA.js → secrets-K67ZKZZM.js} +13 -13
  81. package/dist/cli/{sessions-FML35Y64.mjs → sessions-AOMOV4HG.mjs} +4 -4
  82. package/dist/cli/{sessions-3P6DOM3H.js → sessions-SLQ2QFLK.js} +5 -5
  83. package/dist/cli/{settings-SD6MDI67.js → settings-2X7H6O4I.js} +74 -74
  84. package/dist/cli/{settings-MWFWIGHW.mjs → settings-L4IFYF5D.mjs} +7 -7
  85. package/dist/cli/{setup-HY35HR26.js → setup-2RY5APQS.js} +41 -41
  86. package/dist/cli/{setup-HDYPYPFU.mjs → setup-CZYQTZ2K.mjs} +6 -6
  87. package/dist/cli/{skills-TGEDPYTP.mjs → skills-D2QQH5S7.mjs} +4 -4
  88. package/dist/cli/skills-RIFYRYWL.js +28 -0
  89. package/dist/cli/tools-M7LTH6ZT.js +40 -0
  90. package/dist/cli/{tools-VBARDH32.mjs → tools-TYIEROJJ.mjs} +6 -6
  91. package/dist/cli/{update-OIDLOWGB.mjs → update-7YCSUWFP.mjs} +6 -6
  92. package/dist/cli/update-HC62SVSQ.js +28 -0
  93. package/dist/cli/{workflow-K2SJUC5Z.js → workflow-42IFLYPN.js} +15 -15
  94. package/dist/cli/{workflow-ACHWLHPT.mjs → workflow-GGRUCLCI.mjs} +5 -5
  95. package/dist/{client-l-IoLh8Z.d.ts → client-DkoxT4wF.d.ts} +1 -1
  96. package/dist/{client-D5XOQKsA.d.mts → client-PDAfbylh.d.mts} +1 -1
  97. package/dist/{compiler-manifest-82q4jtfQ.d.ts → compiler-manifest-CButyeZ8.d.mts} +7 -0
  98. package/dist/{compiler-manifest-82q4jtfQ.d.mts → compiler-manifest-CButyeZ8.d.ts} +7 -0
  99. package/dist/{index-BPbW2XHF.d.mts → index-BMsNUoL5.d.mts} +1 -1
  100. package/dist/{index-k0rquHIm.d.ts → index-LDRzwo09.d.ts} +1 -1
  101. package/dist/index.d.mts +6 -6
  102. package/dist/index.d.ts +6 -6
  103. package/dist/index.js +6 -6
  104. package/dist/index.mjs +3 -3
  105. package/dist/install-integrity.json +80 -80
  106. package/dist/plays/bundle-play-file.d.mts +3 -3
  107. package/dist/plays/bundle-play-file.d.ts +3 -3
  108. package/dist/plays/run-request.d.mts +2 -2
  109. package/dist/plays/run-request.d.ts +2 -2
  110. package/dist/release.d.mts +1 -1
  111. package/dist/release.d.ts +1 -1
  112. package/dist/release.js +2 -2
  113. package/dist/release.mjs +1 -1
  114. package/package.json +1 -1
  115. package/dist/cli/auth-6SVPO5HS.js +0 -31
  116. package/dist/cli/client-BPQKKACG.js +0 -20
  117. package/dist/cli/git-ZZU4LXWX.js +0 -23
  118. package/dist/cli/play-LLC44DQW.js +0 -153
  119. package/dist/cli/search-JWUKJSNY.js +0 -26
  120. package/dist/cli/skills-T3F4A4BQ.js +0 -28
  121. package/dist/cli/tools-PXC5VIRU.js +0 -40
  122. package/dist/cli/update-PIT4RK74.js +0 -28
@@ -16,6 +16,7 @@ export interface PlayRunnerRuntimeDeadlineOptions {
16
16
  }
17
17
 
18
18
  export interface PlayRunnerRuntimeDeadlineHandle {
19
+ drainAtMs: number;
19
20
  /** Claim normal settlement. False means the deadline already won. */
20
21
  claimCompletion: () => boolean;
21
22
  /** Settles only if the deadline callback unexpectedly returns or rejects. */
@@ -60,6 +61,10 @@ export function startPlayRunnerRuntimeDeadline(
60
61
  options: PlayRunnerRuntimeDeadlineOptions,
61
62
  ): PlayRunnerRuntimeDeadlineHandle {
62
63
  const limitMs = Math.max(1, Math.floor(options.limitMs));
64
+ const drainDelayMs = options.onDrain
65
+ ? Math.max(1, limitMs - Math.min(options.drainLeadMs ?? 4_000, limitMs / 2))
66
+ : limitMs;
67
+ const drainAtMs = Date.now() + drainDelayMs;
63
68
  const setTimeoutImpl =
64
69
  options.setTimeoutImpl ??
65
70
  ((callback, delayMs) => setTimeout(callback, delayMs));
@@ -73,20 +78,14 @@ export function startPlayRunnerRuntimeDeadline(
73
78
  settleDeadline = resolve;
74
79
  });
75
80
  const drainHandle = options.onDrain
76
- ? setTimeoutImpl(
77
- () => {
78
- if (fired || completionClaimed) return;
79
- try {
80
- options.onDrain?.();
81
- } catch (error) {
82
- options.onError?.(error);
83
- }
84
- },
85
- Math.max(
86
- 1,
87
- limitMs - Math.min(options.drainLeadMs ?? 4_000, limitMs / 2),
88
- ),
89
- )
81
+ ? setTimeoutImpl(() => {
82
+ if (fired || completionClaimed) return;
83
+ try {
84
+ options.onDrain?.();
85
+ } catch (error) {
86
+ options.onError?.(error);
87
+ }
88
+ }, drainDelayMs)
90
89
  : null;
91
90
  const handle = setTimeoutImpl(() => {
92
91
  if (fired || completionClaimed) return;
@@ -99,6 +98,7 @@ export function startPlayRunnerRuntimeDeadline(
99
98
  }, limitMs);
100
99
 
101
100
  return {
101
+ drainAtMs,
102
102
  claimCompletion: () => {
103
103
  if (fired || completionClaimed) return false;
104
104
  completionClaimed = true;
@@ -736,7 +736,7 @@ export class AbsurdExecutionOperationTimeoutError extends Error {
736
736
  * park/resume seam without a real engine. */
737
737
  export type AbsurdSuspensionContext = Pick<
738
738
  TaskContext,
739
- 'sleepFor' | 'awaitEvent' | 'heartbeat'
739
+ 'sleepFor' | 'sleepUntil' | 'awaitEvent' | 'heartbeat'
740
740
  >;
741
741
 
742
742
  /** Injected DB + app-runtime seams so the handler is testable against real SQL
@@ -1393,10 +1393,17 @@ export async function parkAndResolveSuspension(input: {
1393
1393
 
1394
1394
  if (suspension.kind === 'sleep') {
1395
1395
  // Throws SuspendTask on the first leg; returns once the deadline elapsed.
1396
- await ctx.sleepFor(
1397
- suspension.boundaryId,
1398
- Math.max(0, suspension.delayMs) / 1000,
1399
- );
1396
+ if (suspension.wakeAtMs !== undefined) {
1397
+ await ctx.sleepUntil(
1398
+ suspension.boundaryId,
1399
+ new Date(suspension.wakeAtMs),
1400
+ );
1401
+ } else {
1402
+ await ctx.sleepFor(
1403
+ suspension.boundaryId,
1404
+ Math.max(0, suspension.delayMs) / 1000,
1405
+ );
1406
+ }
1400
1407
  return absurdCheckpointWithSleep(
1401
1408
  checkpoint,
1402
1409
  suspension.boundaryId,
@@ -818,7 +818,12 @@ export function absurdCheckpointWithSleep(
818
818
  ...base,
819
819
  resolvedBoundaries: {
820
820
  ...resolvedBoundaries(base),
821
- [boundaryId]: { kind: 'sleep', delayMs, completedAt: Date.now() },
821
+ [boundaryId]: {
822
+ ...resolvedBoundaries(base)[boundaryId],
823
+ kind: 'sleep',
824
+ delayMs,
825
+ completedAt: Date.now(),
826
+ },
822
827
  },
823
828
  };
824
829
  }
@@ -4,7 +4,10 @@
4
4
  * learn from copies of the same provider feedback. These CTEs are shared by
5
5
  * pure-rate and concurrency-bearing admission, in the same atomic grant SQL.
6
6
  */
7
- import { DEFAULT_UNKNOWN_PROVIDER_FAST_RECOVERY_RPS } from '../governor/rate-state-backend';
7
+ import {
8
+ DEFAULT_UNKNOWN_PROVIDER_FAST_RECOVERY_RPS,
9
+ DEFAULT_UNKNOWN_PROVIDER_MIN_RPS,
10
+ } from '../governor/rate-state-backend';
8
11
 
9
12
  const AIMD_RECOVERY_STEP_MS = 1_000;
10
13
  // Increase relative to the learned rate, not by a fixed RPS amount: a long
@@ -14,15 +17,17 @@ const AIMD_RECOVERY_STEP_MS = 1_000;
14
17
  const AIMD_ADDITIVE_STEP_FRACTION = 0.25;
15
18
  // Keep upward probes small enough to retain headroom after a productive
16
19
  // decrease. Sparse successes use the earlier observation window; sustained
17
- // useful work can establish another small probe each second.
20
+ // useful work at nearly the current target can establish another small probe
21
+ // each second. Demand or historical successes cannot certify the next target.
18
22
  const UNKNOWN_PROVIDER_PROBE_STEP_RPS = 0.1;
19
23
  const UNKNOWN_PROVIDER_PROBE_INTERVAL_CAP_MS = 5_000;
20
- const UNKNOWN_PROVIDER_PRODUCTIVE_RATE_FRACTION = 0.8;
24
+ const UNKNOWN_PROVIDER_PRODUCTIVE_RATE_FRACTION = 0.95;
21
25
  export function acquireRateScopeSql(
22
26
  table: string,
23
27
  successesParameter: string,
24
28
  minimumRateSql = 'NULL::float8',
25
29
  unknownProvider = false,
30
+ probeBaselineSupported = true,
26
31
  ): string {
27
32
  const refillRate = `CASE WHEN ${minimumRateSql} IS NULL
28
33
  THEN h.refill_per_sec
@@ -35,11 +40,29 @@ export function acquireRateScopeSql(
35
40
  // Success counts are already applied once by the feedback writer. Measure
36
41
  // this post-hold epoch, excluding Retry-After and earlier idle history. A
37
42
  // productive second justifies a small new probe; a lone completion does not.
43
+ const probeStartedAtMs = probeBaselineSupported
44
+ ? 'GREATEST(h.rate_probe_started_at_ms, h.rate_observation_started_at_ms)'
45
+ : 'h.rate_observation_started_at_ms';
46
+ const probeSuccesses = probeBaselineSupported
47
+ ? 'GREATEST(0, h.rate_successes - h.rate_probe_successes_at_ramp)'
48
+ : 'h.rate_successes';
49
+ const productiveRateFraction = probeBaselineSupported
50
+ ? UNKNOWN_PROVIDER_PRODUCTIVE_RATE_FRACTION
51
+ : 0.8;
52
+ const storedRecoveryInterval = `LEAST(${UNKNOWN_PROVIDER_PROBE_INTERVAL_CAP_MS}, GREATEST(${AIMD_RECOVERY_STEP_MS}, h.rate_recovery_interval_ms))`;
53
+ const sparseRecoveryInterval = probeBaselineSupported
54
+ ? `CASE WHEN ${refillRate} > ${DEFAULT_UNKNOWN_PROVIDER_FAST_RECOVERY_RPS}
55
+ THEN ${UNKNOWN_PROVIDER_PROBE_INTERVAL_CAP_MS} ELSE ${storedRecoveryInterval} END`
56
+ : storedRecoveryInterval;
57
+ const floorRecoveryInterval = probeBaselineSupported
58
+ ? `GREATEST(${AIMD_RECOVERY_STEP_MS}, h.rate_recovery_interval_ms)`
59
+ : 'h.rate_recovery_interval_ms';
38
60
  const productiveRecovery = `h.rate_observation_started_at_ms IS NOT NULL
39
- AND nv.now_ms - h.rate_observation_started_at_ms >= ${AIMD_RECOVERY_STEP_MS}
40
- AND h.rate_successes * 1000.0
41
- / GREATEST(${AIMD_RECOVERY_STEP_MS}, nv.now_ms - h.rate_observation_started_at_ms)
42
- >= ${refillRate} * ${UNKNOWN_PROVIDER_PRODUCTIVE_RATE_FRACTION}`;
61
+ ${probeBaselineSupported ? 'AND h.rate_probe_started_at_ms >= h.last_ramp_at_ms AND h.rate_probe_started_at_ms >= h.rate_observation_started_at_ms' : ''}
62
+ AND nv.now_ms - ${probeStartedAtMs} >= ${AIMD_RECOVERY_STEP_MS}
63
+ AND ${probeSuccesses} * 1000.0
64
+ / GREATEST(${AIMD_RECOVERY_STEP_MS}, nv.now_ms - ${probeStartedAtMs})
65
+ >= ${refillRate} * ${productiveRateFraction}`;
43
66
  // Below useful seed capacity, a confirmed success can prove the next probe
44
67
  // after one dispatch interval at the current rate. Keeping the floor's 500ms
45
68
  // interval at 4/8 RPS waits through additional clean sends unnecessarily.
@@ -47,9 +70,29 @@ export function acquireRateScopeSql(
47
70
  const backpressuredRecoveryInterval = unknownProvider
48
71
  ? `CASE WHEN ${refillRate} >= ${DEFAULT_UNKNOWN_PROVIDER_FAST_RECOVERY_RPS}
49
72
  THEN CASE WHEN ${productiveRecovery} THEN ${AIMD_RECOVERY_STEP_MS}
50
- ELSE LEAST(${UNKNOWN_PROVIDER_PROBE_INTERVAL_CAP_MS}, GREATEST(${AIMD_RECOVERY_STEP_MS}, h.rate_recovery_interval_ms)) END
51
- ELSE LEAST(h.rate_recovery_interval_ms, CEIL(1000.0 / ${refillRate})) END`
73
+ ELSE ${sparseRecoveryInterval} END
74
+ ELSE LEAST(${floorRecoveryInterval}, CEIL(1000.0 / ${refillRate})) END`
52
75
  : `LEAST(h.rate_recovery_interval_ms, ${AIMD_RECOVERY_STEP_MS})`;
76
+ // A local no-progress pause remains active after the provider's shorter
77
+ // Retry-After. Only time beyond every outstanding dispatch/hold is idle.
78
+ const observationWasIdle = `${successesParameter}::int = 0
79
+ AND nv.now_ms - GREATEST(h.refilled_at_ms, h.next_dispatch_at_ms, h.retry_at_ms, h.cool_until_ms)
80
+ > GREATEST(1000, 2000.0 / ${refillRate})`;
81
+ // A denied refill may fold a completion before the next floor interval is
82
+ // due. Keep that fresh completion available for one later ramp rather than
83
+ // requiring another provider call at the old pace. Its baseline advances on
84
+ // every floor ramp, so time alone cannot reuse it for another increase.
85
+ const recoverySuccess =
86
+ unknownProvider && probeBaselineSupported
87
+ ? `(${successesParameter}::int > 0 OR (
88
+ h.seen_backpressure
89
+ AND ${refillRate} < ${DEFAULT_UNKNOWN_PROVIDER_FAST_RECOVERY_RPS}
90
+ AND h.rate_probe_started_at_ms >= h.last_ramp_at_ms
91
+ AND h.rate_probe_started_at_ms >= h.rate_observation_started_at_ms
92
+ AND h.rate_successes > h.rate_probe_successes_at_ramp
93
+ AND NOT (${observationWasIdle})
94
+ ))`
95
+ : `${successesParameter}::int > 0`;
53
96
  return `provider_hold_rows AS (
54
97
  SELECT * FROM ${table} WHERE bucket_id = $1 FOR UPDATE
55
98
  ), provider_hold AS (
@@ -58,12 +101,8 @@ export function acquireRateScopeSql(
58
101
  GREATEST(CASE WHEN h.seen_backpressure THEN 1 ELSE h.capacity END, ${refillRate}),
59
102
  h.tokens + GREATEST(0, nv.now_ms - h.refilled_at_ms) * ${refillRate} / 1000.0
60
103
  ) AS tokens_after_refill,
61
- ${successesParameter}::int = 0
62
- -- A local no-progress pause remains an active observation episode,
63
- -- even after the provider's shorter Retry-After has elapsed.
64
- AND nv.now_ms - GREATEST(h.refilled_at_ms, h.next_dispatch_at_ms, h.retry_at_ms, h.cool_until_ms)
65
- > GREATEST(1000, 2000.0 / ${refillRate}) AS observation_was_idle,
66
- CASE WHEN ${successesParameter}::int > 0
104
+ ${observationWasIdle} AS observation_was_idle,
105
+ CASE WHEN ${recoverySuccess}
67
106
  AND h.rate_observation_started_at_ms IS NOT NULL
68
107
  AND nv.now_ms - h.last_ramp_at_ms >= CASE WHEN h.seen_backpressure
69
108
  THEN ${backpressuredRecoveryInterval}
@@ -73,7 +112,7 @@ export function acquireRateScopeSql(
73
112
  THEN ${recoveryRate}
74
113
  ELSE ${refillRate} * 2 END)
75
114
  ELSE ${refillRate} END AS next_refill_per_sec,
76
- CASE WHEN ${successesParameter}::int > 0
115
+ CASE WHEN ${recoverySuccess}
77
116
  AND h.rate_observation_started_at_ms IS NOT NULL
78
117
  AND nv.now_ms - h.last_ramp_at_ms >= CASE WHEN h.seen_backpressure
79
118
  THEN ${backpressuredRecoveryInterval}
@@ -87,13 +126,24 @@ export function acquireRateScopeSql(
87
126
  export function updateRateScopeSql(
88
127
  table: string,
89
128
  unknownProvider = false,
129
+ probeBaselineSupported = true,
90
130
  ): string {
91
- // Floor recovery and productive probing measure different rates. Once the
92
- // geometric ramp reaches useful capacity, start a fresh measurement instead
93
- // of diluting its successful throughput with the earlier slow-start period.
131
+ // Keep the throttle epoch's productive evidence while measuring each upward
132
+ // probe from a new counter baseline. Losing the earlier useful work would
133
+ // turn an exploratory rejection into a zero-progress failure.
94
134
  const recoveredObservation = `${unknownProvider} AND ph.seen_backpressure
95
135
  AND ph.refill_per_sec < ${DEFAULT_UNKNOWN_PROVIDER_FAST_RECOVERY_RPS}
96
136
  AND ph.next_refill_per_sec >= ${DEFAULT_UNKNOWN_PROVIDER_FAST_RECOVERY_RPS}`;
137
+ const probedObservation = `${unknownProvider} AND ph.seen_backpressure
138
+ AND ph.refill_per_sec >= ${DEFAULT_UNKNOWN_PROVIDER_FAST_RECOVERY_RPS}
139
+ AND ph.next_refill_per_sec > ph.refill_per_sec`;
140
+ const floorRampedObservation = `${unknownProvider} AND ph.seen_backpressure
141
+ AND ph.refill_per_sec < ${DEFAULT_UNKNOWN_PROVIDER_FAST_RECOVERY_RPS}
142
+ AND ph.next_refill_per_sec > ph.refill_per_sec`;
143
+ const idleFloorObservation = `ph.observation_was_idle
144
+ AND ph.refill_per_sec < ${DEFAULT_UNKNOWN_PROVIDER_FAST_RECOVERY_RPS}`;
145
+ const rebaseProbe =
146
+ 'ph.rate_probe_started_at_ms < ph.last_ramp_at_ms OR ph.rate_probe_started_at_ms < COALESCE(ph.rate_observation_started_at_ms, 0) OR ph.rate_probe_successes_at_ramp > ph.rate_successes';
97
147
  return `updated_scope AS (
98
148
  UPDATE ${table} h SET
99
149
  tokens = ph.tokens_after_refill - (SELECT granted FROM grant_calc),
@@ -112,13 +162,34 @@ export function updateRateScopeSql(
112
162
  AND ph.rate_observation_started_at_ms IS NULL THEN 0
113
163
  WHEN (SELECT granted FROM grant_calc) > 0 AND ph.observation_was_idle THEN 0
114
164
  ELSE ph.rate_successes END,
115
- -- Do not turn an earlier productive measurement window into a failed
116
- -- probe streak when an unknown provider starts a new run after idle.
117
- -- Active Retry-After/local cooldowns are excluded by observation_was_idle.
165
+ ${
166
+ unknownProvider && probeBaselineSupported
167
+ ? `rate_probe_successes_at_ramp = CASE
168
+ WHEN ${recoveredObservation} THEN 0
169
+ WHEN (SELECT granted FROM grant_calc) > 0
170
+ AND (ph.rate_observation_started_at_ms IS NULL OR ph.observation_was_idle) THEN 0
171
+ WHEN ${idleFloorObservation} THEN ph.rate_successes
172
+ WHEN ${floorRampedObservation} OR ${probedObservation} OR (${rebaseProbe}) THEN ph.rate_successes
173
+ ELSE ph.rate_probe_successes_at_ramp END,
174
+ rate_probe_started_at_ms = CASE
175
+ WHEN ${idleFloorObservation} OR ${recoveredObservation} OR ${floorRampedObservation} OR ${probedObservation} OR (${rebaseProbe}) THEN nv.now_ms
176
+ WHEN (SELECT granted FROM grant_calc) > 0
177
+ AND (ph.rate_observation_started_at_ms IS NULL OR ph.observation_was_idle) THEN nv.now_ms
178
+ ELSE ph.rate_probe_started_at_ms END,`
179
+ : ''
180
+ }
181
+ -- At the floor, a new successful epoch must not inherit failed-probe
182
+ -- backoff. Above the floor, retain its useful measurement duration for
183
+ -- sparse upward probes; that rate already proves successful recovery.
118
184
  rate_recovery_interval_ms = CASE
119
185
  WHEN ${unknownProvider} AND (SELECT granted FROM grant_calc) > 0
120
- AND ph.observation_was_idle AND ph.rate_successes > 0
121
- THEN ${AIMD_RECOVERY_STEP_MS}
186
+ AND ph.rate_successes > 0
187
+ AND ph.next_refill_per_sec <= ${DEFAULT_UNKNOWN_PROVIDER_MIN_RPS}
188
+ AND (ph.observation_was_idle
189
+ OR ph.rate_observation_started_at_ms IS NULL)
190
+ -- Zero is an unmeasured new episode, not a failed-probe interval.
191
+ -- A real failure establishes the interval before it can double.
192
+ THEN ${probeBaselineSupported ? 0 : AIMD_RECOVERY_STEP_MS}
122
193
  ELSE ph.rate_recovery_interval_ms END,
123
194
  rate_throttle_ceiling_rps = CASE
124
195
  WHEN (SELECT granted FROM grant_calc) > 0
@@ -288,6 +288,36 @@ function isMissingRateThrottleCeilingColumnError(error: unknown): boolean {
288
288
  );
289
289
  }
290
290
 
291
+ /** Run the existing rate operation until the deploy-owned probe expansion lands. */
292
+ function isMissingProbeBaselineColumnError(error: unknown): boolean {
293
+ const candidate = error as { code?: unknown; message?: unknown } | null;
294
+ return (
295
+ candidate?.code === '42703' &&
296
+ typeof candidate.message === 'string' &&
297
+ /\b(rate_probe_successes_at_ramp|rate_probe_started_at_ms)\b/.test(
298
+ candidate.message,
299
+ )
300
+ );
301
+ }
302
+
303
+ async function queryRateStateWithProbeBaseline<
304
+ Row extends Record<string, unknown>,
305
+ >(
306
+ client: PostgresSchedulerQueryClient,
307
+ sql: (probeBaselineSupported: boolean) => string,
308
+ parameters: unknown[],
309
+ ) {
310
+ try {
311
+ return await client.query<Row>(sql(true), parameters);
312
+ } catch (error) {
313
+ if (!isMissingProbeBaselineColumnError(error)) throw error;
314
+ // PostgreSQL rejects the statement before any grant or reservation writes.
315
+ // Retry only that statement; previously accepted success feedback is not
316
+ // applied twice. No DDL, migration preflight, or global schema floor.
317
+ return await client.query<Row>(sql(false), parameters);
318
+ }
319
+ }
320
+
291
321
  /**
292
322
  * v43 added `rate_requesters` and the lease `requester_id` column. Only an
293
323
  * acquire that carries a requester identity touches them, so during the
@@ -565,6 +595,8 @@ export async function ensurePostgresRateStateSchema(
565
595
  ADD COLUMN IF NOT EXISTS last_ramp_at_ms bigint NOT NULL DEFAULT 0,
566
596
  ADD COLUMN IF NOT EXISTS rate_observation_started_at_ms bigint,
567
597
  ADD COLUMN IF NOT EXISTS rate_successes bigint NOT NULL DEFAULT 0,
598
+ ADD COLUMN IF NOT EXISTS rate_probe_successes_at_ramp bigint NOT NULL DEFAULT 0,
599
+ ADD COLUMN IF NOT EXISTS rate_probe_started_at_ms bigint NOT NULL DEFAULT 0,
568
600
  ADD COLUMN IF NOT EXISTS rate_recovery_interval_ms bigint NOT NULL DEFAULT 1000,
569
601
  ADD COLUMN IF NOT EXISTS rate_throttle_ceiling_rps float8
570
602
  `);
@@ -916,6 +948,8 @@ type InternalAcquireOutcome = PlayRunnerRateStateAcquireResult & {
916
948
  observedRuleRows: number;
917
949
  /** True when a seed upsert would still change a stored row (config drift). */
918
950
  configDrift: boolean;
951
+ /** Local monotonic receipt time; never crosses the runtime boundary. */
952
+ serverClockReceivedAtMs?: number;
919
953
  };
920
954
 
921
955
  function acquireResultOf(
@@ -924,9 +958,17 @@ function acquireResultOf(
924
958
  const result: PlayRunnerRateStateAcquireResult & {
925
959
  observedRuleRows?: number;
926
960
  configDrift?: boolean;
961
+ serverClockReceivedAtMs?: number;
927
962
  } = { ...outcome };
928
963
  delete result.observedRuleRows;
929
964
  delete result.configDrift;
965
+ delete result.serverClockReceivedAtMs;
966
+ if (outcome.serverClockReceivedAtMs != null) {
967
+ result.serverClockElapsedMs = Math.max(
968
+ 0,
969
+ Math.floor(performance.now() - outcome.serverClockReceivedAtMs),
970
+ );
971
+ }
930
972
  return result;
931
973
  }
932
974
 
@@ -1307,7 +1349,7 @@ async function acquirePureRate(
1307
1349
  concurrency: null,
1308
1350
  })
1309
1351
  : null;
1310
- const result = await client.query<{
1352
+ const result = await queryRateStateWithProbeBaseline<{
1311
1353
  granted: string | number;
1312
1354
  wait_ms: string | number;
1313
1355
  observed_rule_rows: string | number;
@@ -1319,7 +1361,8 @@ async function acquirePureRate(
1319
1361
  reservation_ids: string[] | null;
1320
1362
  effective_rps: string | number;
1321
1363
  }>(
1322
- `
1364
+ client,
1365
+ (probeBaselineSupported) => `
1323
1366
  WITH now_value AS (
1324
1367
  SELECT COALESCE(
1325
1368
  $3::bigint,
@@ -1346,6 +1389,7 @@ async function acquirePureRate(
1346
1389
  '$5',
1347
1390
  minimumRateSqlForRules(rules),
1348
1391
  unknownProvider,
1392
+ probeBaselineSupported,
1349
1393
  )},
1350
1394
  reclaim_reservations AS (
1351
1395
  DELETE FROM ${reservationTableName(input)}
@@ -1500,7 +1544,7 @@ async function acquirePureRate(
1500
1544
  GREATEST(1, FLOOR(MIN(next_refill_per_sec) / 100.0))::bigint AS burst_size
1501
1545
  FROM refreshed
1502
1546
  ),
1503
- ${updateRateScopeSql(scopeHoldTableName(input), unknownProvider)},
1547
+ ${updateRateScopeSql(scopeHoldTableName(input), unknownProvider, probeBaselineSupported)},
1504
1548
  updated AS (
1505
1549
  UPDATE ${bucketTableName(input)} b
1506
1550
  SET
@@ -1669,6 +1713,8 @@ async function acquirePureRate(
1669
1713
  ...(requesterId ? [requesterId] : []),
1670
1714
  ],
1671
1715
  );
1716
+ const serverClockReceivedAtMs =
1717
+ input.nowMs == null ? performance.now() : undefined;
1672
1718
  const row = result.rows[0];
1673
1719
  if (!row) {
1674
1720
  // Seeding raced away or rows vanished; the caller re-seeds and retries.
@@ -1709,6 +1755,7 @@ async function acquirePureRate(
1709
1755
  granted,
1710
1756
  waitMs,
1711
1757
  serverNowMs,
1758
+ serverClockReceivedAtMs,
1712
1759
  observedRuleRows,
1713
1760
  configDrift,
1714
1761
  leaseIds: [],
@@ -1741,29 +1788,39 @@ async function acquireWithConcurrency(
1741
1788
  seeds: RateBucketSeed[],
1742
1789
  requested: number,
1743
1790
  ): Promise<InternalAcquireOutcome> {
1744
- await client.query('BEGIN ISOLATION LEVEL READ COMMITTED');
1745
- try {
1746
- await client.query(
1747
- `SELECT bucket_id FROM ${scopeHoldTableName(input)}
1748
- WHERE bucket_id = $1 FOR UPDATE`,
1749
- [input.bucketId],
1750
- );
1751
- const outcome = await grantWithConcurrency(
1752
- client,
1753
- input,
1754
- rules,
1755
- seeds,
1756
- requested,
1757
- );
1758
- await client.query('COMMIT');
1759
- return outcome;
1760
- } catch (error) {
1791
+ let probeBaselineSupported = true;
1792
+ while (true) {
1793
+ await client.query('BEGIN ISOLATION LEVEL READ COMMITTED');
1761
1794
  try {
1762
- await client.query('ROLLBACK');
1763
- } catch {
1764
- // Preserve the original admission/storage error.
1795
+ await client.query(
1796
+ `SELECT bucket_id FROM ${scopeHoldTableName(input)}
1797
+ WHERE bucket_id = $1 FOR UPDATE`,
1798
+ [input.bucketId],
1799
+ );
1800
+ const outcome = await grantWithConcurrency(
1801
+ client,
1802
+ input,
1803
+ rules,
1804
+ seeds,
1805
+ requested,
1806
+ probeBaselineSupported,
1807
+ );
1808
+ await client.query('COMMIT');
1809
+ return outcome;
1810
+ } catch (error) {
1811
+ try {
1812
+ await client.query('ROLLBACK');
1813
+ } catch {
1814
+ // Preserve the original admission/storage error.
1815
+ }
1816
+ if (probeBaselineSupported && isMissingProbeBaselineColumnError(error)) {
1817
+ // The failed grant aborts its transaction. Reacquire the same scope lock
1818
+ // with a fresh snapshot; only the grant is repeated, never its feedback.
1819
+ probeBaselineSupported = false;
1820
+ continue;
1821
+ }
1822
+ throw error;
1765
1823
  }
1766
- throw error;
1767
1824
  }
1768
1825
  }
1769
1826
 
@@ -1773,6 +1830,7 @@ async function grantWithConcurrency(
1773
1830
  rules: AcquirePacingRule[],
1774
1831
  seeds: RateBucketSeed[],
1775
1832
  requested: number,
1833
+ probeBaselineSupported: boolean,
1776
1834
  ): Promise<InternalAcquireOutcome> {
1777
1835
  const ruleIds = rules.map((rule) => rule.ruleId);
1778
1836
  const unknownProvider = rules.some(
@@ -1830,6 +1888,7 @@ async function grantWithConcurrency(
1830
1888
  '$10',
1831
1889
  minimumRateSqlForRules(rules),
1832
1890
  unknownProvider,
1891
+ probeBaselineSupported,
1833
1892
  )},
1834
1893
  reclaim_reservations AS (
1835
1894
  DELETE FROM ${reservationTableName(input)}
@@ -2068,7 +2127,7 @@ async function grantWithConcurrency(
2068
2127
  GREATEST(1, FLOOR(MIN(next_refill_per_sec) / 100.0))::bigint AS burst_size
2069
2128
  FROM refreshed
2070
2129
  ),
2071
- ${updateRateScopeSql(scopeHoldTableName(input), unknownProvider)},
2130
+ ${updateRateScopeSql(scopeHoldTableName(input), unknownProvider, probeBaselineSupported)},
2072
2131
  updated AS (
2073
2132
  UPDATE ${bucketTableName(input)} b
2074
2133
  SET
@@ -2283,6 +2342,8 @@ async function grantWithConcurrency(
2283
2342
  ...(requesterId ? [requesterId] : []),
2284
2343
  ],
2285
2344
  );
2345
+ const serverClockReceivedAtMs =
2346
+ input.nowMs == null ? performance.now() : undefined;
2286
2347
  const row = result.rows[0];
2287
2348
  if (!row) {
2288
2349
  return {
@@ -2327,6 +2388,7 @@ async function grantWithConcurrency(
2327
2388
  granted,
2328
2389
  waitMs,
2329
2390
  serverNowMs,
2391
+ serverClockReceivedAtMs,
2330
2392
  observedRuleRows,
2331
2393
  configDrift,
2332
2394
  leaseIds: granted > 0 ? leaseIds : [],
@@ -2410,9 +2472,15 @@ export async function validatePostgresRateStatePermit(
2410
2472
  */
2411
2473
  export async function claimPostgresProviderDispatch(
2412
2474
  client: PostgresSchedulerQueryClient,
2413
- input: PlayRunnerRateStateValidateInput & { nowMs?: number },
2475
+ input: PlayRunnerRateStateValidateInput & {
2476
+ nowMs?: number;
2477
+ retryAtMsAtGrant?: number;
2478
+ },
2414
2479
  ): Promise<
2415
- PlayRunnerRateStateValidateResult & { physicalCadenceWait: boolean }
2480
+ PlayRunnerRateStateValidateResult & {
2481
+ physicalCadenceWait: boolean;
2482
+ stalePermit?: boolean;
2483
+ }
2416
2484
  > {
2417
2485
  return withRateStateAvailabilityGuard(input, async () => {
2418
2486
  const result = await client.query<{
@@ -2420,6 +2488,7 @@ export async function claimPostgresProviderDispatch(
2420
2488
  retry_at_ms: string | number;
2421
2489
  allowed: boolean;
2422
2490
  cadence_wait: boolean;
2491
+ stale_permit: boolean;
2423
2492
  }>(
2424
2493
  `
2425
2494
  WITH now_value AS (
@@ -2438,6 +2507,7 @@ export async function claimPostgresProviderDispatch(
2438
2507
  GREATEST(COALESCE(s.retry_at_ms, 0), COALESCE(s.cool_until_ms, 0)) AS hold_until_ms,
2439
2508
  s.physical_rps,
2440
2509
  COALESCE(s.seen_backpressure, false) AS preserve_cadence_phase,
2510
+ COALESCE(s.retry_at_ms > $3::bigint, false) AS stale_permit,
2441
2511
  GREATEST(1, FLOOR(s.physical_rps / 100))::int AS burst_size,
2442
2512
  GREATEST(1, CEIL(1000.0 / s.physical_rps))::bigint AS spacing_ms
2443
2513
  FROM now_value nv LEFT JOIN scope_rows s ON true
@@ -2453,6 +2523,7 @@ export async function claimPostgresProviderDispatch(
2453
2523
  c.now_ms + c.spacing_ms * c.burst_size, 0
2454
2524
  FROM cadence c
2455
2525
  WHERE c.hold_until_ms <= c.now_ms AND c.physical_rps > 0
2526
+ AND NOT c.stale_permit
2456
2527
  ON CONFLICT (bucket_id, rule_id) DO UPDATE SET
2457
2528
  -- Always return the locked current cursor, including a denial.
2458
2529
  -- A snapshot read after a blocked upsert could miss a concurrent
@@ -2466,9 +2537,16 @@ export async function claimPostgresProviderDispatch(
2466
2537
  cool_until_ms = CASE
2467
2538
  WHEN existing.next_dispatch_at_ms <= EXCLUDED.refilled_at_ms
2468
2539
  OR LEAST(existing.tokens, EXCLUDED.capacity) >= 1 THEN 0
2469
- ELSE LEAST(existing.next_dispatch_at_ms, EXCLUDED.next_dispatch_at_ms) END,
2470
- -- Persist debt decay at the new pace so repeated probes converge
2471
- -- to this deadline instead of moving it forward on every denial.
2540
+ ELSE CASE WHEN EXCLUDED.refill_per_sec > existing.refill_per_sec
2541
+ THEN LEAST(existing.next_dispatch_at_ms,
2542
+ GREATEST(existing.refilled_at_ms, EXCLUDED.refilled_at_ms)
2543
+ + (EXCLUDED.next_dispatch_at_ms - EXCLUDED.refilled_at_ms))
2544
+ ELSE existing.next_dispatch_at_ms END END,
2545
+ -- Only a faster rate shortens existing debt. A contender can
2546
+ -- sample the clock before waiting behind a newer cursor grant;
2547
+ -- its denial must not rewind that grant at the same pace. Anchor
2548
+ -- recovery at the latest cursor clock, then persist its deadline
2549
+ -- so repeated probes converge without consuming another slot.
2472
2550
  next_dispatch_at_ms = CASE
2473
2551
  WHEN existing.next_dispatch_at_ms <= EXCLUDED.refilled_at_ms
2474
2552
  -- Keep the cadence phase when a request reaches its slot
@@ -2487,23 +2565,30 @@ export async function claimPostgresProviderDispatch(
2487
2565
  -- Cold discovery retains strict spacing until provider
2488
2566
  -- backpressure establishes an adaptive cadence.
2489
2567
  ELSE EXCLUDED.next_dispatch_at_ms END
2490
- ELSE LEAST(existing.next_dispatch_at_ms, EXCLUDED.next_dispatch_at_ms) END,
2568
+ ELSE CASE WHEN EXCLUDED.refill_per_sec > existing.refill_per_sec
2569
+ THEN LEAST(existing.next_dispatch_at_ms,
2570
+ GREATEST(existing.refilled_at_ms, EXCLUDED.refilled_at_ms)
2571
+ + (EXCLUDED.next_dispatch_at_ms - EXCLUDED.refilled_at_ms))
2572
+ ELSE existing.next_dispatch_at_ms END END,
2491
2573
  capacity = EXCLUDED.capacity,
2492
2574
  refill_per_sec = EXCLUDED.refill_per_sec,
2493
2575
  base_refill_per_sec = EXCLUDED.base_refill_per_sec,
2494
2576
  max_rps = EXCLUDED.max_rps,
2495
- refilled_at_ms = EXCLUDED.refilled_at_ms
2577
+ refilled_at_ms = GREATEST(existing.refilled_at_ms, EXCLUDED.refilled_at_ms)
2496
2578
  RETURNING cool_until_ms
2497
2579
  )
2498
2580
  SELECT c.now_ms AS server_now_ms,
2499
- GREATEST(c.hold_until_ms, COALESCE(cursor.cool_until_ms, 0)) AS retry_at_ms,
2500
- c.hold_until_ms <= c.now_ms AND (
2581
+ GREATEST(c.hold_until_ms, COALESCE(cursor.cool_until_ms, 0),
2582
+ CASE WHEN c.stale_permit THEN c.now_ms ELSE 0 END) AS retry_at_ms,
2583
+ NOT c.stale_permit AND c.hold_until_ms <= c.now_ms AND (
2501
2584
  c.physical_rps IS NULL OR cursor.cool_until_ms = 0
2502
2585
  ) AS allowed,
2503
- c.hold_until_ms <= c.now_ms AND COALESCE(cursor.cool_until_ms > c.now_ms, false) AS cadence_wait
2586
+ NOT c.stale_permit AND c.hold_until_ms <= c.now_ms
2587
+ AND COALESCE(cursor.cool_until_ms > c.now_ms, false) AS cadence_wait,
2588
+ c.stale_permit
2504
2589
  FROM cadence c LEFT JOIN cursor ON true
2505
2590
  `,
2506
- [input.bucketId, input.nowMs ?? null],
2591
+ [input.bucketId, input.nowMs ?? null, input.retryAtMsAtGrant ?? null],
2507
2592
  );
2508
2593
  const row = result.rows[0];
2509
2594
  if (!row || typeof row.allowed !== 'boolean') {
@@ -2516,6 +2601,9 @@ export async function claimPostgresProviderDispatch(
2516
2601
  physicalCadenceWait: row.cadence_wait,
2517
2602
  serverNowMs: Number(row.server_now_ms),
2518
2603
  retryAtMs: Number(row.retry_at_ms),
2604
+ ...(input.retryAtMsAtGrant !== undefined
2605
+ ? { stalePermit: row.stale_permit }
2606
+ : {}),
2519
2607
  };
2520
2608
  });
2521
2609
  }
@@ -3362,7 +3450,13 @@ async function penalizeRateStateStatements(
3362
3450
  -- no successes justifies the shared exponential pause.
3363
3451
  WHEN h.rate_successes <= 0
3364
3452
  AND ($5::bigint IS NOT NULL OR h.seen_backpressure)
3365
- AND (NOT h.unknown_provider OR h.seen_backpressure) THEN
3453
+ -- An unknown scope can only recover above its floor after
3454
+ -- successful work. A cleared counter at that new epoch does
3455
+ -- not turn its measurement duration into consecutive failures.
3456
+ AND (NOT h.unknown_provider
3457
+ OR h.refill_per_sec <= ${DEFAULT_UNKNOWN_PROVIDER_MIN_RPS})
3458
+ AND (NOT h.unknown_provider OR (h.seen_backpressure
3459
+ AND h.rate_recovery_interval_ms > 0)) THEN
3366
3460
  CASE WHEN h.seen_backpressure THEN
3367
3461
  LEAST(60000, GREATEST(1000, h.rate_recovery_interval_ms * 2))
3368
3462
  ELSE 1000 END
@@ -3392,6 +3486,7 @@ async function penalizeRateStateStatements(
3392
3486
  SELECT e.*,
3393
3487
  CASE WHEN e.new_episode THEN GREATEST(
3394
3488
  CASE WHEN e.seen_backpressure AND e.rate_successes = 0
3489
+ AND e.no_progress_probe_interval_ms IS NOT NULL
3395
3490
  THEN ${noProgressProbeRateRps}
3396
3491
  ELSE CASE WHEN e.unknown_provider
3397
3492
  THEN ${DEFAULT_UNKNOWN_PROVIDER_MIN_RPS} ELSE ${minimumRateRps} END END,