Skip to content

ci(merge-queue): CircleCI-scheduled reconcile heartbeat, surviving Actions outages - #10576

Open
davidfirst wants to merge 2 commits into
masterfrom
merge-queue-circleci-heartbeat
Open

ci(merge-queue): CircleCI-scheduled reconcile heartbeat, surviving Actions outages#10576
davidfirst wants to merge 2 commits into
masterfrom
merge-queue-circleci-heartbeat

Conversation

@davidfirst

@davidfirst davidfirst commented Aug 6, 2026

Copy link
Copy Markdown
Member

Today's GitHub Actions outage froze the merge queue completely: every reconcile failed before reaching the script (runners couldn't even download actions/checkout), leaving green queued PRs unmergeable for everyone without admin bypass. CircleCI kept working throughout — it's independent infrastructure.

  • Adds a merge_queue_heartbeat scheduled workflow (every 10 minutes on master, same config-level cron pattern as nightly) that runs the identical reconcile script from CircleCI. The script is stateless and idempotent, so overlapping runs with the Actions-side reconciles are harmless. This also replaces GitHub's throttled Actions cron as the dependable time-based fallback (20-40m silent gaps observed).
  • No token setup needed: the project is public, so the script's CircleCI reads work unauthenticated; CIRCLE_TOKEN is now optional everywhere (used when present, e.g. to avoid shared-IP rate limits on hosted runners). A failed CircleCI read still fails the run — the queue never treats "couldn't check bit_merge" as settled.
  • The queue now ignores its own check runs (reconcile, ping) when judging a PR green: the outage failed those runs on PR head commits, which then blocked the PRs as "checks failing" — infrastructure artifacts, not PR verdicts.
  • Break-glass documented: GITHUB_TOKEN=<pat> node .github/scripts/merge-queue.js from any machine (used today to unstick the queue mid-outage).

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add CircleCI merge-queue heartbeat to survive GitHub Actions outages

✨ Enhancement ⚙️ Configuration changes 🕐 10-20 Minutes

Grey Divider

AI Description

• Add a CircleCI scheduled heartbeat to run the merge-queue reconcile every 10 minutes.
• Fail fast in CircleCI when CIRCLE_TOKEN is missing, with clear setup guidance.
• Document redundancy and break-glass local execution when Actions cron/outages stall merges.
Diagram

graph TD
  A["GitHub Actions\nmerge-queue"] --> S["merge-queue.js\nreconcile"] --> GH[("GitHub API")]
  C["CircleCI\nmerge_queue_heartbeat"] --> S
  S --> CC[("CircleCI API")]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. External cron runner (self-hosted or serverless)
  • ➕ Remains available even if both GitHub Actions and CircleCI are degraded
  • ➕ Can be isolated with dedicated credentials and tighter network controls
  • ➖ Adds a new operational surface area (hosting, secrets, monitoring)
  • ➖ More work than reusing existing CI infrastructure
2. Self-hosted GitHub Actions runner for the cron path
  • ➕ Keeps all orchestration within GitHub Actions semantics
  • ➕ Avoids duplicating CI scheduling logic across providers
  • ➖ Still depends on GitHub Actions control plane and action distribution (e.g., actions/checkout outages can still block)
  • ➖ Requires provisioning/maintaining runner infrastructure
3. Make CircleCI bit_merge always trigger reconcile (no periodic schedule)
  • ➕ No additional schedules; reconcile happens at a natural queue boundary (end of bit_merge)
  • ➕ Keeps dependency on CircleCI API token already needed by the script
  • ➖ Does not cover non-bit_merge stalls (missed events, cron throttling gaps, other queue state changes)
  • ➖ Still needs a periodic heartbeat to guarantee progress

Recommendation: The PR’s approach (a lightweight CircleCI scheduled heartbeat running the same idempotent script) is the best cost/benefit: it reuses existing infrastructure, materially improves resilience during GitHub Actions outages/throttling, and avoids introducing a new always-on service. The only notable tradeoff is the one-time need to provision CIRCLE_TOKEN in CircleCI; the fast-fail messaging mitigates this setup risk.

Files changed (3) +45 / -2

Documentation (2) +9 / -2
merge-queue.jsDocument CircleCI heartbeat and break-glass local execution +5/-1

Document CircleCI heartbeat and break-glass local execution

• Expands the header comment to describe the three execution paths (Actions, CircleCI heartbeat, and local) and how to run the script manually with required tokens.

.github/scripts/merge-queue.js

merge-queue.ymlDocument CircleCI-based scheduling redundancy for merge-queue reconcile +4/-1

Document CircleCI-based scheduling redundancy for merge-queue reconcile

