Today Platform Web — Dev Docs
Workflow

Merge queue

How GitHub's merge queue interacts with `gh pr merge` on this repo. The `UNSTABLE` vs `CLEAN` vs `BLOCKED` states, what cancels auto-merge, and the speculative-branch double-run.

The dev branch has a merge queue. main does not. This affects every PR that targets dev — that's basically every PR.

gh pr merge flags

CommandBehavior
gh pr merge <n>Queue the PR right now. Rejects if required checks aren't green.
gh pr merge <n> --autoEnable auto-merge. PR enters the queue once required checks pass.
gh pr merge <n> --rebaseRejected: "merge strategy set by merge queue"
gh pr merge <n> --squashRejected: same
gh pr merge <n> --disable-autoCancel auto-merge.

The strategy is configured on the queue itself (squash with merge-commit metadata, currently). Trying to override per-PR fails.

State semantics

gh pr view <n> --json mergeStateStatus returns one of:

StateMeaning
CLEANRequired checks green, ready to merge.
UNSTABLERequired green, optional checks (Vercel deploy, Cursor Bugbot) failing or pending. The queue still accepts.
BLOCKEDRequired check failing or not yet run.

UNSTABLE is fine — most of this repo's PRs sit in UNSTABLE during normal operation because Vercel deploy and Bugbot are not required. BLOCKED means fix something.

The speculative-branch double-run

When a PR enters the queue, GitHub creates a synthetic branch:

gh-readonly-queue/dev/pr-<n>-<sha>

This branch is "what dev would look like with your PR merged on top." The full CI pipeline runs a second time against it. Only when that speculative run passes does GitHub fast-forward dev to include your PR.

This roughly doubles wall-clock time to merge. Don't be surprised when a PR sits in "queued position 1" for 25-30 minutes after CI on the PR itself went green — that's the speculative run.

Pushing to a queued PR

Pushing a new commit cancels auto-merge. The autoMergeRequest field on the PR clears. Re-enable it:

git push                          # fixup commit lands
gh pr merge <n> --auto            # re-queue

This catches teams off-guard regularly. If you're amending in response to a reviewer comment, expect to re-run gh pr merge --auto after the amend.

Required vs optional checks

Required checks (block merge):

  • Lint, Format, Typecheck, Unit Tests, Build
  • Visual Regression (when affected scope includes apps/web)
  • CI Success (aggregator that depends on all the above)

Optional checks (informational, don't block):

  • Deploy Web to Vercel, Deploy Admin to Vercel, Deploy Docs to Vercel, Deploy Dev Docs to Vercel
  • Local smoke (web), Smoke (admin), Smoke (web)
  • Cursor Bugbot
  • CodeQL, Analyze (*) — security scans, eventual blocker but not today

Vercel deploys fail in pre-deploy scenarios where the Vercel project isn't provisioned yet (e.g. when VERCEL_PROJECT_ID_DEV_DOCS isn't set). The PR is still mergeable; the deploy turns green on a follow-up commit after the project is wired.

Bypassing the queue (don't, but)

There is no override for the queue under normal circumstances. The only way to bypass it is git push origin pr-x/topic:dev --force from an admin account, which is destructive (rewrites the queue's expectations of dev) and audited. Never do this.

If a hot-fix to dev is genuinely urgent (a bad PR landed and broke prod), the procedure is:

  1. Open a revert PR targeting dev
  2. Mark it [REVERT] in the title
  3. gh pr merge --auto like any other — the queue will accept it on a clean CI run

The revert ships through the queue in 5-30 minutes, which is the worst-case SLA. If that's too slow, the right escalation is to ask GitHub Support to pause the queue.

Common queue states

SymptomDiagnosis
PR sits at "queued position 1" foreverSpeculative branch CI running. Normal. Check gh-readonly-queue/dev/pr-* runs.
gh pr merge returns "merge strategy set by merge queue"You passed --rebase or --squash. Drop the flag.
Auto-merge silently disappearedSomeone (you?) pushed a commit. Re-run gh pr merge <n> --auto.
PR shows BLOCKED when required checks look greenStale check entry. Re-running often clears it. If not, check for missing required check (e.g. a renamed job).
Queue rejects a PR with "required status check is expected"A new required check was added but hasn't run on this PR yet. Push an empty commit (git commit --allow-empty -m chore: trigger ci) to retrigger.

On this page