fix: gstack's GitHub waits poll REST at most once a minute and wait out rate limits (GST-1713) (#4109)

* fix: gstack's GitHub waits poll REST at most once a minute and wait out rate limits (GST-1713)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test: register the gh-poll REST fixtures as free fixtures (GST-1713)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: pre-landing review fixes (GST-1713)

ci-gate --registration-wait re-polls while time is left in the window; a rate-limited gh pr merge fails at once under the land lock; a bot PR author reads without its [bot] suffix; ci-carry wait documents its one-poll overrun.

Review-Finding: p1/specialist-batched#1 reader=testing
Review-Finding: p1/specialist-batched#2 reader=testing
Review-Finding: p1/specialist-batched#3 reader=testing
Review-Finding: p1/red-team#1 reader=red-team
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs: the GST-1713 changelog fragment names the gh pr merge exception

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
Garry Tanandcapy-ai[bot] authored and GitHub committed 2026-11-07 05:51:12 +00:00
1 parent 3742ee9527
commit aaff4741b9
42 files changed
+2044 -139

No files matched your search

+3 -2
View File
@@ -28,6 +28,7 @@ import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { spawnSync } from 'node:child_process';
import { pollDelayMs } from '../lib/gh-poll';
import { detectBaseBranch } from '../test/helpers/touchfiles';
import { DEFAULT_JOBS, ROOT, type PaidTier } from './lib/paid-types';
import { buildRunManifest, loadPaidTestDurations, recordedShardMs } from './lib/paid-plan';
@@ -243,7 +244,7 @@ export function dispatchWorkflow(dispatch: WorkflowDispatch, ref: string, run: E
export async function awaitNonceRun(workflow: string, nonce: string, run: Exec, repo = CANONICAL_REPO): Promise<RunSummary | null> {
const deadline = Date.now() + 10 * pollMs();
while (Date.now() < deadline) {
await sleep(Math.min(pollMs(), 10_000));
await sleep(pollDelayMs(pollMs()));
const found = runForNonce(listDispatchRuns(workflow, run, repo) ?? [], nonce);
if (found) return found;
}
@@ -335,7 +336,7 @@ async function followWorkflow(dispatch: WorkflowDispatch, ref: string, key: stri
console.log(`[eval-bg] ${dispatch.workflow}: conclusion ${state.conclusion ?? 'unknown'} -> exit ${code}`);
return code;
}
await sleep(pollMs());
await sleep(pollDelayMs(pollMs()));
}
}
+1
View File
@@ -235,6 +235,7 @@ export function collectRecovery(options: CollectOptions): { checkpoint: Recovery
const id = String(listedRun.id);
let run = listedRun;
while (run.status !== 'completed' && left() > (options.pollMs ?? 5_000)) {
// gh-poll-exempt: runs in GitHub Actions (evals.yml) on GITHUB_TOKEN, not the shared Capy installation
sleep(options.pollMs ?? 5_000);
const polled = gh(['api', `repos/${options.repo}/actions/runs/${id}`], left());
if (polled.status !== 0) break;
+2
View File
@@ -123,6 +123,7 @@ function gateMain(o: Required<Pick<BackstopOptions, 'repo' | 'branch' | 'sha'>>
run = json<Run>(o.gh(['api', `repos/${o.repo}/actions/runs/${run.id}`], GH_MS)) ?? run;
if (run.status === 'completed') return run;
}
// gh-poll-exempt: runs in GitHub Actions (evals-main-backstop.yml) on GITHUB_TOKEN; default 60 s
o.sleep(o.pollMs);
}
lines.push(`the gate ${run ? `run ${run.id}` : 'dispatch'} did not finish before the backstop deadline`);
@@ -151,6 +152,7 @@ export function runBackstop(o: BackstopOptions): { action: BackstopAction; lines
if (!merging.length) break;
if (now() >= settleUntil) { lines.push(`BACKSTOP: train(s) ${merging.map(t => t.nonce).join(', ')} still merging after the settle window; gating anyway`); break; }
lines.push(`BACKSTOP: waiting for train(s) ${merging.map(t => t.nonce).join(', ')} to finish merging${fetched.ok ? '' : ` (${fetched.detail})`}`);
// gh-poll-exempt: runs in GitHub Actions (evals-main-backstop.yml) on GITHUB_TOKEN; default 60 s
sleep(pollMs);
}
const digest = stampFreeTree(git, o.sha, o.repoRoot, loaded.policy);