ci: a label CI does not read no longer starts a full CI run
Leo (2026-10-31): "JUST MERGE" "IT IS BINARY DONE OR NOT DONE".
Problem
ci.yml triggers on pull_request labeled/unlabeled, so every label change, risk:* swaps included, started a second full CI run at the same head. On #29618 the duplicate run 41241282080 sat queued for over an hour behind the first one (label runs share its concurrency group and don't cancel), and while it was pending the webview gate kept waiting. cmux-vm.yml had the same trigger with cancel-in-progress: true, so any label change cancelled and restarted its check.
What changed
ci.yml, for pull requests intofeat-cmux-next: every job butci-statusgets one guard that is false only for a label event whose label is notfull-ciordev-build(the two labels it reads).ci-statusstill runs on those events and passes at once into feat-cmux-next, because a skippedci-statusnewer than a red or pending one would read as passing to a newest-by-name gate likegh-merge-green(review finding at 336d0db). PRs into other bases (main) keep re-running as before, so main's required checks are never superseded by a skip. The concurrency group gets a-label-<run_id>suffix for those events, so the skipped run doesn't wait behind the real run, replace a pending one, or cancel anything. It finishes in seconds with every job skipped.full-cianddev-buildwork as before.scripts/ci/next_batch.py(risk:high, since it touches a merge enforcer):newest_checksnow treats a skipped newest run like a cancelled one and falls back to the newest run that has a verdict. Without this, a label run's skipped jobs (VMcheck,web) would hide an earlier red from the batch queue (review finding at 9d793d0). merge_guard'snewestalready works this way. Red without the fix, green with it:tests/test_next_batch_groups.py::GroupCycle::test_newest_run_edge_cases.cmux-vm.yml: same pattern (feat-cmux-next only), keyed oncmux-vm-breaking-change-ok.- Left alone because they're cheap and read labels on purpose:
risk-tier,merge-freeze,pr-capture-check,pr-perf-section-check,backend-migrations,cmux-next-batch(debounce),cmux-next-mediaandcmux-next-motion-golden(already gated on their label). - The skipped duplicate doesn't hide an earlier verdict: the status rollup still lists the earlier suite's checks (checked on #29618), and merge-guard reads the newest check that has a verdict.
Test
tests/test_ci_delta_since_green.py drops the guard before its small evaluator, which has no !, runs. The new test also runs as a step in ci-guards.yml's ci group.
New tests/test_ci_label_events_skip_full_ci.py (linux-guard lane). Every label-triggered workflow must be either GUARDED (its labels listed; every job a label event could start ends with the guard; its concurrency group isolates other label events) or in REACTS with a reason. It also evaluates the guard: synchronize, opened, push, its own labels and a risk:high label into main give true; risk:high, risk:normal and ready-to-land give false. It fails on the base workflows (4+ job failures) and passes on the head. I updated one existing assertion (linux-preflight) that pinned the old if: text. test_ci_pr_runs_cancel_superseded.py, test_ci_linux_compile_runners.py and test_ci_linux_single_size.py pass, and actionlint output is unchanged.
Observed / Not run
- Observed: the tests above, run locally.
test_ci_workflow_run_sources::test_ci_failfast...andtest_ci_change_areas::test_repository_helpers_route_by_where_they_runfail the same way on base (notestsjob; owned_pool_rescue route). - Not run: a live label event on a PR. That's the first label swap after merge.
Changelog
none
Agent session: agent session
š¤ Generated with Claude Code