Repository navigation
cmux-next fast gates: web lint, crash ratchet, defaults stamp and prepush gate fail on the PR merged with base - #23264
Conversation
…and post cmux-next/fast-gates Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue
…push gate fail on the PR merged with base A pull_request and workflow_dispatch (pr=N) workflow runs the four push-time gates in fail mode on BASE..merge and posts cmux-next/fast-gates with "base <sha>" on the PR head, so mergers can judge freshness against the tip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue
|
Owned by land-and-ci on huge-blue · session |
… GitHub's reason Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue
|
Found 5 test failures on Blacksmith runners: Failures
|
|
Bead |
|
Review (subagent, correctness first): BLOCK at dc7b214 Defect: the dispatch path lets code from a fork PR decide what gets posted, with a
Failure scenario: a fork PR changes
It also runs the fork's code on Suggested fix:
Everything else holds:
Nits:
Agent session: huge-blue |
|
codex-review: SHIP at dc7b214
|
…rom a job with no PR code Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue
… gate with read only, post from its own job A dispatch refuses a fork head, a closed pull request or another base before anything is fetched; the gates job runs the PR's code with contents: read and hands post only its result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue
|
codex-review: SHIP at 8ccd8ee
|
|
Review (subagent, correctness first): SHIP at 8ccd8ee The earlier BLOCK (dc7b214) is resolved. PR code can no longer choose what the status job publishes:
Nits, none blocking:
Agent session: huge-blue |
|
|
…shallow cmux-next CI guard group on 8ccd8ee (job 119156226796): tests/test_ci_no_shallow_dev_fetch.py flagged cmux_next_fast_gates.py:90 and :94. In a full clone the fetch now takes the refs whole. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
red: cmux-next checks (god files, concurrency, crash safety, l10n); base red: the same failure on the base 31c7030; does not block |
…a shallow checkout Base red from #23249: tests/test_ci_no_shallow_dev_fetch.py flags base-autofix-settings-exports.sh:60 on every PR's cmux-next CI guard group (seen on #23264, job 119156226796). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue
…ng (#23315) * test: base autofix makes a held repair again on the moved tip, keeps its Mac autofix, and judges settings by their stamp Red against feat-cmux-next dfb5330: base-autofix-land.sh and base-autofix-repair.sh do not exist, the autofix job's concurrency group lets a newer push replace the pending autofix that carries the Mac exports, the push holds settings exports past a settings-area move that is no stamp input, and defaults_guard.py has no source-stamp. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue * base autofix: make a held repair again on the moved tip, keep the Mac exports' autofix, judge settings by their stamp - base-autofix-land.sh pushes the repair (base-autofix-push.sh) and, when the push holds a file this job makes itself because the tip moved its inputs (the CI target graph, the SDK layers, the ratchet, the cloud clients, cargo fmt), checks out the tip, makes the repair again there and pushes that, at most three rounds. Held Mac exports still wait for the next run, which the failed run keeps in its "since". The repair never sees the push token. - base-autofix-repair.sh is the Repair step's logic, shared by the first round and the rounds on a moved tip (which apply no Mac export or webviews patch made at the older commit). - The autofix job of a run that carries Mac exports has a concurrency group of its own, so a newer push can no longer replace it while it waits behind another run's autofix. Groups are per branch. - The settings exports' inputs are what their stamp hashes (defaults_guard.py source-stamp): a settings-area move that leaves the tip's stamp the exports' own, with the same regeneration, neither holds them at the push nor makes the Mac job regenerate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue * test: a push that never got through is no hold, and a held inspector page waits Red on aa0c3fd (review): the push script's held list survives a failed attempt, so base-autofix-land.sh remade only the graph and could pass without the settings exports; and remade() counted the Chief inspector page, which only the webviews patch makes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue * base autofix: only a hold hands on a held list, and the inspector page is never remade on the tip Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue * base autofix: the settings regeneration fetches with --depth only in a shallow checkout Base red from #23249: tests/test_ci_no_shallow_dev_fetch.py flags base-autofix-settings-exports.sh:60 on every PR's cmux-next CI guard group (seen on #23264, job 119156226796). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue * base autofix: settings fetch expands an empty depth list safely under bash 3.2 Review of #23315: in a full clone the depth array is empty, and "${depth[@]}" under set -u stops on macOS /bin/bash 3.2. Same guarded expansion as the repo's other depth fetches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
Review (subagent, correctness first): SHIP at e991606 This reviews the delta from 8ccd8ee (SHIP), which is only scripts/ci/cmux_next_fast_gates.py, +6/-1.
Nit: at :100, Agent session: huge-blue |
|
Shipped in:
|
Why
Four base reds landed on feat-cmux-next on 2026-10-21. Each came through a check that does not fail a pull request:
ci-web.ymlreact-apps-check,CMUX_NEXT_STYLE_GUARDS=warn). They fail only on push (cmux-next-web.yml"web lint (fast)",scripts/web_lint_prepush.py).defaults-snapshot.feat-cmux-next has no required checks. Its ruleset blocks only deletion and force-push.
What
.github/workflows/cmux-next-fast-gates.yml("cmux-next fast gates", Linux) has three jobs, so pull request code never runs next to a write token:resolvenames the head and BASE without running any checked-out code:git rev-parseon the merge ref, or on dispatch the GitHub API. A dispatch refuses a head from a fork, a closed pull request or a base other than feat-cmux-next before anything is fetched.fast gates(needs: resolve,contents: readonly) runs the PR's code: four push-time gates in fail mode on BASE..merge, listed below.post(statuses: writeonly) checks nothing out. It writes the status from resolve's head and base and the gates job's result, never from anything the gates job wrote.The four gates:
scripts/web_lint_prepush.py --base BASE --head MERGE: the same diff-scoped lint thatweb lint (fast)runs on push. Bun, Node andnode_modulesare installed only when the diff touches a web package the script would lint. Otherwise the gate is skipped.scripts/ci/crash_ratchet_compare.pythe waycmux-next-guards.ymldoes: on the merge and on BASE's exported tree.scripts/ci/defaults_guard.py check-stamp --base BASE, the same call as incmux-next-guards.yml.scripts/cmux-next/prepush-gate.py --base BASE --head MERGE, which fails only on violations the PR adds. Its pinned tools are cached bylint-tools.lock.The step logic is in
scripts/ci/cmux_next_fast_gates.py:prepare --head SHA --base SHAfinds the merge onpull_request, or makes it on dispatch, fails unless that merge is exactly BASE + HEAD as resolved (a head that moved after resolve is refused), and writesmergeandweb.runruns every gate, even after one fails, writes a summary table and exits 1 if any gate failed.Both scripts already accepted
--base/--head, so neither needed new flags. The new test is also wired into thetooling-testsjob ofcmux-next-guards.yml.The push-attribution part of the original plan was dropped: it would have posted comments on teammates' PRs. Attribution moves to base-red-watch.
Contract
Other PRs depend on this contract: next-autoland in leo-workspace and the hq merge hook. Each will enforce it in its own PR.
pull_requestinto feat-cmux-next with no paths filter, which uses the default merge ref.workflow_dispatchwith inputpr, validated as digits only. A dispatch fetchesrefs/pull/<pr>/headand the current feat-cmux-next tip and runsgit merge --no-edititself (--no-ff). A conflict fails with a message naming the PR and the conflicting files.pull_request_targetand noworkflow_runtrigger.concurrency: fast-gates-${{ github.event.pull_request.number || inputs.pr }}withcancel-in-progress: true.cmux-next/fast-gatessuccessorfailurebase <40-hex BASE>target_urlset to the run URLcontents: readfor the workflow;resolveaddspull-requests: read;fast gateshascontents: readonly;posthasstatuses: writeonly.resolve.env.cmux-next/fast-gatesstatus on the head issuccess, and its base B is an ancestor of the current tip T.GET compare/B...Thasahead_by <= 250and none of its files is among the PR's files (GET pulls/N/files).cmux-next-fast-gates.ymlwithpr=Non ref feat-cmux-next and judges again after that run.Tests
tests/test_cmux_next_fast_gates.pyhas two parts:cmux-next-guards.yml's)resolveruns no repository script and checks out only onpull_request; a dispatch refuses a fork head, a closed PR or another base there;fast gatesneeds it and takes its head and baseposthas one step, no checkout, and takes head and base fromresolveand only the result fromfast gates, which has no outputspull_request_targetorworkflow_runtrigger${{ }}insiderun, in any jobpostcan write)continue-on-error, and nothing usesCMUX_NEXT_STYLE_GUARDStooling-testspull_requestmerge ref is read, and a mismatched head or base is refusedweb=trueRed (test commit 7ce13bd, before the workflow and script existed):
Green (3fe7900, and still green at dc7b214):
Review fix (chief-fix-3 BLOCK at dc7b214: dispatch ran fork-PR code in the job that posts). Red on the test commit c018308 with the old workflow and script, green on 8ccd8ee:
Changelog
none
Observed / Not run
Observed:
actionlintoncmux-next-fast-gates.ymlandcmux-next-guards.ymlis clean (again at 8ccd8ee).At 8ccd8ee these still pass:
tests/test_ci_workflow_guards_are_wired.py,test_ci_workflow_permissions_are_job_scoped.py,test_ci_actionlint_covers_every_workflow.py,test_ci_workflow_path_filter_parity.pyandtest_ci_repo_variable_defaults.py.In
scripts/ci/guards-local.sh --group ci, the only reds are unrelated to this change:verify_localenvironment testpython3 scripts/cmux-next/prepush-gate.py: no new violations.A real
cmux_next_fast_gates.py run --base <feat-cmux-next tip> --web falseon this branch on huge-blue passed: crash ratchet 63.2s, defaults stamp 0.3s, prepush gate 3.3s, web lint skipped.On this PR, the workflow's first run (39578318736, head 3fe7900) passed all four gates, but the status POST through
curl -fgot a 403 with no reason shown. The token hadStatuses: write. dc7b214 posts withgh apiinstead, so a refusal prints GitHub's message.Run 39578793945 on head dc7b214 passed and posted
cmux-next/fast-gates= success, descriptionbase efaab9b377161282c7a53d797d073ce259b00dfb, with the target URL set to the run.The first 403 is now explained. Run 39580378636 on head 8ccd8ee:
resolveandfast gatespassed (base 66a1a07), andpostgotgh: API rate limit exceeded for installation (HTTP 403)from the Actions token, the same installation 403 that is red on base (high-signal review linkand others). It is infra, not this change, and it is not rerun.Not run:
Agent session: huge-blue
🤖 Generated with Claude Code