Repository navigation
ci: every nightly-only step gets a pull-request dry run, or a reviewed reason - #24261
Conversation
…red) tests/test_nightly_step_coverage.py reads .github/nightly-step-coverage.json and fails until every step of nightly.yml (and each reusable workflow it calls) has an entry: a pull-request step that runs the same check, plumbing, or a reviewed nightly-only reason. Red here: the registry does not exist yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a160a7-80b6-7101-bcbe-c94cbb747f12&host=cmux16
…none .github/nightly-step-coverage.json covers every step of nightly.yml and the reusable nightly-tag-permission-probe.yml: 59 name the pull-request step that runs the same check, 84 are plumbing, 78 are nightly-only with the reason. The 107 checkout, artifact, cache and setup action steps are plumbing by derivation (PLUMBING_ACTIONS in the test) and need no entry. New pull-request dry runs for steps only the nightly ran: - publish-nightly 'Require every DMG to match a tested digest': tests/test_nightly_tested_dmg_gate.py runs the step's own run block from nightly.yml on fixture DMGs. - nightly-next-macos15-launch 'Check every bundled Mach-O loads on macOS 14': tests/test_macho_minos.py runs the same macho_minos.py command on a bundle of synthetic Mach-O files. - publish-nightly-identity-legacy 'Refuse a DMG that is not the tested one': its fixture test now runs directly in the release-notary guards (it only ran after test_nightly_universal_build.sh's earlier checks, which are red on base). - 'Inject the Ghostty dependency licenses' and 'libghostty-vt Zig packages are covered by the notices': their scripts' fixture tests run in cmux-next-guards.yml vt-link-graph on every feat-cmux-next pull request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a160a7-80b6-7101-bcbe-c94cbb747f12&host=cmux16
|
Owned by ci-speed on cmux16 · session |
|
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); no finished base run on 35c4e3d to compare; do not merge until green; see lane-rules |
|
Bead |
|
Found 11 test failures on Blacksmith runners: Failures
...and 1 more test failures. View all on Blacksmith
|
|
|
Review of #24261 (Codex): the validator only ran on the live registry. These fixture tests reject what it let through: a pr entry that names nothing, a pull_request trigger that never admits feat-cmux-next, caller, job and step conditions or a matrix that skip the counterpart, an external reusable workflow in the nightly, and local actions exempted as plumbing. The validator is now one function over parsed workflows, same behaviour; eight fixture tests fail here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a160a7-80b6-7101-bcbe-c94cbb747f12&host=cmux16
Fixes the five findings of the Codex review of #24261 at dcd9b78: 1. A pr entry must be a nonempty <workflow>:<job>:<step>; {"pr": null} no longer passes unchecked. 2. A pull_request trigger counts only when its branches and branches-ignore admit feat-cmux-next and its paths do not exclude everything. 3. A counterpart must be reachable for a feat-cmux-next pull request: caller job, job, needed-job and step if: conditions are judged on event name, base ref, needs.<job>.result, matrix values (after include and exclude, or a caller's fromJSON input) and literal caller inputs; anything else counts as possibly true. cmux-next-guards.yml's guards-local.sh --feat-routes route into ci-guards.yml is modelled from detect_linux_guard_changes. The two ci-macos.yml counterparts (main pull requests only) are now nightly_only with that reason. 4. An external reusable workflow in the nightly is refused. 5. Local actions (cache-restore, cache-save) are no longer plumbing by derivation: their 13 uses have reviewed plumbing entries. To stay under 800 lines, the tested-DMG and macho_minos dry runs move to a follow-up PR; their two steps are nightly_only until it lands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a160a7-80b6-7101-bcbe-c94cbb747f12&host=cmux16
Codex re-review of #24261 at 1787d9e. Each fails here: - ordered negative trigger patterns (branches ["**", "!<base>"], paths ["**", "!**"]) - the guards-script route ignores its job and step conditions - matrix rows ignore multi-key excludes and include-only values - step conditions do not see known skipped dependencies - && must bind tighter than || Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a160a7-80b6-7101-bcbe-c94cbb747f12&host=cmux16
Fixes the Codex re-review of #24261 at 1787d9e: 1. Trigger filters are evaluated in order like GitHub: a later `!` pattern negates, so branches ["**", "!feat-cmux-next"] and paths ["**", "!**"] are refused. Paths need a witness path the list admits. 2. The guards-script route into ci-guards.yml exists only when the ci-guard-group job runs (its if and needs) and the step that runs guards-local.sh --feat-routes can run. 3. The matrix expands to its real rows: cross product, multi-key exclude, include extending original rows or adding its own (include-only works). A step counts when some row can run it. 4. Step conditions see the known skipped jobs (needs.<job>.result). 5. Conditions are read with GitHub's precedence (! over && over ||) in three-valued logic; anything unreadable is unknown, so possibly true. The fixture cases are now one table; the registry packs consecutive plumbing steps on a line to stay under 800 changed lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a160a7-80b6-7101-bcbe-c94cbb747f12&host=cmux16
Codex round 4 on #24261 at e02ba6a. Each fails here: - paths ["src/**", "!src/x/**"] admits src/main.py but is refused - the guards-script route ignores the caller job's matrix rows - github.event_name != 'PULL_REQUEST' passes; GitHub compares strings case-insensitively Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a160a7-80b6-7101-bcbe-c94cbb747f12&host=cmux16
…rings Fixes Codex round 4 on #24261 at e02ba6a: 1. Path filters are refused only when provably empty: after the last match-all closing pattern (!** in paths, ** in paths-ignore) nothing reopens a path. Sampling no longer refuses paths ["src/**", "!src/x/**"]. 2. The guards-script route judges its invoking step in each of the ci-guard-group job's matrix rows, as counterparts are. 3. == and != on known strings ignore case, as GitHub's do; so github.event_name != 'PULL_REQUEST' is false for a pull request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a160a7-80b6-7101-bcbe-c94cbb747f12&host=cmux16
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a160a7-80b6-7101-bcbe-c94cbb747f12&host=cmux16
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a160a7-80b6-7101-bcbe-c94cbb747f12&host=cmux16
…(red) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a160a7-80b6-7101-bcbe-c94cbb747f12&host=cmux16
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a160a7-80b6-7101-bcbe-c94cbb747f12&host=cmux16
|
Review (independent Codex xhigh via acpmux, correctness first): SHIP at da33041 Seven rounds; each finding (fail-open validator cases: null pr, trigger filters, conditions, matrices, reusable workflows, local actions, precedence, case, empty matrices) has a fixture committed red before its fix. |
|
base-red-watch: |
|
The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/39766181620 at 81640d1 failed: cmux-next web bundles (source-keyed cache). |
|
The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/39767073305 at aa8d973 failed: cmux-next web bundles (source-keyed cache). |
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Summary
Leo's ask, after a direct push broke every nightly-next at a step only the nightly ran: "Every step that only nightly runs gets a PR-time dry run."
.github/nightly-step-coverage.jsonnow lists every step ofnightly.yml, plus the reusable workflows it calls (todaynightly-tag-permission-probe.yml), keyed by job id and step name. Each entry is one of:{"pr": "<workflow>:<job>:<step>"}: a step in a workflow that runs on pull requests runs the same script, on fixtures or as a dry run.{"plumbing": true}: setup and transfer steps with no check of their own.{"nightly_only": "<reason>"}: steps that need signing or publishing secrets, the Release app, or a Mac. The reason is written out.Steps that use an external checkout, artifact, cache or setup action are counted as plumbing automatically (
PLUMBING_ACTIONS) and have no entry. Local actions (./.github/actions/cache-restore,cache-save) always need a reviewed entry, because their steps can change in this repo. The nightly may call only local reusable workflows; an externaluses:is refused.tests/test_nightly_step_coverage.pyenforces this in the release-notary guard group, which runs on every feat-cmux-next pull request throughcmux-next-guards.yml. The test fails when:nightly_onlyreason too short to review;prvalue isn't a nonempty<workflow>:<job>:<step>, or the step doesn't exist;pull_requestfilters must admit the branch.branches/branches-ignorepatterns are evaluated in order like GitHub (a later!pattern negates). Path filters are refused only when they provably admit no path: after the last match-all closing pattern (!**inpaths,**inpaths-ignore) nothing reopens a path. Otherwise a caller that runs must call it.if:may be false for that PR. Conditions are read with GitHub's precedence (!, then&&, then||) in three-valued logic, and string comparisons ignore case, as GitHub's do. Only comparisons ofgithub.event_name, the base ref,needs.<job>.resultfor known-skipped jobs, a matrix value or a literal caller input decide; anything else, or anything unreadable, counts as possibly true.exclude,includeextending the original rows or adding its own, a caller'sfromJSONinput), and the step must be able to run in some row.cmux-next-guards.ymlci-guard-group runsci-guards.yml's groups throughguards-local.sh --feat-routes, notuses:. That route counts only when the job (itsifandneeds) can run and the invoking step can run in one of the job's matrix rows. Its groups come fromdetect_linux_guard_changes.py.Fixture tests (
CoverageFixtureTests, mostly one table,COUNTERPART_CASES) show each rejection, and a few pass cases: a null or malformedpr; triggers limited tomain, negated by a later!pattern, or ignoring every path (and a pass case forpaths: ["src/**", "!src/x/**"]); caller, job, needed-job and step conditions that exclude the PR; a step that needs a skipped job's success; precedence; a case-insensitivegithub.event_name != 'PULL_REQUEST'; matrix rows removed by single- or multi-key excludes or absent from an include-only matrix; the guards route with its job or step switched off, or its step limited to a matrix value the job never has; an external reusable workflow; and a local action without an entry. To check the tests, I reverted each fix in a copy of the validator; each of those reversions fails at least one case.The counterparts are derived by following
uses: ./.github/workflows/...fromnightly.yml, so a newly called reusable workflow is pulled in too. The test caught mistakes in the hand-made list it started from. "Build fast arm64 cmux-tui client" was mapped tocmux-tui-build-package.yml, which no pull request runs. "Build Ghostty CLI helper" and "Verify signed diff sidecar runtime contract" pointed atci-macos.yml, which runs only for PRs into main. All three arenightly_onlywith that reason.Totals: 234 entries (55
pr, 97plumbing, 82nightly_only), plus the derived external-action plumbing steps. Consecutive plumbing steps of a job share a registry line, to keep the PR under 800 lines.The 20 gaps (steps with no PR-time counterpart before this PR)
tests/test_nightly_tested_dmg_gate.pyruns this step's ownrun:block from nightly.yml on fixture DMGs.tests/test_macho_minos.pyruns the samemacho_minos.py scan --floor 14.0on synthetic Mach-O fixtures.tests/test_nightly_next_legacy_tested_digest.shalready existed, but onlytest_nightly_universal_build.shran it, after earlier checks that are red on base (below). It now runs directly in the new release-notary step "Dry-run the nightly's tested-DMG gate for the old feed".verify-ghostty-license-bundle.pyand its collector (test_ghostty_licenses.py,test_check_bundle_notices.py) used to run only in the workflow_dispatchcmux-next-notices.yml. They now run incmux-next-guards.ymlvt-link-graph (Linux, every feat-cmux-next PR).--license-manifestmode's fixture test (test_check_ghostty_vt_notices.py) now runs in the same vt-link-graph step. PR CI used to run only--check-link-graph.release-compileandcmux-scheme-compilecover parts of it.The two "Visual smoke" steps added since the list was made point at
nightly-next-canary.ymlcanary / Visual smoke, which runs the samegate.sh.Follow-ups (not in this PR, to stay under the size budget)
pull_requesttrigger tocmux-next-source-archive.yml(ghostty gitlinks, pinned licenses), so a new Zig package without a pinned license fails on the PR. That's a macOS job, so it isn't done here.lipoand plistlib fixture tests on Linux.verify-bundle-load-commands.sh,verify-app-bundle-licenses.sh,macho_minos.py) on therelease-compileapp. They need no signature.Base red found on the way
tests/test_nightly_universal_build.shfails on base at line 1265: it expects the step "Skip a build older than the old feed's", which #24019 renamed to "Skip a build the old feed already has". The script exits at the first failure, so none of the checks after line 1265 run in "Validate universal nightly workflow". Severalprentries name that step. A separate PR (ci-speed/release-notary-red) fixes it; this PR doesn't touch it.Testing
.github/nightly-step-coverage.json is missing. At eab59d9 eight fixture tests fail on the first-review rules; 1787d9e passes them. At a840f3f five fixture tests for the second review fail; e02ba6a passes them. At 8ad6e8a three fixture cases for the third review fail; at 2fad46c all cases pass, the live registry included.nightly.yml job 'publish-nightly' step 'A new nightly-only step'.bash tests/test_nightly_next_legacy_tested_digest.shandpython3 scripts/cmux-next/notices/test_check_ghostty_vt_notices.py(21 tests) pass.test_check_bundle_notices.pyandtest_ghostty_licenses.pypass with a local shim forPath.stat(follow_symlinks=). The local python3 is 3.9, and the verifier needs 3.10+. The Ubuntu runners have 3.12.scripts/ci/validate_test_execution_registry.pyreports the registry is valid.tests/test_cmux_next_guards_route.py,test_ci_linux_guard_routing.py,test_feat_ci_guard_group.pyandtest_ci_run_guards.pypass.python3 scripts/cmux-next/prepush-gate.pyreports no new violations.Observed / Not run
Size: 799 added lines. 257 of them are the registry; the validator and its fixture tests are about 515.
Changelog
none
Proof
CI only, no UI.
Agent session: cmux16
🤖 Generated with Claude Code