• Updates workflow documentation to clarify that GitHub’s cron is no longer the only time-based safety net and calls out the CircleCI heartbeat as the outage-resistant fallback.

.github/workflows/merge-queue.yml

Other (1) +36 / -0
config.ymlAdd merge-queue reconcile job and 10-minute scheduled heartbeat +36/-0

Add merge-queue reconcile job and 10-minute scheduled heartbeat

• Introduces a CircleCI job (merge_queue_reconcile) that runs the existing .github/scripts/merge-queue.js reconcile loop. Adds a scheduled workflow (merge_queue_heartbeat) on master every 10 minutes and fails early with a clear message when CIRCLE_TOKEN is not configured.

.circleci/config.yml

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 6, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Heartbeat runs can overlap 🐞 Bug ☼ Reliability
Description
The new CircleCI scheduled workflow can start a reconcile every 10 minutes with no serialization, so
a slow/stalled run can overlap another run and race the merge-queue script’s GitHub status updates
and update-branch mutations. GitHub Actions explicitly prevents overlap via a workflow concurrency
group, but the CircleCI heartbeat adds an uncoordinated runner, making last-writer-wins updates and
brief inconsistent gate states possible.
Code

.circleci/config.yml[R1620-1623]

+      - schedule:
+          # CircleCI's config-level cron rejects step syntax (*/10) — list the minutes explicitly
+          cron: '4,14,24,34,44,54 * * * *'
+          filters:
Evidence
CircleCI schedules the reconcile every 10 minutes; GitHub Actions explicitly states it must not
overlap and enforces that with concurrency. The script performs per-PR status POSTs and
branch-update mutations without any cross-run lock, and its fetch calls have no explicit
timeouts—so a slow/stuck run can overlap subsequent schedules and/or a GitHub Actions reconcile.

.circleci/config.yml[1610-1629]
.github/workflows/merge-queue.yml[63-67]
.github/scripts/merge-queue.js[354-374]
.github/scripts/merge-queue.js[414-427]
.github/scripts/merge-queue.js[91-112]
.github/scripts/merge-queue.js[122-130]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new CircleCI schedule runs the merge-queue reconcile every 10 minutes, but there’s no guard against overlapping runs. The reconcile script performs multiple external mutations (posting commit statuses and updating PR branches), so concurrent executions can race and cause transient inconsistent state.
### Issue Context
GitHub Actions’ merge-queue workflow intentionally prevents overlap via `concurrency`, but CircleCI pipelines are not covered by that mechanism. Additionally, the script’s network calls use `fetch` without explicit timeouts, increasing the chance that a run exceeds the 10-minute schedule interval.
### Fix Focus Areas
- Add run-time limiting/serialization in CircleCI:
- .circleci/config.yml[736-745]
- .circleci/config.yml[1618-1629]
- Add explicit request timeouts (reduce hung runs):
- .github/scripts/merge-queue.js[91-112]
- .github/scripts/merge-queue.js[122-130]
### Suggested implementation directions
- In CircleCI, wrap the node invocation with a hard timeout (e.g., `timeout 8m node ...`) and/or add logic to skip if another heartbeat pipeline/workflow is currently running.
- In `merge-queue.js`, use `AbortController` (or equivalent) to enforce reasonable timeouts for GitHub/CircleCI API calls so the job can’t hang indefinitely and drift into overlap.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

2. Unvalidated GitHub token 🐞 Bug ⚙ Maintainability
Description
The new CircleCI merge_queue_reconcile job validates CIRCLE_TOKEN but does not validate
GH_RELEASE_GITHUB_API_TOKEN before using it to set GITHUB_TOKEN, so a missing/empty value fails
with a generic “GITHUB_TOKEN is required” error from the script. This makes the new scheduled
heartbeat harder to diagnose and can leave the queue unreconciled until the misconfiguration is
found.
Code

.circleci/config.yml[R742-745]

+              exit 1
+            fi
+            GITHUB_TOKEN=$GH_RELEASE_GITHUB_API_TOKEN node .github/scripts/merge-queue.js
+
Evidence
The CircleCI job only checks CIRCLE_TOKEN and then derives GITHUB_TOKEN from
GH_RELEASE_GITHUB_API_TOKEN. The script’s entrypoint throws a generic error if GITHUB_TOKEN is
missing, which is what you’d see if GH_RELEASE_GITHUB_API_TOKEN were unset/empty.

