#171
fix(ci-speedup): mutually exclusive jobs are not duplicated setup
TL;DR
The speed audit was flagging two jobs as "duplicated setup" even when their run conditions guarantee only one of them ever runs (a same-repo job and a fork-only job), and it credited roughly 600 runner-minutes a month for a fix that could never save anything. A reviewer rejected a whole report over it. The audit now recognizes conditions that provably cannot both be true and stops treating those jobs as a duplicated pair; anything it cannot prove is still treated as co-running, so real duplicates keep firing.
before: test-self (push / same-repo PR) + test-fork (fork PR only) -> "2 duplicate setups", minutes credited
after: provably one-or-the-other -> not a pair, no finding, no minutesWhat changed
skills/ci-speedup/scripts/scan.py: a conservative job-exclusivity check (_jobs_mutually_exclusive). It parses a job-levelif:only when it is made of==/!=comparisons between context paths and string literals joined by&&/||/ parentheses, converts it to disjunctive normal form, and calls two jobs exclusive only when every pair of disjuncts contains a contradiction (x == yvsx != y, orx == 'a'vsx == 'b', compared case-insensitively as Actions does). It gives up (treats the jobs as co-running) on:- function calls,
!, bare operands, or a missingif:; - contexts that can differ per job: only
github,inputsandvarscount, andgithub.job/github.action*do not, becauseneeds.*resolves per job (a job without a direct need sees null); - two literals that both read as numbers (
'1'/'1.0',''/'0'), because Actions compares a numeric or null value as a number; - pathologically nested conditions (no RecursionError can reach the scan).
- function calls,
- OPT14 (repeated checkout/setup without artifact handoff) and OPT12 (duplicated setup across jobs) keep only jobs that can co-run with at least one other candidate. For an exclusive pair alone, the finding no longer fires, so OPT14's flat per-run credit (about 600 runner-minutes a month here) is gone. Other job-pairing detectors in
scan.pywere checked: the remaining workflow-level findings (OPT45, OPT32) and per-job ones such as OPT33 do not pair jobs as duplicates. skills/ci-speedup/CHANGELOG.md: Fixed entry.
Known limits (follow-ups, not regressions)
- When an exclusive pair sits beside a third co-running job, the finding still lists all three jobs, though at most two run per run.
- OPT73's measured cluster-floor credit in
collect_runs.pygroups jobs by per-job timing across runs and does not yet apply this exclusivity rule.
Verification
- Regression tests failed on the unfixed scanner:
test_exclusive_job_pair_is_not_duplicated_setup:assert 'OPT14' not in {'OPT12', 'OPT14', 'OPT32', 'OPT33', 'OPT45', 'OPT5'}test_negated_exclusive_pair_is_not_duplicated_setup(the!= 'pull_request' || ...form): same failuretest_job_exclusivity_is_conservative: helper missingtest_numeric_looking_literals_are_not_proof_of_exclusivityandtest_job_specific_contexts_are_not_proof_of_exclusivity:assert not Trueon the first version of the checktest_deeply_nested_condition_does_not_crash_the_scan:RecursionError
- Positive controls pass before and after: a same-condition pair, an unconditioned pair, and an exclusive pair plus a third co-running job all still fire (OPT14 and OPT12).
skills/ci-speedup/tests/test_scan_detectors.py: 301 passed.- Scanning this repository: the
test-self/test-fork(ci.yml) andscan/scan-fork(ci-secure-check.yml) findings are gone;evals/ci-speedup-evals(skill-evals.yml, same condition) still fires OPT14.
🤖 Generated with Claude Code
Activity
6greptile-apps[bot]reviewed1d ago
[Low impact] The change affects duplicate-setup findings in the CI speed scanner.
Fix the numeric and job-specific value comparisons before merging; both can hide real duplicated setup.
Findings
Reviews (1) · Last reviewed commit: "fix(ci-speedup): mutually exclusive jobs..." · Reviewed by Greptile