ci(cmux-next): feat-cmux-next pull requests run ci-guards.yml's groups routed by path

Merged
TE
16d ago
+424-129

Summary

ci.yml routes ci-guards.yml's guard groups by path only for pull requests into main, and cmux-next-guards.yml ran only the ci and release-notary groups. About 70 guard tests never ran for feat-cmux-next and sat in tests/pr-lane-exemptions.json. This PR has feat-cmux-next pull requests and pushes run those groups, routed by path and judged against the base.

  • Routing (one table): detect_linux_guard_changes.classify_feat picks the groups, plus the SwiftPM lockfile job and the source-lint job, from the ownership ci.yml already uses (workflow_guard_groups.groups_for_path, and classify for the two jobs). It adds no second path table.
  • Diff base: cmux-next guards / cmux-next CI guard group checks out with fetch-depth: 2. A pull request diffs its merge commit against HEAD^1. A push diffs against github.event.before, fetched at depth 1. With no usable base (a new branch, or a zero or unreadable sha), every routed group runs.
  • Unknown paths (bounded): ci.yml runs every group for any unknown path. On feat-cmux-next that matched 254 of the last 300 first-parent commits (mostly Packages/, webviews/, cmux-tui/), so it would mean all ten groups on both head and base for almost every PR. On feat, an unknown path in a product source area (FEAT_PRODUCT_AREAS: Packages, webviews, cmux-tui, Native, backend, web, schemas, release-notes, plans, docs, design, skills, ...) adds no group. Any other unknown path (workflows, scripts, tests, Resources, ios, agent-chat, a new top-level area) runs every routed group. Replayed on the same 300 commits, this rule runs everything for 42.
  • Running them: run_ci_guards.py --feat-routes FILE (which also runs a group in order when a step calls init-submodule.sh) runs the ci group plus the routed groups. The job runs this on the head and on the base checkout (--root .guard-base) with the same file, and guards_new_reds.py fails only on steps that this change turns red. It also sets up bun 1.3.6 (ci-guards.yml's pin), which preflight-agent-chat needs. The timeout goes from 10 to 25 minutes (the last run took 8 minutes). The job also sets up python3.9 beside python3, excludes .guard-base from the head's git status, and takes its base sha from the diff step, so the routing diff, the base checkout and CMUX_GUARDS_BASE_SHA all use the same commit.
  • PR lanes: pr_test_lanes.py reads --feat-routes as "any group classify_feat can pick", so the validator counts those tests and their exemption entries are removed. release-ios stays exempt, with a rewritten reason.
  • Two release-ios steps guard tooling that feat-cmux-next uses: the iOS package conventions diff (test-ios.yml and cmux-next.yml run that lint on feat) and test-ios.yml's dispatch-ref test. Both move to preflight, and the conventions lint's inputs get PATH_OWNERS entries.

Changelog

none

Testing

Focused commands (CI only, never run locally): python3 tests/test_pr_lane_coverage.py, python3 tests/test_ci_linux_guard_routing.py, python3 tests/test_ci_run_guards.py, python3 tests/test_feat_ci_guard_group.py, and python3 scripts/ci/validate_test_execution_registry.py --pr-lanes feat-cmux-next --base-sha "$(git rev-parse HEAD^1)".

Each fix comes after its red test, in a separate commit:

On base, cmux-next guards / tooling-tests passed because nothing routed these groups and the tests above did not exist.

On head aaacb37d3505, run 39638306797:

  • cmux-next CI guard group is green: 235 steps over 11 groups, "0 guard steps red on this change, 8 red on the base too, 2 fixed".
  • tooling-tests is green, including the registry check and test_pr_lane_coverage.py.

Observed / Not run

Observed:

  • The CI results above. I read the logs of all three runs of the guard group (39629372978, 39634548627 and 39638306797).
  • Locally, static checks only: the registry validator, run_ci_guards.py --list --feat-routes, YAML parsing and the prepush gate.

Not run: no test file ran locally (the rule on cosmic-gray). The base-red fixes in #23551, #23553, #23554 and #23576 have not run either, because their own pull requests' CI does not route these groups until this PR lands.

ci-guards groups on feat-cmux-next

Group / jobOn feat PRsWhy
cialways (already)guard group job default set
release-notaryalways (already)nightly-guards job; nightly-next ships from feat
preflightroutedshared CI tooling (registry validator, merge drivers, localization, build graph, review fabric, pr-media), and now the iOS conventions diff and test-ios.yml dispatch ref
preflight-agent-chatroutedagent-chat and control-plane types are in the feat tree
app-host-executionroutedshared Swift test-execution scripts (run-swift-testing-suites.sh, require_swift_test_execution.py, package-test-lane.sh)
app-host-watchdogroutedshared watchdog and timeout helpers
app-host-cacheroutedshared reload and Xcode cache tooling
release-toolingroutednot main-only: guards install-zig-ci.sh, install-cmux-tui-client.sh, prune_nightly_release_assets.py and the GhosttyKit checksums, which cmux-next.yml, nightly(-next) and cmux-tui workflows run and feat edits (2, 11, 2 and 4 feat-only commits)
quality-sharding, quality-runtimeroutedcheap; quality-runtime has no steps
quality-determinismroutedPython syntax, shell-integration and determinism lints on tests
workflow-guard-historyroutedfeat edits Package.resolved
workflow-guard-source-lintsroutedDispatchWorkItem lint over Packages/*/Sources
release-iosexemptguards only main's iOS upload path: ios-testflight.yml and ios-appstore-upload.yml upload only from refs/heads/main; the ios/scripts upload tooling (0 feat-only commits), ios_upload_batch_decision.py and fastlane run in no feat workflow. ci.yml routes this group on PRs into main.
workflow-guard-cli-scriptsunchanged (not run)shell tests, not in the exemption list

Base reds

These came up once the groups ran. The job counts each one as "red on the base too", so they warn and do not fail.

Step (group)CauseFixed in
Validate Python test execution registry blast radius (preflight)expected tests/test_ui_fuzzer_engine.py, which feat deleted (#15693)this PR (6e6abec09e5a)
Validate retried Ghostty Zig package fetches (release-tooling)showed up after release-tooling started running in order: the Zig installer test wrote its fake zig into the job's GITHUB_ENVthis PR (873f13c13aca)
Validate Ghostty Zig version synchronization (release-tooling)before ordering: ghostty not checked out yet (this PR). Then: the checker missed init-submodule.sh ghostty-next and could not parse a nightly.yml step#23576
Validate nightly prune Python compatibility (release-tooling)no python3.9 in this job (runner fidelity)this PR (6e6abec09e5a)
Run canonical CMUX CI guard profile (ci)the untracked .guard-base made the head look dirty (red on every run of this job). Underneath: fleet-test-compile.yml uses GitHub-hosted runnersthis PR (aaacb37d3505), then #23553
Validate macOS runner guards (preflight)fleet-test-compile.yml (#23423) uses GitHub-hosted ubuntu-24.04#23553
Validate reload shared DerivedData default (preflight)write_dev_cli_shim moved to scripts/lib/cmux-dev-shim-install#23553
Test the cmux NEXT media tour's app window capture (preflight)display_capture() calls use_display() without the pid that #22046 added#23553
Validate test determinism gate (quality-determinism)137 findings of feat test debt that nothing had checked; allowlisted as a ratchet#23551
Validate Swift Testing suite timeout guard (app-host-execution)the test's fake swift predates the binary-module guard's swift package resolve (e6c6b4a2a506, a9d9b47dc978)#23554
Validate signing secrets are read only in protected environments (ci, already routed)nightly-next-train.yml#23299 (another lane's, open)
Validate iOS package conventions for this change (preflight)run 2 judged against a stale pull_request.base.sha (ChiefWorkspaceName.swift came from #23391 on base). Not a base red: a base-selection bugthis PR (873f13c13aca)

Agent session: cosmic-gray

🤖 Generated with Claude Code