ci: refuse classic API changes into main without a stated reason
Summary
Classic cmux (cmux.com and its API, plus the relays) deploys on its own when a commit lands on main. Leo, 2026-11-04: "apparently the api if we change it it'll auto deploy so we wanna make extra sure we dont change it for clasisc".
.github/classic-auto-deploy.jsonis the one list of what auto-deploys classic frommain(Vercelmanaflow/cmuxfor cmux.com, theiroh-v2and Cloudflare relay Workers, plus the deploys that carry no customer API, listed but not guarded) and which paths each one guards. The guarded set is cmux.com's server side:web/app/api,web/app/v1,web/app/vm,web/app/handler,web/app/authorize,web/app/lib,web/services,web/db,web/orpc,web/openapi, build config, and the relay sources. Markdown and test files are excluded.scripts/ci/classic_api_guard.py(run by.github/workflows/classic-api-guard.yml, which takes it from the base commit and reads the head only as a list of changed paths) fails a PR intomainthat changes a guarded path unless its description has a lineClassic API change: <reason>. Intofeat-cmux-nextit only warns, becausefeat-cmux-nextdeploys nothing classic (its merge-base withmainis 2026-10-19 and nothing syncs it intomain). New NEXT Cloud backend code goes to manaflow-ai/cmux-platform.- Editing the description re-runs the check. Light Linux job on the
LINUX_RUNNERfleet route, with forks on Blacksmith 4 vCPU as the other light guards are. Runs onpull_request_target(Leo, 2026-11-04: "Sure let's do that then"), so GitHub takes the workflow frommainand a PR cannot edit the check into a no-op; it never checks out the head, reads the description and changed-file list through the API only, uses a read-only token and passes no secrets. EphemeralCI_TRUSTED_RUNNERselector like the otherpull_request_targetguards. Intomainonly;feat-cmux-nextkeeps its own warn-onlypull_requestcopy.
A follow-up PR into feat-cmux-next adds the same files there, so the warning also runs on PRs into that branch.
Testing
python3 tests/test_classic_api_guard.py: 15 tests, OK. They cover fail without a reason, pass with one, a placeholder reason, warn-only onfeat-cmux-next, unguarded and excluded paths, the relays, glob semantics, CLI exit codes, the config's shape, and the workflow'spull_request_targettrigger, base checkout and absence of any head checkout or secret. Registered intests/test-execution.toml(linux-guard) and run fromci-guards.yml.validate_test_execution_registry.py --base-sha origin/main,test_ci_warm_sources.py,test_ci_guard_workflow_structure.py,test_ci_runs_on_values.py,test_ci_self_hosted_guard.sh,test_ci_no_github_hosted_macos.py: all pass locally.
Observed / Not run
Observed: the tests above. Not run: a live PR event. The first PR the check sees after this lands shows it running.
Changelog
none
Agent session: agent session
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Adds a CI check that blocks PRs into main when they change classic cmux API paths unless the description includes Classic API change: <reason>. This makes the auto-deploy risk explicit before those changes reach production.
- The guard and deploy-path list come from the base commit; it reads PR details and changed paths through the API without checking out PR code.
- Incomplete changed-file listings also require a reason, and the guard’s tests run in CI.
Summary by CodeRabbit
- New Features
- Pull requests targeting
mainorfeat-cmux-nextreceive checks when opened, updated, reopened, edited, or marked ready for review. - Changes to guarded deployment paths require a specific explanation in the pull request description. Missing explanations block changes targeting
main; onfeat-cmux-next, they produce a warning. - Checks account for excluded paths and incomplete changed-file listings, helping identify potentially missed guarded changes. A valid explanation can allow a check to pass when the listing is incomplete.
- Pull requests targeting