ci: refuse to re-dispatch a focused test that already failed at that commit - #13680
Merged
Merged
Conversation
These tests fail on the current launcher. They are committed before the guard so CI records that they detect its absence. A focused run compiles the tree before it runs anything, so a red result belongs to the commit. Re-dispatching the same selector at the same commit spends another 10-20 macOS runner-minutes to reprint the same failure. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…commit test-e2e.yml took 959 dispatches in 7 days, 531 of them red, for 6,730 runner minutes. The expensive failures are not flaky tests: sampled logs show Swift compile errors on the dispatched ref, so a 10-20 minute macOS run existed only to discover the branch does not build. One selector, cmuxUITests/SplitPaneBackgroundUITests, was dispatched 24 times in 12 hours for 397 runner-minutes and never once passed. A focused run compiles the whole tree before running anything, so a red result is a property of the commit. The launcher now looks for a completed run of the same selector at the same commit, and refuses to dispatch when one failed and none succeeded, naming the earlier run so it can be read instead of repeated. --force keeps the escape hatch. The lookup is an economy measure, never a gate: if the history cannot be read it dispatches as before. The instruction that produced most of this could not work. The testing skill said to run `gh workflow run test-e2e.yml`, but test_filter is a required input, so that command is rejected by the API and the reader improvises -- which is where filters that match zero tests come from. It now points at scripts/run-e2e.sh with --ref, says to compile the test target locally first, and says not to repeat a selector at a commit that already failed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Warning Review limit reachedNext included review available in 16 minutes. View limit detailsLimit details: You’ve used all 10 included reviews currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Repository: manaflow-ai/cmux/.coderabbit.yaml Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (3)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Contributor
|
All contributors have signed the CLA ✍️ ✅ |
teamleaderleo
added a commit
that referenced
this pull request
Sep 22, 2026
#13680 landed after this branch started. Rebasing kept its refusal but left it reading a list where it expects one selector, and a batched run would have slipped past it in two ways. Refuse per entry, so one already-red selector stops the whole dispatch: the batch shares a single compile, so it would only reprint a failure we have. Match selector membership in the run title instead of a prefix. A batched run names several selectors before " on ", so prefix matching would have made every batch invisible to the guard, including for its own entries on a later dispatch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
teamleaderleo
added a commit
that referenced
this pull request
Sep 22, 2026
* ci: run a batch of e2e filters against one compile test-e2e.yml compiles the app from scratch on every dispatch: it runs `xcodebuild ... test`, and -only-testing: narrows execution, not compilation. Chasing one flake therefore costs a full cold Debug build per filter, and dispatchers routinely fire several filters at the same commit seconds apart: 21:37:54 cmuxUITests/SplitPaneBackgroundUITests @ 9d2a8c0 21:37:51 cmuxTests/SplitPaneGeometryProjectionRenderParity @ 9d2a8c0 21:37:49 cmuxTests/SplitPaneGeometryProjectionTests @ 9d2a8c0 21:37:46 cmuxTests/TerminalWindowPortalProvisionalGeometry @ 9d2a8c0 21:37:44 cmuxTests/WorkspaceSplitProvisionalGeometryTests @ 9d2a8c0 Five dispatches, ten seconds apart, same SHA, five independent ~16 minute compiles of identical source. Measured over the last 90 parsed dispatches (46 distinct refs), 65 runs sit inside a same-ref burst under two minutes wide, and 43 of those are redundant compiles. test_filter now accepts a comma-separated list. Each entry becomes its own -only-testing: flag, which xcodebuild unions, so one compile serves the whole batch. scripts/ci/dispatch-focused-test.py takes several positional filters and joins them into one dispatch. Entries must share a target, because one invocation runs one scheme; mixing cmuxTests and cmuxUITests is rejected rather than silently running half the request. Duplicates and empty entries are rejected too. A single filter behaves exactly as before, including bare class names targeting UI tests and the DisplayResolutionRegressionUITests harness, which stays scoped to a one-selector dispatch. require_selected_test_execution.sh proves that some tests ran, never which ones, so a batch could otherwise let a healthy count from one selector cover a sibling that never started. Each requested suite is now required by name in the xcodebuild log. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: keep the re-dispatch guard working for batches #13680 landed after this branch started. Rebasing kept its refusal but left it reading a list where it expects one selector, and a batched run would have slipped past it in two ways. Refuse per entry, so one already-red selector stops the whole dispatch: the batch shares a single compile, so it would only reprint a failure we have. Match selector membership in the run title instead of a prefix. A batched run names several selectors before " on ", so prefix matching would have made every batch invisible to the guard, including for its own entries on a later dispatch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: do not fail a batch on swift-testing suite naming The per-suite accounting I added greps "Test Suite 'Name'", which only XCTest prints. cmuxTests also runs swift-testing, which prints Suite "Display Name" -- double quotes, and a display name that can differ from the identifier passed to -only-testing:. Batching a swift-testing suite would therefore have failed a run that executed correctly. Accept either form, report an unseen suite as a warning, and fail only when nothing requested was observed at all. That still catches the case this check exists for -- a batch that silently ran none of what was asked for -- without inventing failures from output-format differences. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: reject a leading or trailing comma in test_filter The split loop never visits a trailing empty field, so "cmuxTests/AlphaTests," passed as a single selector instead of failing the empty-entry check. Reject both edges before splitting. Reported by CodeRabbit on #13695. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: stop the suite accounting failing green swift-testing runs The check I added matched "Test Suite 'Name'" and 'Suite "Name"' and hard failed when it found neither. swift-testing prints a bare @suite unquoted -- "Suite RemoteTmuxMirrorPaneInputMappingTests started." -- so neither pattern matched it, and there was no single-selector exemption. Of the 821 suites in cmuxTests, 528 are bare and 293 carry a @suite("...") display name that is not the identifier -only-testing: takes. Only the 341 XCTestCase classes matched, so most focused cmuxTests dispatches would have gone red on a test run that passed, including the example in the dispatcher's own help text. Match the unquoted form too, and stop failing on a miss. A display name can never be matched by identifier, so absence is not evidence a suite did not run, and require_selected_test_execution.sh already owns pass/fail. The accounting now reports and nothing more. That leaves the batch gap open: a batch can pass with only one of its selectors executed, because that guard counts tests rather than naming them. Closing it needs the typed xcresult that run-app-host-xcodebuild.sh already writes, which is worth doing separately. Batching is no worse than today's single dispatch in the meantime. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: brace the suite expansion so shellcheck can parse it SC1087: "$suite[ .]" reads as an array expansion, which failed the Testbox broker trust boundary guard. Braces, same behaviour -- verified against all four log shapes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: drop the comma pattern shellcheck proved unreachable SC2221/SC2222: in ",*|*,|*, )" the "*," arm always wins over "*, ", so the spaced arm never matches. It was redundant anyway -- a trailing comma followed by whitespace still fails the empty-entry check inside the loop, which the behaviour matrix confirms. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
test-e2e.ymltook 959 dispatches in 7 days, 531 of them red, for 6,730 runner-minutes. The expensive failures are not flaky tests. Sampled logs show Swift compile errors on the dispatched ref:A focused run compiles the whole tree before it runs anything, so a 10-20 minute macOS run existed only to discover that the branch does not build.
scripts/ci/require_selected_test_execution.shthen correctly reports that no selected test executed — the guard works, it is being fed refs that do not compile.The single worst line item:
cmuxUITests/SplitPaneBackgroundUITestswas dispatched 24 times in 12 hours for 397 runner-minutes and passed zero times. Two of those failures were the same assertion, two were the same compile error.test-depot.ymlshows the same pattern (348 dispatches, 213 red, 5,335 runner-minutes) against branches that do not compile.Two changes
1. The launcher refuses a repeat that cannot say anything new.
A focused run's result is a property of the commit. Before dispatching, the launcher now looks for a completed run of the same selector at the same commit — the run name already carries both halves,
<selector> on <runner> @ <commit> [<dispatch id>], so no local state is needed — and refuses when one failed and none succeeded, naming the earlier run:--forcekeeps the escape hatch for a failure you know was infrastructure. The lookup is an economy measure and never a gate: if the run history cannot be read for any reason, it dispatches exactly as before.2. The instruction that produced most of this could not work.
skills/cmux-testing/references/local-vs-ci-validation.mdsaid:test_filterisrequired: true, so that command is rejected by the API. Anyone following it literally gets an error and improvises, which is where filters matching zero tests come from. It also omits--ref, so a dispatch that does succeed lands on whatever ref is current rather than the commit under test — whiledocs/contributor-verification.mdhas the correct form viascripts/run-e2e.sh, which the skill never mentions.The section now points at the wrapper with
--ref, says to compile the test target locally first (the same file already gives that exactcmux-unitcommand eight lines earlier), and says not to repeat a selector at a commit that already failed.Validation
Two commits, per the repository's regression policy:
d2039efadds five cases totests/test_run_e2e.pyonly — two fail against the current launcher.ce45716adds the guard and the skill fix — all 19 pass.The new cases cover: a prior failure at the same commit refuses and does not dispatch;
--forcedispatches anyway; a prior success does not block; a failure of a different selector does not block; a failure at a different commit does not block. The fakeghharness distinguishes the pre-dispatch lookup from the post-dispatch correlation by whether the caller asked forconclusion, rather than by call ordering.The 14 pre-existing cases still pass unchanged, including with the guard's extra lookup in place.
Scope and what I did not do
This targets the repeat-dispatch class specifically. It does not stop the first dispatch against a non-compiling ref — that is what the local
cmux-unitbuild in the skill is for, and enforcing it mechanically would mean a cheap pre-gate on the workflow, which is a real cost decision and a separate change.I also have not touched
test-depot.yml, which shows the same pattern from the same source. It has no in-repo dispatcher to add a guard to, and itsworkflow_calltrigger is dead (no callers anywhere) while itsworkflow_dispatchhalf is actively used ~50 times a day — so it needs its own change, not deletion.Numbers are from the 7-day totals in
docs/ci/workflow-inventory.md; the root-cause sampling is from a 17-hour window of runs, so the per-class proportions are a sample rather than a weekly figure.🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by cubic
Stops the focused-test launcher from re-dispatching a selector at a commit where it already failed, cutting wasted macOS runner minutes that mostly reprint Swift compile errors rather than flaky failures. The launcher now looks up completed runs by selector and commit (the run name already encodes both) and refuses with a link to the earlier run unless
--forceis passed; if history can't be read, it dispatches as before. The testing skill now points toscripts/run-e2e.sh --ref <sha>, which is the only working form (test_filteris a required input), and tells readers to compile the test target locally first and never repeat a failed selector at the same commit.Written for commit ce45716. Summary will update on new commits.