.circleci/config.yml[738-745]
.github/scripts/merge-queue.js[529-535]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`merge_queue_reconcile` exits early if `CIRCLE_TOKEN` is missing, but it does not check `GH_RELEASE_GITHUB_API_TOKEN` even though it is required to populate `GITHUB_TOKEN` for the script. When unset, the job fails with the script’s generic `GITHUB_TOKEN is required` error, obscuring which CircleCI project env var needs to be configured.
### Issue Context
The CircleCI job invokes:
`GITHUB_TOKEN=$GH_RELEASE_GITHUB_API_TOKEN node .github/scripts/merge-queue.js`
The script validates `process.env.GITHUB_TOKEN` and throws if missing.
### Fix Focus Areas
- .circleci/config.yml[739-745]
### Suggested implementation directions
- Add an explicit check:
- if `GH_RELEASE_GITHUB_API_TOKEN` is empty, print a clear message naming that variable and exit 1.
- Optionally, prefer `GITHUB_TOKEN` if already set, falling back to `GH_RELEASE_GITHUB_API_TOKEN`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Previous review results

Review updated until commit c62ad96 ⚖️ Balanced

Results up to commit 0ed615a


🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (0)


Remediation recommended
1. Heartbeat runs can overlap 🐞 Bug ☼ Reliability
Description
The new CircleCI scheduled workflow can start a reconcile every 10 minutes with no serialization, so
a slow/stalled run can overlap another run and race the merge-queue script’s GitHub status updates
and update-branch mutations. GitHub Actions explicitly prevents overlap via a workflow concurrency
group, but the CircleCI heartbeat adds an uncoordinated runner, making last-writer-wins updates and
brief inconsistent gate states possible.
Code

.circleci/config.yml[R1620-1623]

+      - schedule:
+          # CircleCI's config-level cron rejects step syntax (*/10) — list the minutes explicitly
+          cron: '4,14,24,34,44,54 * * * *'
+          filters:
Evidence
CircleCI schedules the reconcile every 10 minutes; GitHub Actions explicitly states it must not
overlap and enforces that with concurrency. The script performs per-PR status POSTs and
branch-update mutations without any cross-run lock, and its fetch calls have no explicit
timeouts—so a slow/stuck run can overlap subsequent schedules and/or a GitHub Actions reconcile.

.circleci/config.yml[1610-1629]
.github/workflows/merge-queue.yml[63-67]
.github/scripts/merge-queue.js[354-374]
.github/scripts/merge-queue.js[414-427]
.github/scripts/merge-queue.js[91-112]
.github/scripts/merge-queue.js[122-130]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
A new CircleCI schedule runs the merge-queue reconcile every 10 minutes, but there’s no guard against overlapping runs. The reconcile script performs multiple external mutations (posting commit statuses and updating PR branches), so concurrent executions can race and cause transient inconsistent state.

### Issue Context
GitHub Actions’ merge-queue workflow intentionally prevents overlap via `concurrency`, but CircleCI pipelines are not covered by that mechanism. Additionally, the script’s network calls use `fetch` without explicit timeouts, increasing the chance that a run exceeds the 10-minute schedule interval.

### Fix Focus Areas
- Add run-time limiting/serialization in CircleCI:
 - .circleci/config.yml[736-745]
 - .circleci/config.yml[1618-1629]
- Add explicit request timeouts (reduce hung runs):
 - .github/scripts/merge-queue.js[91-112]
 - .github/scripts/merge-queue.js[122-130]

### Suggested implementation directions
- In CircleCI, wrap the node invocation with a hard timeout (e.g., `timeout 8m node ...`) and/or add logic to skip if another heartbeat pipeline/workflow is currently running.
- In `merge-queue.js`, use `AbortController` (or equivalent) to enforce reasonable timeouts for GitHub/CircleCI API calls so the job can’t hang indefinitely and drift into overlap.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational
2. Unvalidated GitHub token 🐞 Bug ⚙ Maintainability
Description
The new CircleCI merge_queue_reconcile job validates CIRCLE_TOKEN but does not validate
GH_RELEASE_GITHUB_API_TOKEN before using it to set GITHUB_TOKEN, so a missing/empty value fails
with a generic “GITHUB_TOKEN is required” error from the script. This makes the new scheduled
heartbeat harder to diagnose and can leave the queue unreconciled until the misconfiguration is
found.
Code

.circleci/config.yml[R742-745]

+              exit 1
+            fi
+            GITHUB_TOKEN=$GH_RELEASE_GITHUB_API_TOKEN node .github/scripts/merge-queue.js
+
Evidence
The CircleCI job only checks CIRCLE_TOKEN and then derives GITHUB_TOKEN from
GH_RELEASE_GITHUB_API_TOKEN. The script’s entrypoint throws a generic error if GITHUB_TOKEN is
missing, which is what you’d see if GH_RELEASE_GITHUB_API_TOKEN were unset/empty.

