## TL;DR

Agents that block on CI usually busy-poll or sleep in a tight loop until their own timeout kills them. Poll the CI status API with exponential backoff and jitter, distinguish "queued" from "running", cap the total wait with a hard budget, and on expiry report "CI still running" instead of failing. That keeps the agent cheap and honest.

## Error

```text
[agent log] waiting for CI... (poll #412, elapsed 28m)
[agent log] timed out waiting for CI after 30m, marking step failed
CI job actually finished green at 31m.
```

## Steps

1. Query the CI status API, not the web UI: `gh run view [RUN_ID] --json status,conclusion` or the equivalent for your provider. Expected: one call returns queued, in_progress, or completed plus the conclusion.
2. Poll with backoff: start at 15s, double each round, cap at 120s, add jitter. Expected: request count stays well under API rate limits even over an hour.
3. Set a hard budget (45 minutes is a sane default); when it expires, stop and report "CI still running at [time], run [RUN_ID]". Expected: the agent exits cleanly with a status summary, never a false failure.
4. Treat "still queued after 10 minutes" as its own signal: flag a possible runner or queue problem instead of waiting silently. Expected: infra stalls get surfaced, not absorbed.
5. On completion, fetch only the failing jobs' logs, never the full log archive. Expected: the agent reads kilobytes of relevant failure output, not megabytes of green logs.

## When to use

- The agent blocks waiting for CI and risks its own timeout.
- CI polling shows up as hundreds of API calls in usage logs.
- The agent marks builds failed because CI was merely slow.

## When not to use

- The CI job itself is slow because tests are slow (fix the suite, not the polling).
- You need real-time log streaming for debugging (use the provider's log stream instead of polling).

## Tool compatibility

- Any coding agent (Claude Code, Cursor, etc.) driving GitHub Actions, GitLab CI, CircleCI, or Buildkite via their APIs or CLIs.
- Works with gh, glab, or plain HTTPS calls using your API key.

## Variant phrasings

### agent keeps asking CI "are we there yet"

The tight poll loop: fix with backoff and a budget so the agent asks less often and gives up gracefully.

### CI check timed out on the agent side

The agent's own timeout fired before CI finished. A budget plus a "still running" report turns this into information instead of a false failure.

### polling burned through the API rate limit

Too many status calls in a short window. Backoff with jitter plus fetching only failing logs fixes both the rate and the volume.

## Why it happens

Agents default to the simplest loop: ask, sleep a fixed short interval, repeat, until a global timeout. That loop has no model of CI states, so it cannot tell "queued behind other jobs" from "running", cannot pace itself, and reports a timeout as a failure when the truth is just "not done yet".

## Edge cases

- Matrix builds: poll the aggregate run status, then drill into individual jobs only when the aggregate completes.
- Rerun jobs reset the clock; re-read the status after any rerun instead of trusting the first completion.
- Required-status branch protection can block merges while the agent waits; have the agent check which checks are required up front so it waits for the right ones.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_yZREhN32iqgpo0KROem2fw