.circleci/config.yml[738-745]
.github/scripts/merge-queue.js[529-535]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`merge_queue_reconcile` exits early if `CIRCLE_TOKEN` is missing, but it does not check `GH_RELEASE_GITHUB_API_TOKEN` even though it is required to populate `GITHUB_TOKEN` for the script. When unset, the job fails with the script’s generic `GITHUB_TOKEN is required` error, obscuring which CircleCI project env var needs to be configured.

### Issue Context
The CircleCI job invokes:
`GITHUB_TOKEN=$GH_RELEASE_GITHUB_API_TOKEN node .github/scripts/merge-queue.js`
The script validates `process.env.GITHUB_TOKEN` and throws if missing.

### Fix Focus Areas
- .circleci/config.yml[739-745]

### Suggested implementation directions
- Add an explicit check:
 - if `GH_RELEASE_GITHUB_API_TOKEN` is empty, print a clear message naming that variable and exit 1.
- Optionally, prefer `GITHUB_TOKEN` if already set, falling back to `GH_RELEASE_GITHUB_API_TOKEN`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

Comment thread .circleci/config.yml
Comment on lines +1620 to +1623
- schedule:
# CircleCI's config-level cron rejects step syntax (*/10) — list the minutes explicitly
cron: '4,14,24,34,44,54 * * * *'
filters:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

1. Heartbeat runs can overlap 🐞 Bug ☼ Reliability

The new CircleCI scheduled workflow can start a reconcile every 10 minutes with no serialization, so
a slow/stalled run can overlap another run and race the merge-queue script’s GitHub status updates
and update-branch mutations. GitHub Actions explicitly prevents overlap via a workflow concurrency
group, but the CircleCI heartbeat adds an uncoordinated runner, making last-writer-wins updates and
brief inconsistent gate states possible.
Agent Prompt
### Issue description
A new CircleCI schedule runs the merge-queue reconcile every 10 minutes, but there’s no guard against overlapping runs. The reconcile script performs multiple external mutations (posting commit statuses and updating PR branches), so concurrent executions can race and cause transient inconsistent state.

### Issue Context
GitHub Actions’ merge-queue workflow intentionally prevents overlap via `concurrency`, but CircleCI pipelines are not covered by that mechanism. Additionally, the script’s network calls use `fetch` without explicit timeouts, increasing the chance that a run exceeds the 10-minute schedule interval.

### Fix Focus Areas
- Add run-time limiting/serialization in CircleCI:
  - .circleci/config.yml[736-745]
  - .circleci/config.yml[1618-1629]
- Add explicit request timeouts (reduce hung runs):
  - .github/scripts/merge-queue.js[91-112]
  - .github/scripts/merge-queue.js[122-130]

### Suggested implementation directions
- In CircleCI, wrap the node invocation with a hard timeout (e.g., `timeout 8m node ...`) and/or add logic to skip if another heartbeat pipeline/workflow is currently running.
- In `merge-queue.js`, use `AbortController` (or equivalent) to enforce reasonable timeouts for GitHub/CircleCI API calls so the job can’t hang indefinitely and drift into overlap.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread .circleci/config.yml Outdated
Comment on lines +742 to +745
exit 1
fi
GITHUB_TOKEN=$GH_RELEASE_GITHUB_API_TOKEN node .github/scripts/merge-queue.js

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Informational

2. Unvalidated github token 🐞 Bug ⚙ Maintainability

The new CircleCI merge_queue_reconcile job validates CIRCLE_TOKEN but does not validate
GH_RELEASE_GITHUB_API_TOKEN before using it to set GITHUB_TOKEN, so a missing/empty value fails
with a generic “GITHUB_TOKEN is required” error from the script. This makes the new scheduled
heartbeat harder to diagnose and can leave the queue unreconciled until the misconfiguration is
found.
Agent Prompt
### Issue description
`merge_queue_reconcile` exits early if `CIRCLE_TOKEN` is missing, but it does not check `GH_RELEASE_GITHUB_API_TOKEN` even though it is required to populate `GITHUB_TOKEN` for the script. When unset, the job fails with the script’s generic `GITHUB_TOKEN is required` error, obscuring which CircleCI project env var needs to be configured.

### Issue Context
The CircleCI job invokes:
`GITHUB_TOKEN=$GH_RELEASE_GITHUB_API_TOKEN node .github/scripts/merge-queue.js`
The script validates `process.env.GITHUB_TOKEN` and throws if missing.

### Fix Focus Areas
- .circleci/config.yml[739-745]

### Suggested implementation directions
- Add an explicit check:
  - if `GH_RELEASE_GITHUB_API_TOKEN` is empty, print a clear message naming that variable and exit 1.
- Optionally, prefer `GITHUB_TOKEN` if already set, falling back to `GH_RELEASE_GITHUB_API_TOKEN`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit c62ad96

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant